M3 Integration fixes

This commit is contained in:
Mark Gregory
2026-09-21 10:14:57 +01:00
parent 4af997c465
commit 6d60cbff84
5853 changed files with 79032 additions and 1825 deletions
-14
View File
@@ -1,14 +0,0 @@
APP_ENV=testing
APP_URL=https://wms.vm
APP_KEY=base64:O0djxuJapvByUFNx5avmY7od2Y6C07JYv2kAFNrlrMI=
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=wms_test
DB_USERNAME=wms_user
DB_PASSWORD=987400Kmh
SESSION_DRIVER=file
CACHE_STORE=array
QUEUE_CONNECTION=sync
MAIL_MAILER=array
View File
View File
-5
View File
@@ -1,5 +0,0 @@
# Analytics
Display KPIs, warehouse performance trends, executive summaries, and OTTC analysis.
Contains actual sample Blade views and placeholder service provider/routes only.
+1
View File
@@ -64,6 +64,7 @@
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover align-middle mb-0"> <table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light"> <thead class="table-light">
<tr> <tr>
<th>Metric</th> <th>Metric</th>
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
-5
View File
@@ -1,5 +0,0 @@
# Audit
Review activity logs, data changes, interface events, and administrative actions.
Contains actual sample Blade views and placeholder service provider/routes only.
+1
View File
@@ -64,6 +64,7 @@
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover align-middle mb-0"> <table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light"> <thead class="table-light">
<tr> <tr>
<th>When</th> <th>When</th>
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+1 -3
View File
@@ -63,9 +63,7 @@ class AuditTrailServiceProvider extends ServiceProvider
$commandPath = __DIR__ . '/../Console'; $commandPath = __DIR__ . '/../Console';
$commands = collect(File::allFiles($commandPath)) $commands = collect(File::allFiles($commandPath))
->map(function ($file) { ->map(function ($file) {
$class = 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension(); return 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
return $class;
}) })
->filter(function ($class) { ->filter(function ($class) {
return class_exists($class); return class_exists($class);
-12
View File
@@ -14,18 +14,6 @@ class RouteServiceProvider extends ServiceProvider
*/ */
protected $moduleNamespace = 'Modules\AuditTrail\Http\Controllers'; protected $moduleNamespace = 'Modules\AuditTrail\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/** /**
* Define the routes for the application. * Define the routes for the application.
* *
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
View File
-5
View File
@@ -1,5 +0,0 @@
# Carrier Management
Maintain carriers, services, and shipment routing master data.
Contains actual sample Blade views and placeholder service provider/routes only.
+1
View File
@@ -64,6 +64,7 @@
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover align-middle mb-0"> <table class="table table-hover align-middle mb-0">
<caption class="sr-only">Data table</caption>
<thead class="table-light"> <thead class="table-light">
<tr> <tr>
<th>Carrier</th> <th>Carrier</th>
View File
View File
View File
View File
Regular → Executable
View File
+2 -3
View File
@@ -2,7 +2,6 @@
return [ return [
'name' => 'ChatGPT', 'name' => 'ChatGPT',
'features' => 'features' => [
[ ],
],
]; ];
Regular → Executable
View File
+28 -29
View File
@@ -22,42 +22,41 @@ class DeleteOpenAiFilesCommand extends Command
if (! $setting) { if (! $setting) {
$this->error('No active ChatGPT/OpenAI API key is stored.'); $this->error('No active ChatGPT/OpenAI API key is stored.');
return self::FAILURE; return self::FAILURE;
} }
$service = new OpenAiFileService((string) $setting->api_key);
try { try {
if ((bool) $this->option('dry-run')) { return $this->executeFor(new OpenAiFileService((string) $setting->api_key));
$files = $service->listFiles();
$this->info('Found ' . count($files) . ' OpenAI API file(s).');
foreach ($files as $file) {
$this->line(sprintf(
'- %s %s',
(string) ($file['id'] ?? 'unknown'),
(string) ($file['filename'] ?? 'unnamed')
));
}
return self::SUCCESS;
}
$result = $service->deleteAllFiles();
$this->info("Listed: {$result['listed']}");
$this->info("Deleted: {$result['deleted']}");
$this->info("Failed: {$result['failed']}");
foreach ($result['failures'] as $failure) {
$this->error($failure['file_id'] . ': ' . $failure['error']);
}
return $result['failed'] === 0 ? self::SUCCESS : self::FAILURE;
} catch (Throwable $exception) { } catch (Throwable $exception) {
$this->error($exception->getMessage()); $this->error($exception->getMessage());
return self::FAILURE; return self::FAILURE;
} }
} }
private function executeFor(OpenAiFileService $service): int
{
if ((bool) $this->option('dry-run')) {
$files = $service->listFiles();
$this->info('Found ' . count($files) . ' OpenAI API file(s).');
foreach ($files as $file) {
$this->line(sprintf('- %s %s', (string) ($file['id'] ?? 'unknown'), (string) ($file['filename'] ?? 'unnamed')));
}
return self::SUCCESS;
}
$result = $service->deleteAllFiles();
$this->info("Listed: {$result['listed']}");
$this->info("Deleted: {$result['deleted']}");
$this->info("Failed: {$result['failed']}");
foreach ($result['failures'] as $failure) {
$this->error($failure['file_id'] . ': ' . $failure['error']);
}
return $result['failed'] === 0 ? self::SUCCESS : self::FAILURE;
}
} }
View File
View File
+1 -2
View File
@@ -2,8 +2,8 @@
namespace Modules\ChatGPT\Database\Seeders; namespace Modules\ChatGPT\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
class ChatGPTDatabaseSeeder extends Seeder class ChatGPTDatabaseSeeder extends Seeder
{ {
@@ -16,6 +16,5 @@ class ChatGPTDatabaseSeeder extends Seeder
{ {
Model::unguard(); Model::unguard();
// $this->call("OthersTableSeeder");
} }
} }
View File
+9
View File
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Modules\ChatGPT\Exceptions;
use RuntimeException;
final class OpenAiFileException extends RuntimeException {}
Regular → Executable
View File
+2 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
use Modules\ChatGPT\Support\ModuleInfo; use Modules\ChatGPT\Support\ModuleInfo;
if (! function_exists('chatgpt_feature_enabled')) { if (! function_exists('chatgpt_feature_enabled')) {
@@ -7,6 +8,6 @@ if (! function_exists('chatgpt_feature_enabled')) {
*/ */
function chatgpt_feature_enabled(string $key): bool function chatgpt_feature_enabled(string $key): bool
{ {
return (bool) config(ModuleInfo::nameLower().".features.{$key}", false); return (bool) config(ModuleInfo::nameLower() . ".features.{$key}", false);
} }
} }
View File
View File
View File
View File
View File
View File
Regular → Executable
View File
+1 -1
View File
@@ -2,8 +2,8 @@
namespace Modules\ChatGPT\Models; namespace Modules\ChatGPT\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ChatGptApiSetting extends Model class ChatGptApiSetting extends Model
{ {
View File
+38 -38
View File
@@ -2,31 +2,29 @@
namespace Modules\ChatGPT\Providers; namespace Modules\ChatGPT\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Factory;
use Illuminate\Support\Facades\File;
use Modules\ChatGPT\Support\ModuleInfo;
use Config; use Config;
use Illuminate\Support\Facades\File;
use Illuminate\Support\ServiceProvider;
use Modules\ChatGPT\Support\ModuleInfo;
class ChatGPTServiceProvider extends ServiceProvider class ChatGPTServiceProvider extends ServiceProvider
{ {
/** /**
* @var string $moduleName * @var string
*/ */
protected string $moduleName = 'ChatGPT'; protected string $moduleName = 'ChatGPT';
/** /**
* @var string $moduleNameLower * @var string
*/ */
protected string $moduleNameLower = 'chatgpt'; protected string $moduleNameLower = 'chatgpt';
/**
/** * Get the services provided by the provider.
* Get the services provided by the provider. *
* * @return array
* @return array */
*/ public function provides(): array
public function provides():array
{ {
return []; return [];
} }
@@ -36,7 +34,7 @@ class ChatGPTServiceProvider extends ServiceProvider
* *
* @return void * @return void
*/ */
public function boot() : void public function boot(): void
{ {
$this->registerTranslations(); $this->registerTranslations();
$this->registerConfig(); $this->registerConfig();
@@ -63,34 +61,33 @@ class ChatGPTServiceProvider extends ServiceProvider
*/ */
protected function registerHelpers(): void protected function registerHelpers(): void
{ {
foreach (glob(__DIR__.'/../Helpers/*.php') as $filename) { foreach (glob(__DIR__ . '/../Helpers/*.php') as $filename) {
require_once $filename; require_once $filename;
} }
} }
/** /**
* Dynamically Register Commands. * Dynamically Register Commands.
* *
* @return void * @return void
*/ */
public function registerCommands(): void public function registerCommands(): void
{ {
if (!$this->app->runningInConsole()) { if (! $this->app->runningInConsole()) {
return; return;
}
$commandPath = __DIR__ . '/../Console';
$commands = collect(File::allFiles($commandPath))
->map(function ($file) {
$class = 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
return $class;
})
->filter(function ($class) {
return class_exists($class);
})
->values()
->all();
$this->commands($commands);
} }
$commandPath = __DIR__ . '/../Console';
$commands = collect(File::allFiles($commandPath))
->map(function ($file) {
return 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension();
})
->filter(function ($class) {
return class_exists($class);
})
->values()
->all();
$this->commands($commands);
}
/** /**
* Register config. * Register config.
@@ -100,10 +97,11 @@ class ChatGPTServiceProvider extends ServiceProvider
protected function registerConfig(): void protected function registerConfig(): void
{ {
$this->publishes([ $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'); ], 'config');
$this->mergeConfigFrom( $this->mergeConfigFrom(
module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower() module_path(ModuleInfo::name(), 'Config/config.php'),
ModuleInfo::nameLower(),
); );
} }
@@ -117,9 +115,9 @@ class ChatGPTServiceProvider extends ServiceProvider
$viewPath = resource_path('views/modules/' . ModuleInfo::nameLower()); $viewPath = resource_path('views/modules/' . ModuleInfo::nameLower());
$sourcePath = module_path(ModuleInfo::name(), 'Resources/views'); $sourcePath = module_path(ModuleInfo::name(), 'Resources/views');
$this->publishes([ $this->publishes([
$sourcePath => $viewPath $sourcePath => $viewPath,
], ['views', ModuleInfo::nameLower() . '-module-views']); ], ['views', ModuleInfo::nameLower() . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower()); $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower());
} }
/** /**
@@ -129,10 +127,10 @@ class ChatGPTServiceProvider extends ServiceProvider
*/ */
public function registerTranslations(): void public function registerTranslations(): void
{ {
$langPath = resource_path('lang/modules/' . ModuleInfo::nameLower()); $langPath = resource_path('lang/modules/' . ModuleInfo::nameLower());
if (is_dir($langPath)) { if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, ModuleInfo::nameLower()); $this->loadTranslationsFrom($langPath, ModuleInfo::nameLower());
} else { } else {
$this->loadTranslationsFrom(module_path(ModuleInfo::name(), 'Resources/lang'), ModuleInfo::nameLower()); $this->loadTranslationsFrom(module_path(ModuleInfo::name(), 'Resources/lang'), ModuleInfo::nameLower());
} }
@@ -140,6 +138,7 @@ class ChatGPTServiceProvider extends ServiceProvider
/** /**
* Regsiter View Paths * Regsiter View Paths
*
* @return array * @return array
*/ */
private function getPublishableViewPaths(): array private function getPublishableViewPaths(): array
@@ -150,6 +149,7 @@ class ChatGPTServiceProvider extends ServiceProvider
$paths[] = $path . '/modules/' . ModuleInfo::nameLower(); $paths[] = $path . '/modules/' . ModuleInfo::nameLower();
} }
} }
return $paths; return $paths;
} }
} }
+1 -13
View File
@@ -2,8 +2,8 @@
namespace Modules\ChatGPT\Providers; namespace Modules\ChatGPT\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider class RouteServiceProvider extends ServiceProvider
{ {
@@ -14,18 +14,6 @@ class RouteServiceProvider extends ServiceProvider
*/ */
protected $moduleNamespace = 'Modules\ChatGPT\Http\Controllers'; protected $moduleNamespace = 'Modules\ChatGPT\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/** /**
* Define the routes for the application. * Define the routes for the application.
* *
View File
View File
View File
View File
View File
+11 -6
View File
@@ -65,7 +65,8 @@
</div> </div>
<label class="inline-flex items-center gap-2 text-sm text-slate-700"> <label class="inline-flex items-center gap-2 text-sm text-slate-700">
<input type="checkbox" name="is_active" value="1" checked class="rounded border-slate-300 text-blue-600 focus:ring-blue-500"> <label for="sonar-input-a09f3b253f" class="visually-hidden">is active</label>
<input type="checkbox" name="is_active" value="1" checked class="rounded border-slate-300 text-blue-600 focus:ring-blue-500" id="sonar-input-a09f3b253f">
Make active key Make active key
</label> </label>
@@ -82,6 +83,7 @@
<div class="mt-5 overflow-x-auto"> <div class="mt-5 overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-sm"> <table class="min-w-full divide-y divide-slate-200 text-sm">
<caption class="sr-only">Data table</caption>
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"> <thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
<tr> <tr>
<th class="px-4 py-3">Name</th> <th class="px-4 py-3">Name</th>
@@ -99,24 +101,27 @@
@csrf @csrf
@method('PUT') @method('PUT')
<input <label for="sonar-input-30f47d1294" class="visually-hidden">name</label>
<input
name="name" name="name"
type="text" type="text"
value="{{ old('name', $setting->name) }}" value="{{ old('name', $setting->name) }}"
class="w-full rounded-lg border-slate-300 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500" class="w-full rounded-lg border-slate-300 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500"
> id="name">
<input <label for="sonar-input-707eb8d2ba" class="visually-hidden">Leave blank to keep existing key</label>
<input
name="api_key" name="api_key"
type="password" type="password"
value="" value=""
autocomplete="new-password" autocomplete="new-password"
placeholder="Leave blank to keep existing key" placeholder="Leave blank to keep existing key"
class="w-full rounded-lg border-slate-300 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500" class="w-full rounded-lg border-slate-300 text-sm shadow-sm focus:border-blue-500 focus:ring-blue-500"
> id="sonar-input-707eb8d2ba">
<label class="inline-flex items-center gap-2 text-xs text-slate-600"> <label class="inline-flex items-center gap-2 text-xs text-slate-600">
<input <label for="sonar-input-da782f0360" class="visually-hidden">is active</label>
<input
type="checkbox" type="checkbox"
name="is_active" name="is_active"
value="1" value="1"
View File
Regular → Executable
View File
Regular → Executable
+1 -1
View File
@@ -15,4 +15,4 @@ use Illuminate\Http\Request;
Route::middleware('auth:api')->get('/chatgpt', function (Request $request) { Route::middleware('auth:api')->get('/chatgpt', function (Request $request) {
return $request->user(); return $request->user();
}); });
Regular → Executable
View File
+5 -7
View File
@@ -4,15 +4,13 @@ namespace Modules\ChatGPT\Services;
use Illuminate\Http\Client\PendingRequest; use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use RuntimeException; use Modules\ChatGPT\Exceptions\OpenAiFileException;
class OpenAiFileService class OpenAiFileService
{ {
private const BASE_URL = 'https://api.openai.com/v1'; private const BASE_URL = 'https://api.openai.com/v1';
public function __construct(private readonly string $apiKey) public function __construct(private readonly string $apiKey) {}
{
}
/** /**
* List all OpenAI API files visible to the configured API key. * List all OpenAI API files visible to the configured API key.
@@ -35,7 +33,7 @@ class OpenAiFileService
->get(self::BASE_URL . '/files', $query); ->get(self::BASE_URL . '/files', $query);
if ($response->failed()) { if ($response->failed()) {
throw new RuntimeException('OpenAI file list request failed: ' . $response->body()); throw new OpenAiFileException('OpenAI file list request failed: ' . $response->body());
} }
$payload = $response->json(); $payload = $response->json();
@@ -65,7 +63,7 @@ class OpenAiFileService
->delete(self::BASE_URL . '/files/' . rawurlencode($fileId)); ->delete(self::BASE_URL . '/files/' . rawurlencode($fileId));
if ($response->failed()) { if ($response->failed()) {
throw new RuntimeException("OpenAI file delete request failed for {$fileId}: " . $response->body()); throw new OpenAiFileException("OpenAI file delete request failed for {$fileId}: " . $response->body());
} }
return $response->json() ?? []; return $response->json() ?? [];
@@ -92,7 +90,7 @@ class OpenAiFileService
try { try {
$this->deleteFile($fileId); $this->deleteFile($fileId);
$deleted++; $deleted++;
} catch (RuntimeException $exception) { } catch (OpenAiFileException $exception) {
$failures[] = [ $failures[] = [
'file_id' => $fileId, 'file_id' => $fileId,
'error' => $exception->getMessage(), 'error' => $exception->getMessage(),
Regular → Executable
View File
+3 -1
View File
@@ -4,10 +4,11 @@ namespace Modules\ChatGPT\Support;
class ModuleInfo class ModuleInfo
{ {
protected static string $moduleName = 'ChatGPT'; protected static string $moduleName = 'ChatGPT';
/** /**
* defines the Module Name * defines the Module Name
*
* @return string * @return string
*/ */
public static function name(): string public static function name(): string
@@ -17,6 +18,7 @@ class ModuleInfo
/** /**
* define the lowercase version * define the lowercase version
*
* @return string * @return string
*/ */
public static function nameLower(): string public static function nameLower(): string
View File

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