style updates

This commit is contained in:
Mark Gregory
2026-06-23 13:39:02 +01:00
parent e79cadc7f8
commit 3be4ff590f
129 changed files with 482 additions and 404 deletions
@@ -11,8 +11,8 @@ class AnalyticsServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'analytics');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'analytics');
}
/**
@@ -11,8 +11,8 @@ class AuditServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'audit');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'audit');
}
/**
+1 -1
View File
@@ -8,6 +8,6 @@ if (! function_exists('audittrail_feature_enabled')) {
*/
function audittrail_feature_enabled(string $key): bool
{
return (bool) config(ModuleInfo::nameLower().".features.{$key}", false);
return (bool) config(ModuleInfo::nameLower() . ".features.{$key}", false);
}
}
@@ -41,7 +41,8 @@ class AuditTrailController extends Controller
/**
* Show the specified resource.
*
* @param int $id
* @param int $id
*
* @return Renderable
*/
public function show($id)
@@ -52,7 +53,8 @@ class AuditTrailController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @param int $id
*
* @return Renderable
*/
public function edit($id)
@@ -63,7 +65,8 @@ class AuditTrailController extends Controller
/**
* Update the specified resource in storage.
*
* @param int $id
* @param int $id
*
* @return Renderable
*/
public function update(Request $request, $id)
@@ -74,7 +77,8 @@ class AuditTrailController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @param int $id
*
* @return Renderable
*/
public function destroy($id)
@@ -47,7 +47,7 @@ class AuditTrailServiceProvider extends ServiceProvider
*/
protected function registerHelpers(): void
{
foreach (glob(__DIR__.'/../Helpers/*.php') as $filename) {
foreach (glob(__DIR__ . '/../Helpers/*.php') as $filename) {
require_once $filename;
}
}
@@ -60,10 +60,10 @@ class AuditTrailServiceProvider extends ServiceProvider
if (! $this->app->runningInConsole()) {
return;
}
$commandPath = __DIR__.'/../Console';
$commandPath = __DIR__ . '/../Console';
$commands = collect(File::allFiles($commandPath))
->map(function ($file) {
$class = 'Modules\\'.$this->moduleName.'\\Console\\'.$file->getFilenameWithoutExtension();
$class = 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
return $class;
})
@@ -81,10 +81,11 @@ class AuditTrailServiceProvider extends ServiceProvider
protected function registerConfig(): void
{
$this->publishes([
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');
$this->mergeConfigFrom(
module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower()
module_path(ModuleInfo::name(), 'Config/config.php'),
ModuleInfo::nameLower(),
);
}
@@ -93,11 +94,11 @@ class AuditTrailServiceProvider extends ServiceProvider
*/
public function registerViews(): void
{
$viewPath = resource_path('views/modules/'.ModuleInfo::nameLower());
$viewPath = resource_path('views/modules/' . ModuleInfo::nameLower());
$sourcePath = module_path(ModuleInfo::name(), 'Resources/views');
$this->publishes([
$sourcePath => $viewPath,
], ['views', ModuleInfo::nameLower().'-module-views']);
], ['views', ModuleInfo::nameLower() . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower());
}
@@ -106,7 +107,7 @@ class AuditTrailServiceProvider extends ServiceProvider
*/
public function registerTranslations(): void
{
$langPath = resource_path('lang/modules/'.ModuleInfo::nameLower());
$langPath = resource_path('lang/modules/' . ModuleInfo::nameLower());
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, ModuleInfo::nameLower());
@@ -122,8 +123,8 @@ class AuditTrailServiceProvider extends ServiceProvider
{
$paths = [];
foreach (Config::get('view.paths') as $path) {
if (is_dir($path.'/modules/'.ModuleInfo::nameLower())) {
$paths[] = $path.'/modules/'.ModuleInfo::nameLower();
if (is_dir($path . '/modules/' . ModuleInfo::nameLower())) {
$paths[] = $path . '/modules/' . ModuleInfo::nameLower();
}
}
@@ -11,8 +11,8 @@ class CarrierManagementServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'carriermanagement');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'carriermanagement');
}
/**
@@ -11,8 +11,8 @@ class ConfigurationServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'configuration');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'configuration');
}
/**
@@ -273,7 +273,7 @@ class IsoCountriesSeeder extends Seeder
'iso_status' => 'official',
'source' => 'bundled-iso-3166-1',
'source_version' => '2026-06-15',
])
]),
);
}
}
@@ -63,7 +63,7 @@ class IsoCountrySubdivisionsSeeder extends Seeder
'last_synced_at' => $now,
'source' => 'bundled-bootstrap',
'source_version' => '2026-06-15',
]
],
);
}
}
+1 -1
View File
@@ -8,6 +8,6 @@ if (! function_exists('countries_feature_enabled')) {
*/
function countries_feature_enabled(string $key): bool
{
return (bool) config(ModuleInfo::nameLower().".features.{$key}", false);
return (bool) config(ModuleInfo::nameLower() . ".features.{$key}", false);
}
}
@@ -39,7 +39,7 @@ class CountriesServiceProvider extends ServiceProvider
*/
protected function registerMigrations(): void
{
$path = dirname(__DIR__).'/Database/Migrations';
$path = dirname(__DIR__) . '/Database/Migrations';
if (is_dir($path)) {
$this->loadMigrationsFrom($path);
@@ -51,7 +51,7 @@ class CountriesServiceProvider extends ServiceProvider
*/
protected function registerHelpers(): void
{
foreach (glob(dirname(__DIR__).'/Helpers/*.php') ?: [] as $filename) {
foreach (glob(dirname(__DIR__) . '/Helpers/*.php') ?: [] as $filename) {
require_once $filename;
}
}
@@ -61,14 +61,14 @@ class CountriesServiceProvider extends ServiceProvider
*/
protected function registerCommands(): void
{
$commandPath = dirname(__DIR__).'/Console';
$commandPath = dirname(__DIR__) . '/Console';
if (! is_dir($commandPath)) {
return;
}
$commands = collect(File::allFiles($commandPath))
->map(fn ($file): string => 'Modules\\'.$this->moduleName.'\\Console\\'.$file->getFilenameWithoutExtension())
->map(fn ($file): string => 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension())
->filter(fn (string $class): bool => class_exists($class))
->values()
->all();
@@ -83,14 +83,14 @@ class CountriesServiceProvider extends ServiceProvider
*/
protected function registerConfig(): void
{
$sourcePath = dirname(__DIR__).'/Config/config.php';
$sourcePath = dirname(__DIR__) . '/Config/config.php';
if (! is_file($sourcePath)) {
return;
}
$this->publishes([
$sourcePath => config_path(ModuleInfo::nameLower().'.php'),
$sourcePath => config_path(ModuleInfo::nameLower() . '.php'),
], 'config');
$this->mergeConfigFrom($sourcePath, ModuleInfo::nameLower());
@@ -101,8 +101,8 @@ class CountriesServiceProvider extends ServiceProvider
*/
protected function registerViews(): void
{
$viewPath = resource_path('views/modules/'.ModuleInfo::nameLower());
$sourcePath = dirname(__DIR__).'/Resources/views';
$viewPath = resource_path('views/modules/' . ModuleInfo::nameLower());
$sourcePath = dirname(__DIR__) . '/Resources/views';
if (! is_dir($sourcePath)) {
return;
@@ -110,7 +110,7 @@ class CountriesServiceProvider extends ServiceProvider
$this->publishes([
$sourcePath => $viewPath,
], ['views', ModuleInfo::nameLower().'-module-views']);
], ['views', ModuleInfo::nameLower() . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower());
}
@@ -120,8 +120,8 @@ class CountriesServiceProvider extends ServiceProvider
*/
protected function registerTranslations(): void
{
$publishedPath = resource_path('lang/modules/'.ModuleInfo::nameLower());
$sourcePath = dirname(__DIR__).'/Resources/lang';
$publishedPath = resource_path('lang/modules/' . ModuleInfo::nameLower());
$sourcePath = dirname(__DIR__) . '/Resources/lang';
if (is_dir($publishedPath)) {
$this->loadTranslationsFrom($publishedPath, ModuleInfo::nameLower());
@@ -144,8 +144,8 @@ class CountriesServiceProvider extends ServiceProvider
$paths = [];
foreach (Config::get('view.paths', []) as $path) {
if (is_dir($path.'/modules/'.ModuleInfo::nameLower())) {
$paths[] = $path.'/modules/'.ModuleInfo::nameLower();
if (is_dir($path . '/modules/' . ModuleInfo::nameLower())) {
$paths[] = $path . '/modules/' . ModuleInfo::nameLower();
}
}
@@ -21,7 +21,7 @@ class RouteServiceProvider extends ServiceProvider
*/
protected function mapWebRoutes(): void
{
$path = dirname(__DIR__).'/Routes/web.php';
$path = dirname(__DIR__) . '/Routes/web.php';
if (is_file($path)) {
Route::middleware(['web', 'auth'])->group($path);
@@ -33,7 +33,7 @@ class RouteServiceProvider extends ServiceProvider
*/
protected function mapApiRoutes(): void
{
$path = dirname(__DIR__).'/Routes/api.php';
$path = dirname(__DIR__) . '/Routes/api.php';
if (is_file($path)) {
Route::middleware(['api'])->prefix('api/v1')->group($path);
-2
View File
@@ -1,8 +1,6 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Countries\Http\Controllers\CountriesController;
use Modules\Countries\Http\Controllers\CountrySyncController;
use Modules\Countries\Http\Controllers\ReferenceDataController;
+3 -3
View File
@@ -69,7 +69,7 @@ final class Iso3166
$country = self::normaliseAlpha2($countryAlpha2);
$code = strtoupper(trim($subdivisionCode));
if (str_starts_with($code, $country.'-')) {
if (str_starts_with($code, $country . '-')) {
return $code;
}
@@ -77,7 +77,7 @@ final class Iso3166
throw new InvalidArgumentException('ISO 3166-2 subdivision codes must contain 1 to 6 letters or numbers after the country prefix.');
}
return $country.'-'.$code;
return $country . '-' . $code;
}
public static function flagEmoji(string $alpha2): string
@@ -85,6 +85,6 @@ final class Iso3166
$alpha2 = self::normaliseAlpha2($alpha2);
$base = 127397;
return mb_chr($base + ord($alpha2[0]), 'UTF-8').mb_chr($base + ord($alpha2[1]), 'UTF-8');
return mb_chr($base + ord($alpha2[0]), 'UTF-8') . mb_chr($base + ord($alpha2[1]), 'UTF-8');
}
}
@@ -11,8 +11,8 @@ class DocumentArchiveServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'documentarchive');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'documentarchive');
}
/**
@@ -31,7 +31,7 @@ class DocumentationController extends Controller
}
if ($request->filled('search')) {
$search = '%'.$request->string('search')->trim()->toString().'%';
$search = '%' . $request->string('search')->trim()->toString() . '%';
$query->where(function ($builder) use ($search): void {
$builder->where('title', 'like', $search)
->orWhere('summary', 'like', $search)
@@ -33,9 +33,9 @@ class StoreDocumentationDocumentRequest extends FormRequest
'is_featured' => ['nullable', 'boolean'],
'version' => ['required', 'string', 'max:50'],
'reference' => ['nullable', 'string', 'max:100'],
'attachment' => ['nullable', 'file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))],
'attachment' => ['nullable', 'file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
'attachments' => ['nullable', 'array'],
'attachments.*' => ['file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))],
'attachments.*' => ['file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
];
}
}
@@ -36,9 +36,9 @@ class UpdateDocumentationDocumentRequest extends FormRequest
'is_featured' => ['nullable', 'boolean'],
'version' => ['required', 'string', 'max:50'],
'reference' => ['nullable', 'string', 'max:100'],
'attachment' => ['nullable', 'file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))],
'attachment' => ['nullable', 'file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
'attachments' => ['nullable', 'array'],
'attachments.*' => ['file', 'max:'.config('documentation.max_upload_kb', 20480), 'mimes:'.implode(',', config('documentation.allowed_upload_mimes', []))],
'attachments.*' => ['file', 'max:' . config('documentation.max_upload_kb', 20480), 'mimes:' . implode(',', config('documentation.allowed_upload_mimes', []))],
'remove_attachment' => ['nullable', 'boolean'],
];
}
@@ -7,13 +7,13 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property int $id
* @property int $documentation_document_id
* @property string $disk
* @property string $path
* @property string $original_name
* @property int $id
* @property int $documentation_document_id
* @property string $disk
* @property string $path
* @property string $original_name
* @property string|null $mime_type
* @property int|null $size
* @property int|null $size
*/
class DocumentationAttachment extends Model
{
@@ -57,13 +57,13 @@ class DocumentationAttachment extends Model
}
if ($bytes < 1024) {
return $bytes.' B';
return $bytes . ' B';
}
if ($bytes < 1048576) {
return number_format($bytes / 1024, 1).' KB';
return number_format($bytes / 1024, 1) . ' KB';
}
return number_format($bytes / 1048576, 1).' MB';
return number_format($bytes / 1048576, 1) . ' MB';
}
}
@@ -9,14 +9,14 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;
/**
* @property int $id
* @property string $name
* @property string $slug
* @property int $id
* @property string $name
* @property string $slug
* @property string|null $icon
* @property string $colour
* @property string $colour
* @property string|null $description
* @property int $sort_order
* @property bool $is_active
* @property int $sort_order
* @property bool $is_active
*/
class DocumentationCategory extends Model
{
@@ -66,7 +66,8 @@ class DocumentationCategory extends Model
/**
* Active category scope.
*
* @param Builder<DocumentationCategory> $query
* @param Builder<DocumentationCategory> $query
*
* @return Builder<DocumentationCategory>
*/
public function scopeActive(Builder $query): Builder
@@ -77,7 +78,8 @@ class DocumentationCategory extends Model
/**
* Ordered category scope.
*
* @param Builder<DocumentationCategory> $query
* @param Builder<DocumentationCategory> $query
*
* @return Builder<DocumentationCategory>
*/
public function scopeOrdered(Builder $query): Builder
@@ -11,15 +11,15 @@ use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
/**
* @property int $id
* @property int|null $documentation_category_id
* @property string $title
* @property string $slug
* @property int $id
* @property int|null $documentation_category_id
* @property string $title
* @property string $slug
* @property string|null $summary
* @property string|null $body
* @property string $status
* @property bool $is_featured
* @property string $version
* @property string $status
* @property bool $is_featured
* @property string $version
* @property string|null $reference
* @property-read string $rendered_body
*/
@@ -101,7 +101,8 @@ class DocumentationDocument extends Model
/**
* Published document scope.
*
* @param Builder<DocumentationDocument> $query
* @param Builder<DocumentationDocument> $query
*
* @return Builder<DocumentationDocument>
*/
public function scopePublished(Builder $query): Builder
@@ -112,7 +113,8 @@ class DocumentationDocument extends Model
/**
* Featured document scope.
*
* @param Builder<DocumentationDocument> $query
* @param Builder<DocumentationDocument> $query
*
* @return Builder<DocumentationDocument>
*/
public function scopeFeatured(Builder $query): Builder
@@ -138,7 +140,7 @@ class DocumentationDocument extends Model
]);
}
return '<p>'.nl2br(e($body), false).'</p>';
return '<p>' . nl2br(e($body), false) . '</p>';
}
/**
@@ -19,7 +19,7 @@ final class DocumentationServiceProvider extends ServiceProvider
$this->mergeConfigFrom(
module_path('Documentation', 'Config/config.php'),
'documentation'
'documentation',
);
}
@@ -47,12 +47,12 @@ final class DocumentationServiceProvider extends ServiceProvider
$paths = [$sourcePath];
foreach (Config::get('view.paths', []) as $path) {
if (is_dir($path.'/modules/documentation')) {
array_unshift($paths, $path.'/modules/documentation');
if (is_dir($path . '/modules/documentation')) {
array_unshift($paths, $path . '/modules/documentation');
}
}
$this->loadViewsFrom($paths, 'documentation');
Blade::anonymousComponentPath($sourcePath.'/components', 'documentation');
Blade::anonymousComponentPath($sourcePath . '/components', 'documentation');
}
}
@@ -17,7 +17,8 @@ class DocumentationModuleTest extends TestCase
/**
* 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>
*/
private function csrfPayload(array $payload): array
@@ -49,7 +49,7 @@ return new class extends Migration
'sort_order' => ($index + 1) * 10,
'created_at' => now(),
'updated_at' => now(),
]
],
);
}
}
@@ -74,7 +74,7 @@ return new class extends Migration
Comment::query()->orderBy('id')->chunkById(100, static function ($comments): void {
foreach ($comments as $comment) {
$guid = $comment->guid ?: (string) Str::uuid();
$unid = $comment->unid ?: strtoupper(hash('sha256', $guid.'|'.$comment->ticket_id.'|'.$comment->id));
$unid = $comment->unid ?: strtoupper(hash('sha256', $guid . '|' . $comment->ticket_id . '|' . $comment->id));
$parent = $comment->parent_id ? Comment::query()->find($comment->parent_id) : null;
$parentUnid = $parent?->unid;
$rootUnid = $parent?->root_unid ?: $parentUnid ?: $unid;
@@ -86,7 +86,7 @@ return new class extends Migration
'unid' => $unid,
'parent_unid' => $parentUnid,
'root_unid' => $rootUnid,
'originator_id' => $comment->originator_id ?: $unid.':1',
'originator_id' => $comment->originator_id ?: $unid . ':1',
'sync_sequence' => $comment->sync_sequence ?: 1,
'sync_hash' => hash('sha256', (string) $comment->body),
'body_text' => trim(strip_tags((string) $comment->body)),
@@ -28,7 +28,7 @@ return new class extends Migration
$plain = trim(preg_replace('/\s+/', ' ', strip_tags((string) ($comment->body_text ?: $comment->body))) ?: '');
$subject = (bool) $comment->is_internal
? 'Internal note'
: ((int) ($comment->parent_id ?? 0) > 0 ? 'RE: ' : '').Str::limit($plain !== '' ? $plain : 'Response', 120, '');
: ((int) ($comment->parent_id ?? 0) > 0 ? 'RE: ' : '') . Str::limit($plain !== '' ? $plain : 'Response', 120, '');
DB::table('helpdesk_comments')
->where('id', $comment->id)
@@ -89,7 +89,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
] as $row) {
Category::query()->updateOrCreate(
['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',
'description' => 'Default support team for triage, ownership and ticket routing.',
'is_default' => true,
]
],
);
Project::query()->updateOrCreate(
@@ -132,7 +132,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
'color' => 'indigo',
'description' => 'Default project for operational helpdesk tickets.',
'is_default' => true,
]
],
);
}
@@ -151,7 +151,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
'description' => 'Default Helpdesk workflow board covering new work through closure.',
'is_default' => true,
'sort_order' => 1,
]
],
);
$statuses = Status::query()
@@ -181,7 +181,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
] as $template) {
WorkflowTemplate::query()->updateOrCreate(
['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,
'is_default' => true,
'is_active' => true,
]
],
);
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>',
'is_active' => true,
'sort_order' => 10,
]
],
);
ChecklistTemplate::query()->updateOrCreate(
@@ -236,7 +236,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
'slug' => 'bug-triage-checklist',
'items' => ['Confirm reproduction steps', 'Check recent deployment/build', 'Identify affected tenant/company', 'Record fix or workaround'],
'is_active' => true,
]
],
);
foreach ([
@@ -245,7 +245,7 @@ final class HelpdeskDatabaseSeeder extends Seeder
] as $tag) {
Tag::query()->updateOrCreate(
['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',
'description' => 'Default service record for WMS platform support tickets.',
'is_active' => true,
]
],
);
}
}
@@ -21,7 +21,7 @@ final class HelpdeskLivewireAssetsTest extends TestCase
self::assertLessThan(
strpos($layout, '@fluxScripts'),
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.',
);
}
}
@@ -75,7 +75,7 @@ final class GitWebhookController
private function hasValidGitHubSignature(Request $request, string $secret, string $rawPayload): bool
{
$signature = (string) $request->header('X-Hub-Signature-256', '');
$expected = 'sha256='.hash_hmac('sha256', $rawPayload, $secret);
$expected = 'sha256=' . hash_hmac('sha256', $rawPayload, $secret);
return hash_equals($expected, $signature);
}
@@ -7,6 +7,7 @@ namespace Modules\Helpdesk\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Modules\Helpdesk\Models\Ticket;
@@ -26,7 +27,7 @@ final class TicketAttachmentStoreController extends Controller
$request->validate([
'attachments' => ['required', 'array', 'min:1'],
'attachments.*' => ['required', 'file', 'max:'.$maxUploadKilobytes, 'mimes:'.implode(',', $allowedMimes)],
'attachments.*' => ['required', 'file', 'max:' . $maxUploadKilobytes, 'mimes:' . implode(',', $allowedMimes)],
]);
$user = Auth::user();
@@ -34,12 +35,12 @@ final class TicketAttachmentStoreController extends Controller
$uploadedFiles = $request->file('attachments', []);
$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);
return redirect()
->route('helpdesk.tickets.show', $ticket)
->with('ticket-attachments-added', count($uploadedFiles).' supporting file'.(count($uploadedFiles) === 1 ? '' : 's').' uploaded.');
->with('ticket-attachments-added', count($uploadedFiles) . ' supporting file' . (count($uploadedFiles) === 1 ? '' : 's') . ' uploaded.');
}
}
@@ -7,6 +7,7 @@ namespace Modules\Helpdesk\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str;
@@ -42,7 +43,7 @@ final class TicketCommentStoreController extends Controller
],
'canned_response_id' => ['nullable', 'integer', Rule::exists('helpdesk_canned_responses', 'id')],
'attachments' => ['nullable', 'array'],
'attachments.*' => ['file', 'max:'.$maxUploadKilobytes, 'mimes:'.implode(',', $allowedMimes)],
'attachments.*' => ['file', 'max:' . $maxUploadKilobytes, 'mimes:' . implode(',', $allowedMimes)],
]);
$user = Auth::user();
@@ -74,7 +75,7 @@ final class TicketCommentStoreController extends Controller
$uploadedFiles = $request->file('attachments', []);
$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(
ticket: $ticket,
@@ -108,7 +109,7 @@ final class TicketCommentStoreController extends Controller
return $isReply ? 'RE: Response' : 'Response';
}
return ($isReply ? 'RE: ' : '').Str::limit($plain, 120, '');
return ($isReply ? 'RE: ' : '') . Str::limit($plain, 120, '');
}
/**
@@ -145,7 +146,7 @@ final class TicketCommentStoreController extends Controller
return collect(preg_split('/\R{2,}/', $body) ?: [])
->map(static fn (string $paragraph): string => trim($paragraph))
->filter(static fn (string $paragraph): bool => $paragraph !== '')
->map(static fn (string $paragraph): string => '<p>'.nl2br(e($paragraph), false).'</p>')
->map(static fn (string $paragraph): string => '<p>' . nl2br(e($paragraph), false) . '</p>')
->implode('');
}
@@ -59,7 +59,7 @@ final class TicketControlsController extends Controller
->route('helpdesk.tickets.show', $fresh)
->with('ticket-action-updated', $agentId === null
? 'Ticket is now unassigned.'
: 'Ticket assigned to '.($fresh->agent?->name ?? 'selected agent').'.');
: 'Ticket assigned to ' . ($fresh->agent?->name ?? 'selected agent') . '.');
}
/**
@@ -107,7 +107,7 @@ final class TicketControlsController extends Controller
(bool) ($validated['closure_internal'] ?? false),
[],
null,
'Closure comment'
'Closure comment',
);
$fresh = $this->freshTicketForAction($ticket);
@@ -118,7 +118,7 @@ final class TicketControlsController extends Controller
return redirect()
->route('helpdesk.tickets.show', $fresh)
->with('ticket-action-updated', 'Status updated to '.($fresh->status?->name ?? 'selected status').'.');
->with('ticket-action-updated', 'Status updated to ' . ($fresh->status?->name ?? 'selected status') . '.');
}
/**
@@ -144,13 +144,13 @@ final class TicketControlsController extends Controller
return redirect()
->route('helpdesk.tickets.show', $fresh)
->with('ticket-action-updated', 'Priority updated to '.($fresh->priority?->name ?? 'selected priority').'.');
->with('ticket-action-updated', 'Priority updated to ' . ($fresh->priority?->name ?? 'selected priority') . '.');
}
/**
* Apply a direct guarded ticket update without relying on Livewire hydration.
*
* @param array<string, mixed> $updates
* @param array<string, mixed> $updates
*/
private function updateTicket(Ticket $ticket, array $updates): void
{
@@ -35,7 +35,7 @@ final class TicketStoreController extends Controller
'team_id' => ['nullable', 'integer', Rule::exists('helpdesk_teams', 'id')],
'due_at' => ['nullable', 'date'],
'attachments' => ['nullable', 'array'],
'attachments.*' => ['file', 'max:'.$maxUploadKilobytes, 'mimes:'.implode(',', $allowedMimes)],
'attachments.*' => ['file', 'max:' . $maxUploadKilobytes, 'mimes:' . implode(',', $allowedMimes)],
]);
$user = Auth::user();
@@ -59,6 +59,6 @@ final class TicketStoreController extends Controller
return redirect()
->route('helpdesk.tickets.index')
->with('success', 'Ticket '.$ticket->reference.' has been created.');
->with('success', 'Ticket ' . $ticket->reference . ' has been created.');
}
}
+6 -6
View File
@@ -66,10 +66,10 @@ final class KanbanBoard extends Component
$workflow->dispatch(
$freshTicket,
$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 . '.';
session()->flash('success', $message);
$this->refreshBoard();
@@ -112,7 +112,7 @@ final class KanbanBoard extends Component
$fresh = $ticket->fresh(['status', 'priority', 'category', 'project', 'team', 'board']) ?? $ticket;
$workflow->dispatch($fresh, 'ticket.updated', Auth::user());
$message = 'Ticket '.$fresh->reference.' moved from '.$oldBoardName.' to '.$board->name.'.';
$message = 'Ticket ' . $fresh->reference . ' moved from ' . $oldBoardName . ' to ' . $board->name . '.';
session()->flash('success', $message);
$this->refreshBoard();
@@ -213,7 +213,7 @@ final class KanbanBoard extends Component
/**
* Resolve the selected board from an already-loaded board collection.
*
* @param Collection<int, KanbanBoardModel> $boards
* @param Collection<int, KanbanBoardModel> $boards
*/
private function resolveSelectedBoard(Collection $boards): ?KanbanBoardModel
{
@@ -241,7 +241,7 @@ final class KanbanBoard extends Component
{
DB::table('helpdesk_kanban_board_statuses')->updateOrInsert(
['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,
'created_at' => now(),
'updated_at' => now(),
]
],
);
}
}
+13 -8
View File
@@ -6,6 +6,8 @@ namespace Modules\Helpdesk\Livewire;
use App\Models\User;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
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.
*
* @template TModel of \Illuminate\Database\Eloquent\Model
* @param class-string<TModel> $modelClass
* @param int|string|null $id
* @param array<string, mixed> $data
*
* @param class-string<TModel> $modelClass
* @param int|string|null $id
* @param array<string, mixed> $data
*
* @return TModel
*/
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);
return $model;
} catch (\Illuminate\Database\QueryException $exception) {
} catch (QueryException $exception) {
throw ValidationException::withMessages([
'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.
*
* @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
{
@@ -867,7 +871,7 @@ final class SettingsManager extends Component
$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);
}
@@ -881,7 +885,7 @@ final class SettingsManager extends Component
* nullable integer and exists rules expect a real null, so normalising here
* keeps every settings CRUD form usable from the browser.
*
* @param array<int, string> $keys
* @param array<int, string> $keys
*/
private function normalizeNullableFormValues(string $formProperty, array $keys): void
{
@@ -906,7 +910,8 @@ final class SettingsManager extends Component
/**
* 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>
*/
private function guardDefaultColumn(string $table, array $data): array
+3 -4
View File
@@ -52,7 +52,6 @@ final class TicketCreate extends Component
public bool $isSaving = false;
/**
* Validation rules are intentionally evaluated only when the user submits
* the form, not while rich-text/select widgets are synchronising.
@@ -77,8 +76,8 @@ final class TicketCreate extends Component
'attachments' => ['array'],
'attachments.*' => [
'file',
'max:'.config('helpdesk.max_upload_kb'),
'mimes:'.implode(',', config('helpdesk.allowed_mimes')),
'max:' . config('helpdesk.max_upload_kb'),
'mimes:' . implode(',', config('helpdesk.allowed_mimes')),
],
];
}
@@ -121,7 +120,7 @@ final class TicketCreate extends Component
'tag_ids' => $this->tag_ids,
], $this->attachments);
session()->flash('success', 'Ticket '.$ticket->reference.' has been created.');
session()->flash('success', 'Ticket ' . $ticket->reference . ' has been created.');
return $this->redirectRoute('helpdesk.tickets.index', navigate: true);
} finally {
+3 -3
View File
@@ -109,7 +109,7 @@ final class TicketEdit extends Component
$this->validate();
$this->validate([
'attachments' => 'array',
'attachments.*' => 'file|max:'.config('helpdesk.max_upload_kb', 10240).'|mimes:'.implode(',', config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip'])),
'attachments.*' => 'file|max:' . config('helpdesk.max_upload_kb', 10240) . '|mimes:' . implode(',', config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip'])),
'tag_ids.*' => 'integer|exists:helpdesk_tags,id',
]);
@@ -142,7 +142,7 @@ final class TicketEdit extends Component
$fresh = $this->freshTicket($this->ticket);
$workflow->dispatch($fresh, $status->is_closed ? 'ticket.closed' : 'ticket.updated', Auth::user());
session()->flash('success', 'Ticket '.$fresh->reference.' has been updated.');
session()->flash('success', 'Ticket ' . $fresh->reference . ' has been updated.');
return redirect()->route('helpdesk.tickets.show', $fresh);
}
@@ -276,7 +276,7 @@ final class TicketEdit extends Component
}
$disk = (string) config('helpdesk.disk', 'local');
$path = $upload->store('tickets/'.$this->ticket->id, $disk);
$path = $upload->store('tickets/' . $this->ticket->id, $disk);
$this->ticket->attachments()->create($this->attachmentPayload($upload, $path, $disk));
}
+3 -3
View File
@@ -133,9 +133,9 @@ final class TicketIndex extends Component
->when($this->priority !== '', fn (Builder $query): Builder => $query->where('priority_id', (int) $this->priority))
->when($search !== '', static function (Builder $query) use ($search): Builder {
return $query->where(static function (Builder $subQuery) use ($search): void {
$subQuery->where('reference', 'like', '%'.$search.'%')
->orWhere('subject', 'like', '%'.$search.'%')
->orWhere('body', 'like', '%'.$search.'%');
$subQuery->where('reference', 'like', '%' . $search . '%')
->orWhere('subject', 'like', '%' . $search . '%')
->orWhere('body', 'like', '%' . $search . '%');
});
})
->latest('last_activity_at')
+12 -14
View File
@@ -92,7 +92,7 @@ final class TicketShow extends Component
$this->validate([
'commentBody' => ['required', 'string', 'min:2'],
'attachments.*' => 'file|max:'.config('helpdesk.max_upload_kb', 10240).'|mimes:'.implode(',', config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip'])),
'attachments.*' => 'file|max:' . config('helpdesk.max_upload_kb', 10240) . '|mimes:' . implode(',', config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip'])),
]);
$tickets->comment($this->ticket, Auth::user(), $this->commentBody, $this->internal, $this->attachments);
@@ -120,7 +120,6 @@ final class TicketShow extends Component
$this->dispatch('helpdesk-reply-added');
}
/**
* Refresh ticket aggregates and the time-log modal after the timer component starts or stops.
*/
@@ -143,7 +142,7 @@ final class TicketShow extends Component
->findOrFail($this->canned_response_id);
$rendered = $renderer->render($response->body, $this->ticket, Auth::user());
$this->commentBody = trim($this->commentBody."\n\n".$rendered);
$this->commentBody = trim($this->commentBody . "\n\n" . $rendered);
$this->dispatch('helpdesk-canned-response-applied', body: $this->commentBody);
}
@@ -211,7 +210,7 @@ final class TicketShow extends Component
$this->closureInternal,
[],
null,
'Closure comment'
'Closure comment',
);
$this->reset(['closureComment', 'closureInternal']);
@@ -221,7 +220,7 @@ final class TicketShow extends Component
$workflow->dispatch($this->ticket, $status->is_closed ? 'ticket.closed' : 'ticket.updated', Auth::user());
session()->flash('ticket-action-updated', 'Status updated to '.$this->ticket->status?->name.'.');
session()->flash('ticket-action-updated', 'Status updated to ' . $this->ticket->status?->name . '.');
$this->dispatch('helpdesk-status-updated', statusId: $this->status_id);
}
@@ -254,7 +253,7 @@ final class TicketShow extends Component
$workflow->dispatch($this->ticket, 'ticket.updated', Auth::user());
session()->flash('ticket-action-updated', 'Priority updated to '.$this->ticket->priority?->name.'.');
session()->flash('ticket-action-updated', 'Priority updated to ' . $this->ticket->priority?->name . '.');
$this->dispatch('helpdesk-priority-updated', priorityId: $this->priority_id);
}
@@ -291,7 +290,7 @@ final class TicketShow extends Component
session()->flash('ticket-action-updated', $this->agent_id === null
? 'Ticket is now unassigned.'
: 'Ticket assigned to '.$this->ticket->agent?->name.'.');
: 'Ticket assigned to ' . $this->ticket->agent?->name . '.');
$this->dispatch('helpdesk-agent-updated', agentId: $this->agent_id);
}
@@ -305,7 +304,7 @@ final class TicketShow extends Component
$this->validate([
'supportingFiles' => ['required', 'array', 'min:1'],
'supportingFiles.*' => ['file', 'max:'.$maxUploadKilobytes, 'mimes:'.$allowedMimes],
'supportingFiles.*' => ['file', 'max:' . $maxUploadKilobytes, 'mimes:' . $allowedMimes],
]);
$user = Auth::user();
@@ -398,7 +397,6 @@ final class TicketShow extends Component
]);
}
/**
* Get the first-response SLA headline for the SLA panel.
*/
@@ -421,14 +419,14 @@ final class TicketShow extends Component
public function firstResponseSlaDetail(): string
{
if ($this->ticket->first_response_at !== null) {
return 'Responded in '.$this->formatDurationBetween($this->ticket->created_at, $this->ticket->first_response_at);
return 'Responded in ' . $this->formatDurationBetween($this->ticket->created_at, $this->ticket->first_response_at);
}
if ($this->ticket->sla_first_response_due_at === null) {
return 'No first-response target is set.';
}
return 'Due '.$this->ticket->sla_first_response_due_at->format('d M Y H:i');
return 'Due ' . $this->ticket->sla_first_response_due_at->format('d M Y H:i');
}
/**
@@ -489,14 +487,14 @@ final class TicketShow extends Component
public function resolutionSlaDetail(): string
{
if ($this->ticket->completed_at !== null) {
return 'Resolved in '.$this->formatDurationBetween($this->ticket->created_at, $this->ticket->completed_at);
return 'Resolved in ' . $this->formatDurationBetween($this->ticket->created_at, $this->ticket->completed_at);
}
if ($this->ticket->sla_resolution_due_at === null) {
return 'No resolution target is set.';
}
return 'Due '.$this->ticket->sla_resolution_due_at->format('d M Y H:i');
return 'Due ' . $this->ticket->sla_resolution_due_at->format('d M Y H:i');
}
/**
@@ -539,7 +537,7 @@ final class TicketShow extends Component
$seconds = max(0, $start->diffInSeconds($end));
if ($seconds < 60) {
return $seconds.' seconds';
return $seconds . ' seconds';
}
return CarbonInterval::seconds($seconds)
+3 -2
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Models;
use App\Models\Company;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -23,10 +24,10 @@ final class Category extends Model
/** @var array<string, string> */
protected $casts = ['company_id' => 'integer', 'is_default' => 'boolean'];
/** @return BelongsTo<\App\Models\Company, Category> */
/** @return BelongsTo<Company, Category> */
public function company(): BelongsTo
{
return $this->belongsTo(\App\Models\Company::class, 'company_id');
return $this->belongsTo(Company::class, 'company_id');
}
/** @return HasMany<Ticket> */
+1 -4
View File
@@ -5,10 +5,10 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Models;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Builder;
final class Comment extends Model
{
@@ -89,13 +89,10 @@ final class Comment extends Model
}
/** @param Builder<Comment> $query */
public function scopeActive(Builder $query): Builder
{
return $query->whereNull('tombstoned_at');
}
}
+3 -2
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Models;
use App\Models\Company;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -23,10 +24,10 @@ final class Project extends Model
/** @var array<string, string> */
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
{
return $this->belongsTo(\App\Models\Company::class, 'company_id');
return $this->belongsTo(Company::class, 'company_id');
}
/** @return BelongsTo<Team, Project> */
+2 -2
View File
@@ -9,11 +9,11 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
* @property int $id
* @property string $name
* @property string $slug
* @property string $color
* @property bool $is_closed
* @property bool $is_closed
*/
final class Status extends Model
{
+3 -2
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Models;
use App\Models\Company;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -27,10 +28,10 @@ final class Team extends Model
'is_default' => 'boolean',
];
/** @return BelongsTo<\App\Models\Company, Team> */
/** @return BelongsTo<Company, Team> */
public function company(): BelongsTo
{
return $this->belongsTo(\App\Models\Company::class, 'company_id');
return $this->belongsTo(Company::class, 'company_id');
}
/** @return HasMany<Project> */
+1 -1
View File
@@ -13,7 +13,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* @property int $id
* @property int $id
* @property string $reference
* @property string $subject
* @property string $body
@@ -42,7 +42,7 @@ final class HelpdeskServiceProvider extends ServiceProvider
$this->mergeConfigFrom(
module_path(ModuleInfo::name(), 'Config/config.php'),
ModuleInfo::nameLower()
ModuleInfo::nameLower(),
);
}
@@ -113,7 +113,7 @@ final class HelpdeskServiceProvider extends ServiceProvider
}
$commands = collect(File::allFiles($commandPath))
->map(fn ($file): string => 'Modules\\'.$this->moduleName.'\\Console\\'.$file->getFilenameWithoutExtension())
->map(fn ($file): string => 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension())
->filter(fn (string $class): bool => class_exists($class))
->values()
->all();
@@ -127,7 +127,7 @@ final class HelpdeskServiceProvider extends ServiceProvider
protected function registerConfig(): void
{
$this->publishes([
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');
}
@@ -136,17 +136,17 @@ final class HelpdeskServiceProvider extends ServiceProvider
*/
protected function registerViews(): void
{
$viewPath = resource_path('views/modules/'.ModuleInfo::nameLower());
$viewPath = resource_path('views/modules/' . ModuleInfo::nameLower());
$sourcePath = module_path(ModuleInfo::name(), 'Resources/views');
$this->publishes([
$sourcePath => $viewPath,
], ['views', ModuleInfo::nameLower().'-module-views']);
], ['views', ModuleInfo::nameLower() . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower());
Blade::componentNamespace('Modules\Helpdesk\View\Components', 'helpdesk');
Blade::anonymousComponentPath($sourcePath.'/components', 'helpdesk');
Blade::anonymousComponentPath($sourcePath . '/components', 'helpdesk');
}
/**
@@ -154,7 +154,7 @@ final class HelpdeskServiceProvider extends ServiceProvider
*/
protected function registerTranslations(): void
{
$langPath = resource_path('lang/modules/'.ModuleInfo::nameLower());
$langPath = resource_path('lang/modules/' . ModuleInfo::nameLower());
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, ModuleInfo::nameLower());
@@ -175,8 +175,8 @@ final class HelpdeskServiceProvider extends ServiceProvider
$paths = [];
foreach (Config::get('view.paths') as $path) {
if (is_dir($path.'/modules/'.ModuleInfo::nameLower())) {
$paths[] = $path.'/modules/'.ModuleInfo::nameLower();
if (is_dir($path . '/modules/' . ModuleInfo::nameLower())) {
$paths[] = $path . '/modules/' . ModuleInfo::nameLower();
}
}
+2 -2
View File
@@ -5,10 +5,10 @@ declare(strict_types=1);
use Illuminate\Support\Facades\Route;
use Modules\Helpdesk\Http\Controllers\AttachmentDownloadController;
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\TicketCommentStoreController;
use Modules\Helpdesk\Http\Controllers\TicketAttachmentStoreController;
use Modules\Helpdesk\Http\Controllers\TicketControlsController;
use Modules\Helpdesk\Http\Controllers\TicketStoreController;
use Modules\Helpdesk\Http\Controllers\TicketTimeLogController;
use Modules\Helpdesk\Http\Controllers\WorkflowOpenController;
@@ -26,7 +26,7 @@ final class AgentAssignmentService
->selectRaw('count(*)')
->whereColumn('helpdesk_tickets.agent_id', 'users.id')
->whereNull('helpdesk_tickets.completed_at'),
'open_helpdesk_tickets_count'
'open_helpdesk_tickets_count',
);
if ($categoryId !== null) {
@@ -24,7 +24,7 @@ final class AttachmentService
$mimeType = $file->getClientMimeType();
$size = (int) ($file->getSize() ?: 0);
$path = $file->store('tickets/'.$ticket->id, $disk);
$path = $file->store('tickets/' . $ticket->id, $disk);
if ($size === 0 && $path !== false) {
$size = (int) Storage::disk($disk)->size($path);
@@ -19,7 +19,8 @@ final class GitWebhookIngestionService
/**
* 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}
*/
public function ingest(GitRepository $repository, array $payload, string $provider, ?string $deliveryId, ?string $event): array
@@ -76,7 +77,7 @@ final class GitWebhookIngestionService
'message' => $message,
'url' => $url,
'committed_at' => $committedAt,
]
],
);
$commitCount++;
@@ -102,8 +103,8 @@ final class GitWebhookIngestionService
/**
* Record a provider webhook acceptance, rejection or ignore decision for auditability.
*
* @param array{status: string, message: string, commit_count?: int, linked_count?: int, ignored_count?: int} $result
* @param array<string, mixed>|null $payload
* @param array{status: string, message: string, commit_count?: int, linked_count?: int, ignored_count?: int} $result
* @param array<string, mixed>|null $payload
*/
public function logWebhook(GitRepository $repository, string $provider, ?string $deliveryId, ?string $event, array $result, ?array $payload = null): void
{
@@ -170,7 +171,8 @@ final class GitWebhookIngestionService
}
/**
* @param array<string, mixed> $payload
* @param array<string, mixed> $payload
*
* @return array<int, array<string, mixed>>
*/
private function commitsFromPayload(array $payload): array
@@ -186,7 +188,7 @@ final class GitWebhookIngestionService
private function ticketForCommit(GitRepository $repository, string $message, ?string $branch, ?string $url): ?Ticket
{
$reference = $this->extractTicketReference(trim($message.' '.($branch ?? '').' '.($url ?? '')));
$reference = $this->extractTicketReference(trim($message . ' ' . ($branch ?? '') . ' ' . ($url ?? '')));
if ($reference === null) {
return null;
+3 -2
View File
@@ -5,8 +5,9 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Services;
use App\Models\User;
use Modules\Helpdesk\Models\SlaPolicy;
use Illuminate\Support\Carbon;
use Modules\Helpdesk\Models\Comment;
use Modules\Helpdesk\Models\SlaPolicy;
use Modules\Helpdesk\Models\Ticket;
use Modules\Helpdesk\Models\TicketSlaEvent;
@@ -89,7 +90,7 @@ final class SlaService
private function markFirstResponse(Ticket $ticket, mixed $respondedAt, ?User $actor = null): void
{
$respondedAt = $respondedAt instanceof \Illuminate\Support\Carbon
$respondedAt = $respondedAt instanceof Carbon
? $respondedAt
: now();
@@ -29,7 +29,7 @@ final class TicketReferenceService
{
$year = (int) now()->format('Y');
$prefix = 'HD';
$seriesPrefix = $prefix.'-'.$year.'-';
$seriesPrefix = $prefix . '-' . $year . '-';
$this->lockReferenceSeries($seriesPrefix);
@@ -76,7 +76,7 @@ final class TicketReferenceService
'updated_at' => now(),
]);
return $seriesPrefix.str_pad((string) $nextNumber, 5, '0', STR_PAD_LEFT);
return $seriesPrefix . str_pad((string) $nextNumber, 5, '0', STR_PAD_LEFT);
}
/**
@@ -84,7 +84,7 @@ final class TicketReferenceService
*/
private function nextFromExistingReferences(string $seriesPrefix): string
{
return $seriesPrefix.str_pad((string) ($this->highestExistingSuffix($seriesPrefix) + 1), 5, '0', STR_PAD_LEFT);
return $seriesPrefix . str_pad((string) ($this->highestExistingSuffix($seriesPrefix) + 1), 5, '0', STR_PAD_LEFT);
}
/**
@@ -96,9 +96,9 @@ final class TicketReferenceService
$existingReferences = Ticket::query()
->withoutGlobalScopes()
->withTrashed()
->where('reference', 'like', $seriesPrefix.'%')
->where('reference', 'like', $seriesPrefix . '%')
->pluck('reference')
->filter(static fn (mixed $reference): bool => is_string($reference) && preg_match('/^'.preg_quote($seriesPrefix, '/').'\d+$/', $reference) === 1)
->filter(static fn (mixed $reference): bool => is_string($reference) && preg_match('/^' . preg_quote($seriesPrefix, '/') . '\d+$/', $reference) === 1)
->values()
->all();
@@ -121,7 +121,7 @@ final class TicketReferenceService
}
DB::select('select pg_advisory_xact_lock(hashtext(?))', [
'helpdesk-ticket-reference:'.$seriesPrefix,
'helpdesk-ticket-reference:' . $seriesPrefix,
]);
}
}
+8 -8
View File
@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Services;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Database\QueryException;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
@@ -30,8 +30,8 @@ final class TicketService
/**
* Create a ticket and store initial attachments.
*
* @param array<string, mixed> $data
* @param array<int, UploadedFile> $files
* @param array<string, mixed> $data
* @param array<int, UploadedFile> $files
*/
public function create(User $user, array $data, array $files = []): Ticket
{
@@ -51,8 +51,8 @@ final class TicketService
/**
* Create the ticket while the caller owns the database transaction.
*
* @param array<string, mixed> $data
* @param array<int, UploadedFile> $files
* @param array<string, mixed> $data
* @param array<int, UploadedFile> $files
*/
private function createInsideTransaction(User $user, array $data, array $files = []): Ticket
{
@@ -132,7 +132,7 @@ final class TicketService
/**
* Add a public or internal comment and optional attachments.
*
* @param array<int, UploadedFile> $files
* @param array<int, UploadedFile> $files
*/
public function comment(Ticket $ticket, User $user, string $body, bool $internal = false, array $files = [], ?int $parentId = null, ?string $subject = null): Comment
{
@@ -144,7 +144,7 @@ final class TicketService
->findOrFail($parentId);
$guid = (string) Str::uuid();
$unid = strtoupper(hash('sha256', $guid.'|'.$ticket->id.'|'.microtime(true)));
$unid = strtoupper(hash('sha256', $guid . '|' . $ticket->id . '|' . microtime(true)));
$parentUnid = $parent?->unid;
$rootUnid = $parent?->root_unid ?: $parentUnid ?: $unid;
$plainTextBody = trim(preg_replace('/\s+/', ' ', strip_tags($body)) ?: '');
@@ -159,7 +159,7 @@ final class TicketService
'unid' => $unid,
'parent_unid' => $parentUnid,
'root_unid' => $rootUnid,
'originator_id' => $unid.':1',
'originator_id' => $unid . ':1',
'sync_sequence' => 1,
'sync_hash' => hash('sha256', $body),
'source_system' => 'wms-helpdesk',
@@ -133,16 +133,16 @@ final class WorkflowNotificationService
private function wrapHtml(string $body, Ticket $ticket, ?string $trackingToken = null): string
{
return '<div style="font-family:Inter,Arial,sans-serif;background:#f6f8fb;padding:24px">'
.'<div style="max-width:720px;margin:0 auto;background:#ffffff;border:1px solid #d8dee8;border-radius:18px;overflow:hidden">'
.'<div style="background:#0f172a;color:#ffffff;padding:24px 28px">'
.'<div style="font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:#bfdbfe">Helpdesk notification</div>'
.'<h1 style="margin:8px 0 0;font-size:22px">'.e($ticket->reference).'</h1>'
.'<p style="margin:8px 0 0;color:#dbeafe">'.e($ticket->subject).'</p>'
.'</div><div style="padding:28px;color:#1f2937;line-height:1.65">'
.$body
.'<p style="margin-top:28px"><a href="'.e(route('helpdesk.tickets.show', ['ticket' => $ticket->getKey()])).'" style="display:inline-block;background:#2563eb;color:#ffffff;text-decoration:none;padding:11px 16px;border-radius:10px">Open ticket</a></p>'
.$this->trackingPixel($trackingToken)
.'</div></div></div>';
. '<div style="max-width:720px;margin:0 auto;background:#ffffff;border:1px solid #d8dee8;border-radius:18px;overflow:hidden">'
. '<div style="background:#0f172a;color:#ffffff;padding:24px 28px">'
. '<div style="font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:#bfdbfe">Helpdesk notification</div>'
. '<h1 style="margin:8px 0 0;font-size:22px">' . e($ticket->reference) . '</h1>'
. '<p style="margin:8px 0 0;color:#dbeafe">' . e($ticket->subject) . '</p>'
. '</div><div style="padding:28px;color:#1f2937;line-height:1.65">'
. $body
. '<p style="margin-top:28px"><a href="' . e(route('helpdesk.tickets.show', ['ticket' => $ticket->getKey()])) . '" style="display:inline-block;background:#2563eb;color:#ffffff;text-decoration:none;padding:11px 16px;border-radius:10px">Open ticket</a></p>'
. $this->trackingPixel($trackingToken)
. '</div></div></div>';
}
private function trackingPixel(?string $trackingToken): string
@@ -153,9 +153,9 @@ final class WorkflowNotificationService
$url = Route::has('helpdesk.workflow.open')
? route('helpdesk.workflow.open', ['token' => $trackingToken])
: URL::to('/'.trim((string) config('helpdesk.route_prefix', 'helpdesk'), '/').'/workflow/open/'.$trackingToken);
: URL::to('/' . trim((string) config('helpdesk.route_prefix', 'helpdesk'), '/') . '/workflow/open/' . $trackingToken);
return '<img src="'.e($url).'" width="1" height="1" alt="" style="display:none" />';
return '<img src="' . e($url) . '" width="1" height="1" alt="" style="display:none" />';
}
private function log(WorkflowTemplate $template, Ticket $ticket, string $event, ?string $recipient, string $status, ?string $error = null, ?string $subject = null, ?string $trackingToken = null): void
@@ -41,7 +41,7 @@ final class HelpdeskSettingsBrowserTest extends DuskTestCase
private function openSettingsSection(Browser $browser, string $section, string $expectedHeading): Browser
{
return $browser
->click('@helpdesk-settings-section-'.$section)
->click('@helpdesk-settings-section-' . $section)
->waitForText($expectedHeading, 10);
}
}
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Tests\Browser;
use Laravel\Dusk\Browser;
use Modules\Helpdesk\Models\CannedResponse;
use Modules\Helpdesk\Models\Comment;
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
use Tests\DuskTestCase;
@@ -19,7 +18,7 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase
$fixtures = $this->seedLookupData();
$user = $this->user(['name' => 'Ticket Browser User']);
$subject = 'Browser created helpdesk ticket '.uniqid();
$subject = 'Browser created helpdesk ticket ' . uniqid();
$this->browse(function (Browser $browser) use ($user, $fixtures, $subject): void {
$browser->loginAs($user)
@@ -65,11 +64,10 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase
'user_id' => $user->id,
'parent_id' => null,
'title' => 'Browser canned response',
'body' => '<p>Hello Response Browser User</p><p>Inserted canned browser response for '.$ticket->reference.'</p>',
'body' => '<p>Hello Response Browser User</p><p>Inserted canned browser response for ' . $ticket->reference . '</p>',
'is_internal' => false,
]);
Comment::query()->create([
'ticket_id' => $ticket->id,
'user_id' => $user->id,
@@ -91,7 +89,7 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase
$this->assertDatabaseHas('helpdesk_comments', [
'ticket_id' => $ticket->id,
'parent_id' => null,
'body' => '<p>Hello Response Browser User</p><p>Inserted canned browser response for '.$ticket->reference.'</p>',
'body' => '<p>Hello Response Browser User</p><p>Inserted canned browser response for ' . $ticket->reference . '</p>',
]);
$this->assertDatabaseHas('helpdesk_comments', [
@@ -137,7 +135,7 @@ document.body.appendChild(form);
form.submit();
JS);
$browser->waitForLocation('/helpdesk/tickets/'.$ticketId, 10);
$browser->waitForLocation('/helpdesk/tickets/' . $ticketId, 10);
}
private function setHelpdeskEditor(Browser $browser, string $dusk, string $html): void
@@ -134,7 +134,7 @@ trait BuildsHelpdeskFixtures
unset($attributes['owner'], $attributes['agent']);
return Ticket::query()->create(array_merge([
'reference' => 'HD-'.now()->format('Y').'-'.str_pad((string) random_int(1, 99999), 5, '0', STR_PAD_LEFT),
'reference' => 'HD-' . now()->format('Y') . '-' . str_pad((string) random_int(1, 99999), 5, '0', STR_PAD_LEFT),
'user_id' => $owner->id,
'agent_id' => $agent?->id,
'status_id' => $this->ticketStatus()->id,
@@ -32,7 +32,7 @@ final class GitAndAttachmentTest extends TestCase
'branch' => 'develop',
'author_name' => 'Mark Gregory',
'author_email' => 'mark@example.com',
'message' => 'Resolve '.$ticket->reference.' ticket save redirect',
'message' => 'Resolve ' . $ticket->reference . ' ticket save redirect',
'url' => 'https://example.com/commit/b',
'committed_at' => now(),
]);
@@ -25,12 +25,12 @@ final class GitWebhookIngestionTest extends TestCase
]);
$ticket = $this->ticket(['project_id' => $fixtures['project']->id]);
$payload = [
'ref' => 'refs/heads/feature/'.$ticket->reference.'-automation',
'ref' => 'refs/heads/feature/' . $ticket->reference . '-automation',
'commits' => [
[
'id' => str_repeat('a', 40),
'message' => $ticket->reference.' Add webhook ingestion',
'url' => 'https://github.example.test/org/repo/commit/'.str_repeat('a', 40),
'message' => $ticket->reference . ' Add webhook ingestion',
'url' => 'https://github.example.test/org/repo/commit/' . str_repeat('a', 40),
'timestamp' => '2026-06-15T10:00:00+00:00',
'author' => ['name' => 'Mark Gregory', 'email' => 'mark@example.com'],
],
@@ -43,7 +43,7 @@ final class GitWebhookIngestionTest extends TestCase
'HTTP_ACCEPT' => 'application/json',
'HTTP_X_GITHUB_EVENT' => 'push',
'HTTP_X_GITHUB_DELIVERY' => 'delivery-123',
'HTTP_X_HUB_SIGNATURE_256' => 'sha256='.hash_hmac('sha256', $json, 'github-secret'),
'HTTP_X_HUB_SIGNATURE_256' => 'sha256=' . hash_hmac('sha256', $json, 'github-secret'),
], $json);
$response->assertStatus(202)->assertJsonPath('status', 'accepted')->assertJsonPath('linked_count', 1);
@@ -53,7 +53,7 @@ final class GitWebhookIngestionTest extends TestCase
'ticket_id' => $ticket->id,
'hash' => str_repeat('a', 40),
'short_hash' => str_repeat('a', 12),
'branch' => 'feature/'.$ticket->reference.'-automation',
'branch' => 'feature/' . $ticket->reference . '-automation',
]);
$this->assertDatabaseHas('helpdesk_git_webhook_logs', [
@@ -96,7 +96,7 @@ final class GitWebhookIngestionTest extends TestCase
'CONTENT_TYPE' => 'application/json',
'HTTP_ACCEPT' => 'application/json',
'HTTP_X_GITHUB_EVENT' => 'ping',
'HTTP_X_HUB_SIGNATURE_256' => 'sha256='.hash_hmac('sha256', $json, 'github-secret'),
'HTTP_X_HUB_SIGNATURE_256' => 'sha256=' . hash_hmac('sha256', $json, 'github-secret'),
], $json);
$response->assertStatus(202)->assertJsonPath('status', 'ignored');
@@ -125,8 +125,8 @@ final class GitWebhookIngestionTest extends TestCase
'commits' => [
[
'id' => str_repeat('c', 40),
'message' => 'Fix '.$ticket->reference.' SLA report',
'url' => 'https://gitlab.example.test/org/repo/-/commit/'.str_repeat('c', 40),
'message' => 'Fix ' . $ticket->reference . ' SLA report',
'url' => 'https://gitlab.example.test/org/repo/-/commit/' . str_repeat('c', 40),
'timestamp' => '2026-06-15T11:00:00+00:00',
'author' => ['name' => 'Support Agent', 'email' => 'agent@example.com'],
],
@@ -21,7 +21,7 @@ final class HelpdeskLivewireAssetsTest extends TestCase
self::assertLessThan(
strpos($layout, '@fluxScripts'),
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.',
);
}
}
@@ -95,7 +95,7 @@ final class RedmineInspiredHelpdeskFeatureTest extends TestCase
->test(TicketShow::class, ['ticket' => $ticket])
->set('canned_response_id', $response->id)
->call('applyCannedResponse')
->assertSet('commentBody', 'Hello Customer Jane, ticket '.$ticket->reference.' is being reviewed by Agent Smith.');
->assertSet('commentBody', 'Hello Customer Jane, ticket ' . $ticket->reference . ' is being reviewed by Agent Smith.');
}
public function test_sla_service_records_first_response_and_resolution_breaches(): void
@@ -214,7 +214,7 @@ final class SettingsCrudTest extends TestCase
$checklist = ChecklistTemplate::query()->where('slug', 'release-checklist')->firstOrFail();
Livewire::test(SettingsManager::class)
->call('editChecklistTemplate', $checklist->id)
->set('checklistTemplateForm.items_text', "Updated item")
->set('checklistTemplateForm.items_text', 'Updated item')
->call('saveChecklistTemplate')
->assertHasNoErrors();
$this->assertDatabaseHas('helpdesk_checklist_templates', ['id' => $checklist->id]);
@@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Modules\Helpdesk\Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Models\Company;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
@@ -13,6 +13,7 @@ use Livewire\Livewire;
use Modules\Helpdesk\Livewire\TicketShow;
use Modules\Helpdesk\Models\Attachment;
use Modules\Helpdesk\Models\Comment;
use Modules\Helpdesk\Services\TicketService;
use Modules\Helpdesk\Tests\Concerns\BuildsHelpdeskFixtures;
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
{
$fixtures = $this->seedLookupData();
@@ -98,7 +98,7 @@ final class TicketConversationTest extends TestCase
$fixtures = $this->seedLookupData();
$owner = $this->user();
$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)
->post(route('helpdesk.tickets.comments.store', $ticket), [
@@ -115,8 +115,6 @@ final class TicketConversationTest extends TestCase
self::assertSame('RE: Parent response', $reply->subject);
}
public function test_existing_ticket_accepts_multiple_supporting_file_attachments(): void
{
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]);
$owner = $this->user(['company_id' => $company->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)
->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
{
$this->seedLookupData();
@@ -137,7 +136,7 @@ final class TicketCrudTest extends TestCase
$ticket = Ticket::query()->where('subject', 'Reference check')->firstOrFail();
self::assertStringStartsWith('HD-'.now()->format('Y').'-', $ticket->reference);
self::assertStringStartsWith('HD-' . now()->format('Y') . '-', $ticket->reference);
self::assertNotNull($ticket->last_activity_at);
}
}
@@ -13,7 +13,7 @@ final class TicketFirstResponseSlaFilesTest extends TestCase
*/
public function test_public_response_updates_first_response_sla_state(): void
{
$servicePath = dirname(__DIR__, 2).'/Services/SlaService.php';
$servicePath = dirname(__DIR__, 2) . '/Services/SlaService.php';
self::assertFileExists($servicePath);
@@ -30,8 +30,8 @@ final class TicketFirstResponseSlaFilesTest extends TestCase
*/
public function test_ticket_show_synchronises_first_response_from_existing_public_comments(): void
{
$componentPath = dirname(__DIR__, 2).'/Livewire/TicketShow.php';
$servicePath = dirname(__DIR__, 2).'/Services/SlaService.php';
$componentPath = dirname(__DIR__, 2) . '/Livewire/TicketShow.php';
$servicePath = dirname(__DIR__, 2) . '/Services/SlaService.php';
$component = file_get_contents($componentPath);
$service = file_get_contents($servicePath);
@@ -48,7 +48,7 @@ final class TicketFirstResponseSlaFilesTest extends TestCase
*/
public function test_sla_card_has_clear_responded_and_awaiting_public_reply_states(): void
{
$viewPath = dirname(__DIR__, 2).'/Resources/views/livewire/ticket-show.blade.php';
$viewPath = dirname(__DIR__, 2) . '/Resources/views/livewire/ticket-show.blade.php';
$view = file_get_contents($viewPath);
@@ -13,7 +13,7 @@ final class TicketPublicResponseVisibilityFilesTest extends TestCase
*/
public function test_main_ticket_response_form_has_public_visibility_control(): void
{
$viewPath = dirname(__DIR__, 2).'/Resources/views/livewire/ticket-show.blade.php';
$viewPath = dirname(__DIR__, 2) . '/Resources/views/livewire/ticket-show.blade.php';
self::assertFileExists($viewPath);
@@ -33,7 +33,7 @@ final class TicketPublicResponseVisibilityFilesTest extends TestCase
*/
public function test_threaded_responses_have_public_visibility_control_and_badges(): void
{
$partialPath = dirname(__DIR__, 2).'/Resources/views/partials/comment-thread.blade.php';
$partialPath = dirname(__DIR__, 2) . '/Resources/views/partials/comment-thread.blade.php';
self::assertFileExists($partialPath);
@@ -13,8 +13,8 @@ final class TicketSlaOperationalDetailFilesTest extends TestCase
*/
public function test_first_response_sla_card_shows_duration_responder_and_timestamp(): void
{
$componentPath = dirname(__DIR__, 2).'/Livewire/TicketShow.php';
$viewPath = dirname(__DIR__, 2).'/Resources/views/livewire/ticket-show.blade.php';
$componentPath = dirname(__DIR__, 2) . '/Livewire/TicketShow.php';
$viewPath = dirname(__DIR__, 2) . '/Resources/views/livewire/ticket-show.blade.php';
$component = file_get_contents($componentPath);
$view = file_get_contents($viewPath);
@@ -33,8 +33,8 @@ final class TicketSlaOperationalDetailFilesTest extends TestCase
*/
public function test_sla_cards_have_colour_coded_state_helpers(): void
{
$componentPath = dirname(__DIR__, 2).'/Livewire/TicketShow.php';
$viewPath = dirname(__DIR__, 2).'/Resources/views/livewire/ticket-show.blade.php';
$componentPath = dirname(__DIR__, 2) . '/Livewire/TicketShow.php';
$viewPath = dirname(__DIR__, 2) . '/Resources/views/livewire/ticket-show.blade.php';
$component = file_get_contents($componentPath);
$view = file_get_contents($viewPath);
@@ -55,8 +55,8 @@ final class TicketSlaOperationalDetailFilesTest extends TestCase
*/
public function test_sla_audit_trail_is_visible_on_ticket_page(): void
{
$servicePath = dirname(__DIR__, 2).'/Services/SlaService.php';
$viewPath = dirname(__DIR__, 2).'/Resources/views/livewire/ticket-show.blade.php';
$servicePath = dirname(__DIR__, 2) . '/Services/SlaService.php';
$viewPath = dirname(__DIR__, 2) . '/Resources/views/livewire/ticket-show.blade.php';
$service = file_get_contents($servicePath);
$view = file_get_contents($viewPath);
@@ -75,7 +75,7 @@ final class TicketSlaOperationalDetailFilesTest extends TestCase
*/
public function test_public_reply_count_uses_all_ticket_comments(): void
{
$componentPath = dirname(__DIR__, 2).'/Livewire/TicketShow.php';
$componentPath = dirname(__DIR__, 2) . '/Livewire/TicketShow.php';
$component = file_get_contents($componentPath);
@@ -26,6 +26,7 @@ final class TicketTimerButtonSizeFilesTest extends TestCase
self::assertStringNotContainsString('px-3 py-1.5 text-xs', $view);
self::assertStringNotContainsString('ticket-timer.js', $view);
}
/**
* Ensure the active timer action also remains readable and single-line.
*/
@@ -16,7 +16,7 @@ class TicketTimerFilesTest extends TestCase
$this->assertStringStartsWith('<div', $view);
$this->assertFalse(
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.',
);
}
@@ -50,7 +50,7 @@ final class WorkflowNotificationTest extends TestCase
'event' => 'ticket.created',
'recipient' => 'requester@example.com',
'status' => 'sent',
'subject' => 'Ticket '.$ticket->reference.' raised for Requester Name',
'subject' => 'Ticket ' . $ticket->reference . ' raised for Requester Name',
]);
}
@@ -18,39 +18,39 @@ final class TicketReferenceServiceTest extends TestCase
{
$reference = app(TicketReferenceService::class)->next();
self::assertSame('HD-'.now()->format('Y').'-00001', $reference);
self::assertSame('HD-' . now()->format('Y') . '-00001', $reference);
}
public function test_it_generates_the_next_reference_after_existing_tickets(): void
{
$this->ticket(['reference' => 'HD-'.now()->format('Y').'-00041']);
$this->ticket(['reference' => 'HD-' . now()->format('Y') . '-00041']);
$reference = app(TicketReferenceService::class)->next();
self::assertSame('HD-'.now()->format('Y').'-00042', $reference);
self::assertSame('HD-' . now()->format('Y') . '-00042', $reference);
}
public function test_it_does_not_reuse_soft_deleted_ticket_references(): void
{
$year = now()->format('Y');
$ticket = $this->ticket(['reference' => 'HD-'.$year.'-00002']);
$ticket = $this->ticket(['reference' => 'HD-' . $year . '-00002']);
$ticket->delete();
$reference = app(TicketReferenceService::class)->next();
self::assertSame('HD-'.$year.'-00003', $reference);
self::assertSame('HD-' . $year . '-00003', $reference);
}
public function test_it_uses_the_highest_reference_suffix_instead_of_latest_database_id(): void
{
$year = now()->format('Y');
$this->ticket(['reference' => 'HD-'.$year.'-00042']);
$this->ticket(['reference' => 'HD-'.$year.'-00002']);
$this->ticket(['reference' => 'HD-' . $year . '-00042']);
$this->ticket(['reference' => 'HD-' . $year . '-00002']);
$reference = app(TicketReferenceService::class)->next();
self::assertSame('HD-'.$year.'-00043', $reference);
self::assertSame('HD-' . $year . '-00043', $reference);
}
}
@@ -13,7 +13,7 @@ class InternalApiServiceProvider extends ServiceProvider
*/
public function boot(): void
{
Route::middleware('api')->prefix('api/internal')->group(__DIR__.'/../routes/api.php');
Route::middleware('api')->prefix('api/internal')->group(__DIR__ . '/../routes/api.php');
app('router')->aliasMiddleware('internal.signed', VerifyInternalApiSignature::class);
}
}
@@ -23,9 +23,9 @@ class VerifyInternalApiSignature
$body = $request->getContent();
$payloads = array_unique([
$timestamp.'.'.$body,
$timestamp.'.',
$timestamp.'.[]',
$timestamp . '.' . $body,
$timestamp . '.',
$timestamp . '.[]',
]);
foreach ($payloads as $payload) {
@@ -19,7 +19,7 @@ class InternalApiSignatureTest extends TestCase
config(['platform.internal_api_key' => 'testing-secret']);
$timestamp = now()->toIso8601String();
$body = '';
$signature = hash_hmac('sha256', $timestamp.'.'.$body, 'testing-secret');
$signature = hash_hmac('sha256', $timestamp . '.' . $body, 'testing-secret');
$this->withHeaders([
'X-WMS-Timestamp' => $timestamp,
@@ -11,8 +11,8 @@ class InventoryManagementServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'inventorymanagement');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'inventorymanagement');
}
/**
@@ -11,8 +11,8 @@ class M3IntegrationServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'm3integration');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'm3integration');
}
/**
@@ -15,7 +15,7 @@ class PlatformServiceProvider extends ServiceProvider
*/
public function register(): void
{
$this->mergeConfigFrom(__DIR__.'/../config/platform.php', 'platform');
$this->mergeConfigFrom(__DIR__ . '/../config/platform.php', 'platform');
}
/**
@@ -23,10 +23,10 @@ class PlatformServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'platform');
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
$this->loadViewsFrom(__DIR__ . '/../resources/views', 'platform');
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
Route::middleware('web')->group(__DIR__.'/../routes/web.php');
Route::middleware('web')->group(__DIR__ . '/../routes/web.php');
app('router')->aliasMiddleware('tenant.assigned', EnsureTenantAssigned::class);
app('router')->aliasMiddleware('tenant.licensed', EnsureLicensedTenant::class);
@@ -97,7 +97,7 @@ class PlatformPriceController extends Controller
private function validatedPriceData(Request $request, ?PlatformPrice $price = null): array
{
$data = $request->validate([
'code' => ['nullable', 'string', 'max:40', 'unique:platform_prices,code,'.($price?->id ?? 'NULL')],
'code' => ['nullable', 'string', 'max:40', 'unique:platform_prices,code,' . ($price?->id ?? 'NULL')],
'name' => ['required', 'string', 'max:255'],
'strapline' => ['nullable', 'string', 'max:255'],
'description' => ['nullable', 'string', 'max:2000'],
@@ -32,7 +32,7 @@ class StripeWebhookController extends Controller
'description' => 'Stripe payment received',
'paid_at' => now(),
'metadata' => $intent,
]
],
);
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ class LicenseKey extends Model
public static function issueFor(Company $company, string $label, string $plan = 'standard', int $maxUsers = 10): string
{
$plainKey = 'WMS-'.Str::upper(Str::random(32));
$plainKey = 'WMS-' . Str::upper(Str::random(32));
self::create([
'company_id' => $company->id,
@@ -48,7 +48,7 @@ class PlatformPrice extends Model
'gbp' => '£',
'eur' => '€',
'usd' => '$',
default => strtoupper($this->currency).' ',
default => strtoupper($this->currency) . ' ',
};
}
@@ -61,7 +61,7 @@ class PlatformPrice extends Model
return '—';
}
return $this->currencySymbol().number_format($amount / 100, 2);
return $this->currencySymbol() . number_format($amount / 100, 2);
}
/**
@@ -29,7 +29,7 @@ class PlatformSeeder extends Seeder
'timezone' => 'Europe/London',
'is_system_company' => true,
'billing_email' => 'mark.gregory@mpba.co.uk',
]
],
);
$user = User::updateOrCreate(
@@ -55,7 +55,7 @@ class PlatformSeeder extends Seeder
'is_company_admin' => true,
'status' => 'active',
'email_verified_at' => now(),
]
],
);
$user->assignRole($role);
$warehouse = Warehouse::firstOrNew([
@@ -72,7 +72,7 @@ class PlatformSeeder extends Seeder
WarehouseValidationRule::updateOrCreate(
['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(
['code' => $plan['code']],
$payload
$payload,
);
}
}
+1 -1
View File
@@ -8,6 +8,6 @@ if (! function_exists('pricing_feature_enabled')) {
*/
function pricing_feature_enabled(string $key): bool
{
return (bool) config(ModuleInfo::nameLower().".features.{$key}", false);
return (bool) config(ModuleInfo::nameLower() . ".features.{$key}", false);
}
}
@@ -47,7 +47,7 @@ class PricingServiceProvider extends ServiceProvider
*/
protected function registerHelpers(): void
{
foreach (glob(__DIR__.'/../Helpers/*.php') as $filename) {
foreach (glob(__DIR__ . '/../Helpers/*.php') as $filename) {
require_once $filename;
}
}
@@ -60,10 +60,10 @@ class PricingServiceProvider extends ServiceProvider
if (! $this->app->runningInConsole()) {
return;
}
$commandPath = __DIR__.'/../Console';
$commandPath = __DIR__ . '/../Console';
$commands = collect(File::allFiles($commandPath))
->map(function ($file) {
$class = 'Modules\\'.$this->moduleName.'\\Console\\'.$file->getFilenameWithoutExtension();
$class = 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
return $class;
})
@@ -81,10 +81,11 @@ class PricingServiceProvider extends ServiceProvider
protected function registerConfig(): void
{
$this->publishes([
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');
$this->mergeConfigFrom(
module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower()
module_path(ModuleInfo::name(), 'Config/config.php'),
ModuleInfo::nameLower(),
);
}
@@ -93,11 +94,11 @@ class PricingServiceProvider extends ServiceProvider
*/
public function registerViews(): void
{
$viewPath = resource_path('views/modules/'.ModuleInfo::nameLower());
$viewPath = resource_path('views/modules/' . ModuleInfo::nameLower());
$sourcePath = module_path(ModuleInfo::name(), 'Resources/views');
$this->publishes([
$sourcePath => $viewPath,
], ['views', ModuleInfo::nameLower().'-module-views']);
], ['views', ModuleInfo::nameLower() . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower());
}
@@ -106,7 +107,7 @@ class PricingServiceProvider extends ServiceProvider
*/
public function registerTranslations(): void
{
$langPath = resource_path('lang/modules/'.ModuleInfo::nameLower());
$langPath = resource_path('lang/modules/' . ModuleInfo::nameLower());
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, ModuleInfo::nameLower());
@@ -122,8 +123,8 @@ class PricingServiceProvider extends ServiceProvider
{
$paths = [];
foreach (Config::get('view.paths') as $path) {
if (is_dir($path.'/modules/'.ModuleInfo::nameLower())) {
$paths[] = $path.'/modules/'.ModuleInfo::nameLower();
if (is_dir($path . '/modules/' . ModuleInfo::nameLower())) {
$paths[] = $path . '/modules/' . ModuleInfo::nameLower();
}
}
@@ -11,8 +11,8 @@ class ReplenishmentServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'replenishment');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'replenishment');
}
/**
@@ -11,8 +11,8 @@ class ReportingServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'reporting');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'reporting');
}
/**
@@ -11,8 +11,8 @@ class SecurityServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'security');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'security');
}
/**
@@ -11,8 +11,8 @@ class SentinelIntegrationServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'sentinelintegration');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'sentinelintegration');
}
/**
@@ -11,8 +11,8 @@ class ShippingServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'shipping');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'shipping');
}
/**
@@ -11,8 +11,8 @@ class TransportationServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadRoutesFrom(__DIR__.'/../Routes/web.php');
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'transportation');
$this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'transportation');
}
/**
@@ -12,8 +12,8 @@ class WarehouseServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'warehouse');
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
Route::middleware('web')->group(__DIR__.'/../routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../resources/views', 'warehouse');
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
Route::middleware('web')->group(__DIR__ . '/../routes/web.php');
}
}
@@ -6,7 +6,6 @@ use App\Http\Controllers\Controller;
use Modules\Warehouse\app\Models\Warehouse;
use Modules\Warehouse\app\Models\WarehouseValidationRule;
class WarehouseDashboardController extends Controller
{
/**
+1 -1
View File
@@ -21,7 +21,7 @@ class Warehouse extends Model
];
protected $casts = [
'settings' => 'array'
'settings' => 'array',
];
protected static function booted(): void
+3 -3
View File
@@ -18,7 +18,7 @@ class CreateNewUser implements CreatesNewUsers
/**
* Validate and create a newly registered user with an isolated company tenant.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
public function create(array $input): User
{
@@ -29,11 +29,11 @@ class CreateNewUser implements CreatesNewUsers
])->validate();
return DB::transaction(function () use ($input): User {
$companyName = $input['company_name'] ?? ($input['name'].' Company');
$companyName = $input['company_name'] ?? ($input['name'] . ' Company');
$company = Company::create([
'name' => $companyName,
'slug' => Str::slug($companyName.'-'.Str::random(6)),
'slug' => Str::slug($companyName . '-' . Str::random(6)),
'status' => 'active',
'timezone' => 'Europe/London',
]);
+1 -1
View File
@@ -14,7 +14,7 @@ class ResetUserPassword implements ResetsUserPasswords
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
public function reset(User $user, array $input): void
{
+1 -1
View File
@@ -15,7 +15,7 @@ class UpdateUserPassword implements UpdatesUserPasswords
/**
* Validate and update the user's password.
*
* @param array<string, string> $input
* @param array<string, string> $input
*
* @throws ValidationException
*/
@@ -14,7 +14,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
/**
* Validate and update the given user's profile information.
*
* @param array<string, string> $input
* @param array<string, string> $input
*
* @throws ValidationException
*/
@@ -46,7 +46,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{
+2 -1
View File
@@ -37,7 +37,8 @@ class AppServiceProvider extends ServiceProvider
app()->isProduction(),
);
Password::defaults(fn (): ?Password => app()->isProduction()
Password::defaults(
fn (): ?Password => app()->isProduction()
? Password::min(12)
->mixedCase()
->letters()
+2 -2
View File
@@ -64,7 +64,7 @@ class FortifyServiceProvider extends ServiceProvider
});
RateLimiter::for('login', function (Request $request) {
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip());
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())) . '|' . $request->ip());
return Limit::perMinute(5)->by($throttleKey);
});
@@ -73,7 +73,7 @@ class FortifyServiceProvider extends ServiceProvider
$credentialId = $request->input('credential.id');
return Limit::perMinute(10)->by(
($credentialId ?: $request->session()->getId()).'|'.$request->ip(),
($credentialId ?: $request->session()->getId()) . '|' . $request->ip(),
);
});
}

Some files were not shown because too many files have changed in this diff Show More