Add admin views for quick replies, settings, and ticket details
- Created `quick-replies.blade.php` for managing quick replies. - Added `settings.blade.php` for admin settings management. - Implemented `ticket-show.blade.php` to display ticket details. - Introduced `timeline-card.blade.php` component for displaying timeline information. Enhance quick reply management functionality - Developed `quick-reply-manager.blade.php` for creating and editing quick replies. - Integrated Livewire for dynamic interaction and validation. Implement settings page for AI configuration - Created `settings-page.blade.php` for managing AI settings, including prompts and provider instances. - Added functionality for managing models and embeddings. Add ticket show functionality with real-time updates - Implemented ticket details view with processing status and tool call logs. - Added support for displaying article suggestions and error messages. Create unit tests for AI classifier and domain info tool - Added `AIClassifierServiceTest.php` to validate AI classifier functionality. - Implemented `DomainInfoToolTest.php` for domain parameter validation. - Created `OxxaClientTest.php` to test API interactions and password hashing.
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
DB::statement('CREATE EXTENSION IF NOT EXISTS vector');
|
||||
@@ -13,4 +14,4 @@ return new class extends Migration {
|
||||
{
|
||||
DB::statement('DROP EXTENSION IF EXISTS vector');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,8 @@ use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('articles', function (Blueprint $table) {
|
||||
@@ -24,4 +25,4 @@ return new class extends Migration {
|
||||
{
|
||||
Schema::dropIfExists('articles');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,8 @@ use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tickets', function (Blueprint $table) {
|
||||
@@ -23,4 +24,4 @@ return new class extends Migration {
|
||||
{
|
||||
Schema::dropIfExists('tickets');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('embedding_cache', function (Blueprint $table) {
|
||||
@@ -20,4 +21,4 @@ return new class extends Migration {
|
||||
{
|
||||
Schema::dropIfExists('embedding_cache');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('ai_decisions', function (Blueprint $table) {
|
||||
@@ -22,4 +23,4 @@ return new class extends Migration {
|
||||
{
|
||||
Schema::dropIfExists('ai_decisions');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('feedback', function (Blueprint $table) {
|
||||
@@ -21,4 +22,4 @@ return new class extends Migration {
|
||||
{
|
||||
Schema::dropIfExists('feedback');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('categories', function (Blueprint $table) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->string('status')->default('queued')->after('embedding');
|
||||
$table->foreignId('best_article_id')->nullable()->after('status')->constrained('articles')->nullOnDelete();
|
||||
$table->float('confidence')->nullable()->after('best_article_id');
|
||||
$table->text('explanation')->nullable()->after('confidence');
|
||||
$table->json('result_payload')->nullable()->after('explanation');
|
||||
$table->text('error_message')->nullable()->after('result_payload');
|
||||
$table->timestamp('processed_at')->nullable()->after('error_message');
|
||||
$table->index('status');
|
||||
});
|
||||
|
||||
Schema::create('ticket_processing_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('step', 100);
|
||||
$table->string('status', 30)->default('info');
|
||||
$table->text('message')->nullable();
|
||||
$table->json('context')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['ticket_id', 'created_at']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ticket_processing_logs');
|
||||
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropConstrainedForeignId('best_article_id');
|
||||
$table->dropColumn(['status', 'confidence', 'explanation', 'result_payload', 'error_message', 'processed_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->text('normalized_message')->nullable()->after('message');
|
||||
$table->json('redaction_report')->nullable()->after('normalized_message');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropColumn(['normalized_message', 'redaction_report']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->text('support_reply')->nullable()->after('explanation');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropColumn('support_reply');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique();
|
||||
$table->longText('value')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('settings');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('articles', function (Blueprint $table) {
|
||||
$table->string('status')->default('published')->after('content');
|
||||
$table->boolean('is_ai_draft')->default(false)->after('status');
|
||||
$table->foreignId('source_ticket_id')->nullable()->after('subcategory_id')->constrained('tickets')->nullOnDelete();
|
||||
});
|
||||
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->boolean('needs_article_draft')->default(false)->after('support_reply');
|
||||
$table->foreignId('draft_article_id')->nullable()->after('needs_article_draft')->constrained('articles')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropConstrainedForeignId('draft_article_id');
|
||||
$table->dropColumn('needs_article_draft');
|
||||
});
|
||||
|
||||
Schema::table('articles', function (Blueprint $table) {
|
||||
$table->dropConstrainedForeignId('source_ticket_id');
|
||||
$table->dropColumn(['status', 'is_ai_draft']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('article_chunks', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('article_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedInteger('chunk_index');
|
||||
$table->text('content');
|
||||
$table->timestamps();
|
||||
$table->unique(['article_id', 'chunk_index']);
|
||||
});
|
||||
|
||||
$dimension = (int) config('services.embedding.dimension', 768);
|
||||
DB::statement("ALTER TABLE article_chunks ADD COLUMN embedding vector({$dimension})");
|
||||
DB::statement('CREATE INDEX article_chunks_embedding_cosine_idx ON article_chunks USING ivfflat (embedding vector_cosine_ops)');
|
||||
DB::statement('CREATE INDEX article_chunks_article_id_idx ON article_chunks(article_id)');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('article_chunks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('embedding_cache', function (Blueprint $table) {
|
||||
$table->string('provider_instance_id')->nullable()->after('id');
|
||||
$table->string('embedding_model')->nullable()->after('provider_instance_id');
|
||||
$table->dropUnique(['text_hash']);
|
||||
$table->unique(['provider_instance_id', 'embedding_model', 'text_hash'], 'embedding_cache_model_text_unique');
|
||||
});
|
||||
|
||||
Schema::table('article_chunks', function (Blueprint $table) {
|
||||
$table->string('embedding_provider_instance_id')->nullable()->after('embedding');
|
||||
$table->string('embedding_model')->nullable()->after('embedding_provider_instance_id');
|
||||
$table->timestamp('embedded_at')->nullable()->after('embedding_model');
|
||||
$table->index(['embedding_provider_instance_id', 'embedding_model'], 'article_chunks_embedding_model_idx');
|
||||
});
|
||||
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->string('embedding_provider_instance_id')->nullable()->after('embedding');
|
||||
$table->string('embedding_model')->nullable()->after('embedding_provider_instance_id');
|
||||
$table->timestamp('embedded_at')->nullable()->after('embedding_model');
|
||||
});
|
||||
|
||||
DB::table('embedding_cache')->truncate();
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropColumn(['embedding_provider_instance_id', 'embedding_model', 'embedded_at']);
|
||||
});
|
||||
|
||||
Schema::table('article_chunks', function (Blueprint $table) {
|
||||
$table->dropIndex('article_chunks_embedding_model_idx');
|
||||
$table->dropColumn(['embedding_provider_instance_id', 'embedding_model', 'embedded_at']);
|
||||
});
|
||||
|
||||
Schema::table('embedding_cache', function (Blueprint $table) {
|
||||
$table->dropUnique('embedding_cache_model_text_unique');
|
||||
$table->unique('text_hash');
|
||||
$table->dropColumn(['provider_instance_id', 'embedding_model']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('articles', function (Blueprint $table) {
|
||||
$table->text('note')->nullable()->after('content');
|
||||
$table->json('allowed_actions')->nullable()->after('note');
|
||||
});
|
||||
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->text('api_credentials')->nullable()->after('result_payload');
|
||||
});
|
||||
|
||||
Schema::create('ticket_tool_calls', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('ticket_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('article_id')->nullable()->constrained('articles')->nullOnDelete();
|
||||
$table->string('action', 100);
|
||||
$table->string('status', 30)->default('pending');
|
||||
$table->json('parameters')->nullable();
|
||||
$table->json('response')->nullable();
|
||||
$table->text('error')->nullable();
|
||||
$table->timestamp('executed_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['ticket_id', 'created_at']);
|
||||
$table->index(['action', 'status']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ticket_tool_calls');
|
||||
|
||||
Schema::table('tickets', function (Blueprint $table) {
|
||||
$table->dropColumn('api_credentials');
|
||||
});
|
||||
|
||||
Schema::table('articles', function (Blueprint $table) {
|
||||
$table->dropColumn(['note', 'allowed_actions']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('quick_replies', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->text('content');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('is_active');
|
||||
});
|
||||
|
||||
Schema::create('article_quick_reply', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('article_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('quick_reply_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['article_id', 'quick_reply_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('article_quick_reply');
|
||||
Schema::dropIfExists('quick_replies');
|
||||
}
|
||||
};
|
||||
@@ -21,4 +21,4 @@ class ArticleSeeder extends Seeder
|
||||
Article::query()->create($article);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ class DatabaseSeeder extends Seeder
|
||||
ArticleSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user