1688 PO Automation Project

This commit is contained in:
Dillon Ngo
2026-01-04 21:41:42 +08:00
parent fc6863a4de
commit 91ae61185f
38 changed files with 1282 additions and 169 deletions
@@ -6,16 +6,12 @@ namespace App\Classes\General\Eloquent\Filters;
use App\Classes\ValueObjects\Constants\KVPKey;
use Illuminate\Database\Eloquent\Builder;
class ChatgptPrompt implements Filter
class Prompt implements Filter
{
public static function apply(Builder $builder, $value)
{
return $builder->where(
'key',
'LIKE',
KVPKey::CHATGPT_PROMPT_PREFIX . '%'
);
return $builder->where('key', 'LIKE', KVPKey::CHATGPT_PROMPT_PREFIX . '%')
->orWhere('key', 'LIKE', KVPKey::CLAUDE_PROMPT_PREFIX . '%');
}
}
@@ -42,7 +42,7 @@ class ETLPurchaseOrderTransactionV2CommandJob implements ShouldQueue
Log::info(Carbon::now() . ': Start job - ETL Purchase Order Transaction.');
$start = new Carbon();
(App()->make(ETLPurchaseOrderTransactionProcessor::class))->execute($this->products, $this->productsMetadata, $this->bookingId);
// (App()->make(ETLPurchaseOrderTransactionProcessor::class))->execute($this->products, $this->productsMetadata, $this->bookingId);
$end = new Carbon();
$elapsedTime = $start->diff($end)->format('%H:%I:%S');
@@ -0,0 +1,62 @@
<?php
namespace App\Classes\Modules\Anthropic\Services;
use Illuminate\Support\Facades\Http;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Exceptions\ConnectionErrorException;
use Illuminate\Support\Facades\Log;
class CreatesClaudeResponse
{
/**
* @param string $userPrompt
* @param string $model
* @return null|object
* @throws MalformedRequestException
*/
public function execute(string $userPrompt, string $model = "") {
try {
// Default to the latest recommended Claude model
$modelId = $model ?? config('anthropic.default_model', 'claude-sonnet-4-5-20250929');
$data = [
'model' => $modelId,
'messages' => [
[
'role' => 'user',
'content' => $userPrompt
]
]
];
$response = Http::withHeaders([
'x-api-key' => config('anthropic.api_key'),
'anthropic-version' => config('anthropic.api_version', '2023-06-01'),
])->post(config('anthropic.base_url') . '/v1/messages', $data);
if ($response->successful()) {
return (object) $response->json();
} else {
Log::info('CreatesClaudeResponse error: ' . $response->body());
return null;
}
}
catch (\Illuminate\Http\Client\ConnectionException $exception) {
Log::info('ConnectionException' . json_encode($exception));
throw new ConnectionErrorException(
'Failed to connect to Claude API',
$exception->getMessage(),
$userPrompt,
$exception->getTraceAsString()
);
}
catch (\Exception $exception) {
Log::info('Exception' . json_encode($exception));
throw new MalformedRequestException(
'Unable to get correct response from Claude API: ' . $exception->getMessage()
);
}
}
}
@@ -0,0 +1,67 @@
<?php
namespace App\Classes\Modules\Anthropic\Services;
use Illuminate\Support\Facades\Http;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Exceptions\ConnectionErrorException;
class CreatesClaudeResponseWithFiles
{
public function execute(string $userPrompt, array $fileIds)
{
try {
$content = [];
// User prompt
$content[] = [
'type' => 'text',
'text' => $userPrompt,
];
// Attach files
foreach ($fileIds as $fileId) {
$content[] = [
'type' => 'document',
'source' => [
'type' => 'file',
'file_id' => $fileId,
],
];
}
$response = Http::withHeaders([
'x-api-key' => config('anthropic.api_key'),
'anthropic-version' => config('anthropic.api_version', '2023-06-01'),
'anthropic-beta' => 'files-api-2025-04-14',
'Content-Type' => 'application/json',
])->post(config('anthropic.base_url') . '/v1/messages', [
'model' => config('anthropic.default_model', 'claude-sonnet-4-5-20250929'),
'max_tokens' => 1024,
'messages' => [
[
'role' => 'user',
'content' => $content,
],
],
]);
return $response->json();
}
catch (\Illuminate\Http\Client\ConnectionException $exception) {
$error = 'Failed to connect to Claude API';
throw new ConnectionErrorException(
$error,
$exception->getMessage(),
$userPrompt,
$exception->getTraceAsString()
);
}
catch (\Exception $exception) {
throw new MalformedRequestException(
'Unable to get correct response from Claude API: ' . $exception->getMessage()
);
}
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\Classes\Modules\Anthropic\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Exceptions\ConnectionErrorException;
class UploadsClaudeFiles
{
public function execute(array $paths): array
{
try {
$ids = [];
foreach ($paths as $path) {
$response = Http::withHeaders([
'x-api-key' => config('anthropic.api_key'),
'anthropic-version' => '2023-06-01',
'anthropic-beta' => 'files-api-2025-04-14',
])->attach(
'file',
fopen($path, 'r'),
basename($path)
)->post(config('anthropic.base_url') . '/v1/files');
$data = $response->json();
if ($response->successful() && isset($data['id'])) {
$ids[] = $data['id'];
} else {
Log::error('Claude file upload failed', [
'path' => $path,
'response' => $data,
]);
}
}
return $ids;
}
catch (\Illuminate\Http\Client\ConnectionException $exception) {
$error = 'Failed to connect to Claude API';
throw new ConnectionErrorException(
$error,
$exception->getMessage(),
$exception->getTraceAsString()
);
}
catch (\Exception $exception) {
throw new MalformedRequestException(
'Unable to get correct response from Claude API: ' . $exception->getMessage()
);
}
}
}
@@ -0,0 +1,68 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject;
use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair;
use App\Classes\Modules\KeyValuePairs\DataTransferObjects\CreateClaudePromptDTO;
use App\Classes\Modules\KeyValuePairs\Standards\Rules\CanCreateClaudePrompt;
use App\Classes\ValueObjects\Constants\KVPKey;
use App\Http\Resources\ClaudePromptResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CreateClaudePromptKVPLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => 'Created Claude Prompt',
'message' => 'You have successfully created a new Claude Prompt'
];
}
/** @var CanCreateClaudePrompt */
private $canCreateClaudePrompt;
/** @var CreatesKeyValuePair */
private $createsKeyValuePair;
/**
* CreateClaudePromptKVPLogic constructor.
* @param CanCreateClaudePrompt $canCreateClaudePrompt
* @param CreatesKeyValuePair $createsKeyValuePair
*/
public function __construct(
CanCreateClaudePrompt $canCreateClaudePrompt,
CreatesKeyValuePair $createsKeyValuePair
) {
$this->canCreateClaudePrompt = $canCreateClaudePrompt;
$this->createsKeyValuePair = $createsKeyValuePair;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request): JsonResponse
{
$dto = new CreateClaudePromptDTO($request->all());
$this->canCreateClaudePrompt->passes($dto);
$key = KVPKey::CLAUDE_PROMPT_PREFIX . strtoupper(str_replace(' ', '_', $dto->getTitle()));
$keyValuePairObject = new KeyValuePairObject($key, $dto->getPrompt());
$kvp = $this->createsKeyValuePair->execute(null, $keyValuePairObject);
return $this->resourceResponse(new ClaudePromptResource($kvp));
}
}
@@ -0,0 +1,69 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounts\Services\DeletesKeyValuePair;
use App\Classes\Modules\Accounts\Services\FetchesKeyValuePair;
use App\Classes\Modules\KeyValuePairs\Standards\Rules\CanDeleteClaudePrompt;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DeleteClaudePromptKVPLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => 'Delete Claude Prompt',
'message' => 'You have successfully deleted the Claude Prompt'
];
}
/** @var CanDeleteClaudePrompt */
private $canDeleteClaudePrompt;
/** @var FetchesKeyValuePair */
private $fetchesKeyValuePair;
/** @var DeletesKeyValuePair */
private $deletesKeyValuePair;
/**
* DeleteClaudePromptKVPLogic constructor.
* @param CanDeleteClaudePrompt $canDeleteClaudePrompt
* @param FetchesKeyValuePair $fetchesKeyValuePair
* @param DeletesKeyValuePair $deletesKeyValuePair
*/
public function __construct(
CanDeleteClaudePrompt $canDeleteClaudePrompt,
FetchesKeyValuePair $fetchesKeyValuePair,
DeletesKeyValuePair $deletesKeyValuePair
) {
$this->canDeleteClaudePrompt = $canDeleteClaudePrompt;
$this->fetchesKeyValuePair = $fetchesKeyValuePair;
$this->deletesKeyValuePair = $deletesKeyValuePair;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request): JsonResponse
{
$this->canDeleteClaudePrompt->passes();
$kvp = $this->fetchesKeyValuePair->execute(['id' => $request->route('id')]);
$this->deletesKeyValuePair->execute($kvp);
return $this->response([]);
}
}
@@ -5,12 +5,12 @@ namespace App\Classes\Modules\KeyValuePairs\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\KeyValuePairs\Services\ListsKeyValuePairs;
use App\Classes\Modules\KeyValuePairs\Standards\Rules\CanListChatGPTPrompts;
use App\Classes\Modules\KeyValuePairs\Standards\Rules\CanListAiPrompts;
use App\Http\Resources\ChatGPTPromptResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListChatGPTPromptsKVPLogic extends AbstractControllerLogic
class ListAiPromptsKVPLogic extends AbstractControllerLogic
{
/**
@@ -19,25 +19,25 @@ class ListChatGPTPromptsKVPLogic extends AbstractControllerLogic
protected function notification(): array
{
return [
'title' => 'Retrieve ChatGPT Prompts',
'message' => 'You have successfully retrieved a list of ChatGPT Prompts'
'title' => 'Retrieve Prompts',
'message' => 'You have successfully retrieved a list of Prompts'
];
}
/** @var CanListChatGPTPrompts */
private $canListChatGPTPrompts;
/** @var CanListAiPrompts */
private $canListAiPrompts;
/** @var ListsKeyValuePairs */
private $listsKeyValuePairs;
/**
* ListChatGPTPromptsKVPLogic constructor.
* @param CanListChatGPTPrompts $canListChatGPTPrompts
* ListAiPromptsKVPLogic constructor.
* @param CanListAiPrompts $canListAiPrompts
* @param ListsKeyValuePairs $listsKeyValuePairs
*/
public function __construct(CanListChatGPTPrompts $canListChatGPTPrompts, ListsKeyValuePairs $listsKeyValuePairs)
public function __construct(CanListAiPrompts $canListAiPrompts, ListsKeyValuePairs $listsKeyValuePairs)
{
$this->canListChatGPTPrompts = $canListChatGPTPrompts;
$this->canListAiPrompts = $canListAiPrompts;
$this->listsKeyValuePairs = $listsKeyValuePairs;
}
@@ -51,9 +51,9 @@ class ListChatGPTPromptsKVPLogic extends AbstractControllerLogic
*/
public function logic(Request $request): JsonResponse
{
$this->canListChatGPTPrompts->passes();
$this->canListAiPrompts->passes();
$prompts = $this->listsKeyValuePairs->execute(array_merge($this->listsKeyValuePairs->deserializeFilters($request->input('filters')), ['chatgpt_prompt' => false]));
$prompts = $this->listsKeyValuePairs->execute(array_merge($this->listsKeyValuePairs->deserializeFilters($request->input('filters')), ['prompt' => false]));
return $this->collectionResponse(ChatGPTPromptResource::collection($prompts));
}
@@ -0,0 +1,69 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject;
use App\Classes\Modules\KeyValuePairs\DataTransferObjects\UpdateClaudePromptDTO;
use App\Classes\Modules\KeyValuePairs\Standards\Rules\CanUpdateClaudePrompt;
use App\Classes\Modules\Accounts\Services\FetchesKeyValuePair;
use App\Classes\Modules\Accounts\Services\UpdatesKeyValuePair;
use App\Http\Resources\ClaudePromptResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UpdateClaudePromptKVPLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Update Claude Prompt',
'message' => 'You have successfully updated the Claude prompt'
];
}
/** @var CanUpdateClaudePrompt */
private $canUpdateClaudePrompt;
/** @var UpdatesKeyValuePair */
private $updatesKeyValuePair;
/** @var FetchesKeyValuePair */
private $fetchesKeyValuePair;
/**
* UpdateClaudePromptKVPLogic constructor.
* @param CanUpdateClaudePrompt $canUpdateClaudePrompt
* @param UpdatesKeyValuePair $updatesKeyValuePair
* @param FetchesKeyValuePair $fetchesKeyValuePair
*/
public function __construct(CanUpdateClaudePrompt $canUpdateClaudePrompt, UpdatesKeyValuePair $updatesKeyValuePair, FetchesKeyValuePair $fetchesKeyValuePair)
{
$this->canUpdateClaudePrompt = $canUpdateClaudePrompt;
$this->updatesKeyValuePair = $updatesKeyValuePair;
$this->fetchesKeyValuePair = $fetchesKeyValuePair;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request) : JsonResponse
{
$dto = new UpdateClaudePromptDTO($request->all());
$this->canUpdateClaudePrompt->passes($dto);
$kvp = $this->fetchesKeyValuePair->execute(['id' => $dto->getId()]);
$keyValuePairObject = new KeyValuePairObject($kvp->key, $dto->getPrompt());
$this->updatesKeyValuePair->execute($kvp, $keyValuePairObject);
return $this->resourceResponse(new ClaudePromptResource($kvp));
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class CreateClaudePromptDTO implements DataTransferObject
{
public string $title;
public string $prompt;
public function __construct(array $data)
{
$this->title = (string) ($data['key'] ?? '');
$this->prompt = (string) ($data['value'] ?? '');
}
public function toArray(): array
{
return [
'title' => $this->title,
'prompt' => $this->prompt,
];
}
public function getTitle(): string
{
return $this->title;
}
public function getPrompt(): string
{
return $this->prompt;
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class UpdateClaudePromptDTO implements DataTransferObject
{
public int $id;
public string $title;
public string $prompt;
public function __construct(array $data)
{
$this->id = (int) ($data['id'] ?? 0);
$this->title = (string) ($data['key'] ?? '');
$this->prompt = (string) ($data['value'] ?? '');
}
public function toArray(): array
{
return [
'id' => $this->id,
'title' => $this->title,
'prompt' => $this->prompt,
];
}
public function getId(): int
{
return $this->id;
}
public function getTitle(): string
{
return $this->title;
}
public function getPrompt(): string
{
return $this->prompt;
}
}
@@ -0,0 +1,56 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\KeyValuePairs\Standards\Validators\CreateClaudePromptValidation;
use App\Classes\Modules\KeyValuePairs\DataTransferObjects\CreateClaudePromptDTO;
use App\Classes\ValueObjects\Constants\RoleTypes;
use Illuminate\Support\Facades\Auth;
class CanCreateClaudePrompt extends AbstractRule
{
/** @var CreateClaudePromptValidation */
private $validation;
/**
* CanCreateClaudePrompt constructor.
* @param CreateClaudePromptValidation $validation
*/
public function __construct(CreateClaudePromptValidation $validation)
{
$this->validation = $validation;
}
/**
* @return bool
*/
protected function authorized($object): bool
{
if (in_array(Auth::user()->type, RoleTypes::ADMIN_ROLES)) {
return true;
}
return false;
}
/**
* @param CreateClaudePromptDTO $object
* @return bool
* @throws \App\Classes\Exceptions\RequestValidationException
*/
protected function validators($object): bool
{
return $this->validation->validate($object);
}
/**
* @param CreateChatGPTPromptDTO $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,43 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\ValueObjects\Constants\RoleTypes;
use Illuminate\Support\Facades\Auth;
class CanDeleteClaudePrompt extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
if (in_array(Auth::user()->type, RoleTypes::ADMIN_ROLES)) {
return true;
}
return false;
}
/**
* @param $object
* @return bool
*/
protected function validators($object): bool
{
return true;
}
/**
* @param $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -7,7 +7,7 @@ use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\ValueObjects\Constants\RoleTypes;
use Illuminate\Support\Facades\Auth;
class CanListChatGPTPrompts extends AbstractRule
class CanListAiPrompts extends AbstractRule
{
/**
@@ -0,0 +1,58 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\KeyValuePairs\DataTransferObjects\UpdateClaudePromptDTO;
use App\Classes\Modules\KeyValuePairs\Standards\Validators\UpdateClaudePromptValidation;
use App\Classes\ValueObjects\Constants\RoleTypes;
use Illuminate\Support\Facades\Auth;
class CanUpdateClaudePrompt extends AbstractRule
{
/** @var UpdateClaudePromptValidation */
private $validation;
/**
* CanUpdateClaudePrompt constructor.
* @param UpdateClaudePromptValidation $validation
*/
public function __construct(UpdateClaudePromptValidation $validation)
{
$this->validation = $validation;
}
/**
* @return bool
*/
protected function authorized($object): bool
{
if (in_array(Auth::user()->type, RoleTypes::ADMIN_ROLES)) {
return true;
}
return false;
}
/**
* @param UpdateClaudePromptDTO $object
* @return bool
* @throws \App\Classes\Exceptions\RequestValidationException
*/
protected function validators($object): bool
{
return $this->validation->validate($object);
}
/**
* @param UpdateClaudePromptDTO $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,41 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\Standards\Validators;
use App\Classes\General\Abstracts\AbstractValidation;
use App\Classes\Modules\KeyValuePairs\DataTransferObjects\CreateClaudePromptDTO;
class CreateClaudePromptValidation extends AbstractValidation
{
/**
* @param CreateClaudePromptDTO $object
* @return array
*/
protected function data($object): array
{
return [
'title' => $object->getTitle(),
'prompt' => $object->getPrompt(),
];
}
/**
* @return array
*/
protected function rules(): array
{
return [
'title' => 'required',
'prompt' => 'required',
];
}
/**
* @return array
*/
protected function messages(): array
{
return [];
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Classes\Modules\KeyValuePairs\Standards\Validators;
use App\Classes\General\Abstracts\AbstractValidation;
use App\Classes\Modules\KeyValuePairs\DataTransferObjects\UpdateClaudePromptDTO;
class UpdateClaudePromptValidation extends AbstractValidation
{
/**
* @param UpdateClaudePromptDTO $object
* @return array
*/
protected function data($object): array
{
return [
'title' => $object->getTitle(),
'prompt' => $object->getPrompt(),
];
}
/**
* @return array
*/
protected function rules(): array
{
return [
'prompt' => 'required',
];
}
/**
* @return array
*/
protected function messages(): array
{
return [];
}
}
@@ -3,11 +3,9 @@
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\Exceptions\InternalServerErrorException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\Processors\ETLPDFPurchaseOrderTransactionProcessor;
use App\Classes\Modules\Transactions\Processors\ETLPDFPurchaseOrderClaudeProcessor;
use App\Classes\Modules\Transactions\Standards\Rules\CanETLPurchaseOrder;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@@ -29,18 +27,18 @@ class ETLPurchaseOrderTransactionLogic extends AbstractControllerLogic
/** @var CanETLPurchaseOrder */
private $canETLPurchaseOrder;
/** @var ETLPDFPurchaseOrderTransactionProcessor */
private $etlPDFPurchaseOrderTransactionProcessor;
/** @var ETLPDFPurchaseOrderClaudeProcessor */
private $eTLPDFPurchaseOrderClaudeProcessor;
/**
* ETLPurchaseOrderTransactionLogic constructor.
* @param CanETLPurchaseOrder $canETLPurchaseOrder
* @param ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor
* @param ETLPDFPurchaseOrderClaudeProcessor $eTLPDFPurchaseOrderClaudeProcessor
*/
public function __construct(CanETLPurchaseOrder $canETLPurchaseOrder, ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor)
public function __construct(CanETLPurchaseOrder $canETLPurchaseOrder, ETLPDFPurchaseOrderClaudeProcessor $eTLPDFPurchaseOrderClaudeProcessor)
{
$this->canETLPurchaseOrder = $canETLPurchaseOrder;
$this->etlPDFPurchaseOrderTransactionProcessor = $etlPDFPurchaseOrderTransactionProcessor;
$this->eTLPDFPurchaseOrderClaudeProcessor = $eTLPDFPurchaseOrderClaudeProcessor;
}
/**
@@ -62,11 +60,12 @@ class ETLPurchaseOrderTransactionLogic extends AbstractControllerLogic
// try{
// $products = $this->etlPDFPurchaseOrderTransactionProcessor->execute($products, $productsMetadata);
// } catch (\Exception $exception) {
// Log::info('Exception: ' .json_encode($exception));
// throw new InternalServerErrorException('Something went wrong!');
// }
$files = $request->input('files');
$result = $this->etlPDFPurchaseOrderTransactionProcessor->execute($files);
$result = $this->eTLPDFPurchaseOrderClaudeProcessor->execute($files);
return $this->response($result);
}
}
@@ -4,12 +4,11 @@ namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Jobs\Commands\V2\ETLPurchaseOrderTransactionV2CommandJob;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
use App\Classes\Modules\Transactions\Processors\ETLPDFPurchaseOrderTransactionProcessor;
use App\Classes\Modules\Transactions\Processors\ETLPDFPurchaseOrderClaudeProcessor;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Transactions\Standards\Rules\CanETLPurchaseOrder;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
@@ -47,8 +46,8 @@ class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
/** @var CreatePurchaseOrderTransactionProcessor */
private $createPurchaseOrderTransactionProcessor;
/** @var ETLPDFPurchaseOrderTransactionProcessor */
private $etlPDFPurchaseOrderTransactionProcessor;
/** @var ETLPDFPurchaseOrderClaudeProcessor */
private $eTLPDFPurchaseOrderClaudeProcessor;
/** @var CanETLPurchaseOrder */
private $canETLPurchaseOrder;
@@ -58,15 +57,15 @@ class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
* @param FetchesBooking $fetchesBooking
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
* @param ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor
* @param ETLPDFPurchaseOrderClaudeProcessor $eTLPDFPurchaseOrderClaudeProcessor
* @param CanETLPurchaseOrder $canETLPurchaseOrder
*/
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, ETLPDFPurchaseOrderTransactionProcessor $etlPDFPurchaseOrderTransactionProcessor, CanETLPurchaseOrder $canETLPurchaseOrder)
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, ETLPDFPurchaseOrderClaudeProcessor $eTLPDFPurchaseOrderClaudeProcessor, CanETLPurchaseOrder $canETLPurchaseOrder)
{
$this->fetchesBooking = $fetchesBooking;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
$this->etlPDFPurchaseOrderTransactionProcessor = $etlPDFPurchaseOrderTransactionProcessor;
$this->eTLPDFPurchaseOrderClaudeProcessor = $eTLPDFPurchaseOrderClaudeProcessor;
$this->canETLPurchaseOrder = $canETLPurchaseOrder;
}
@@ -86,7 +85,7 @@ class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
if(empty($products)){
$files = $request->input('files');
$products = $this->etlPDFPurchaseOrderTransactionProcessor->execute($files);
$products = $this->eTLPDFPurchaseOrderClaudeProcessor->execute($files);
}
}
else {
@@ -2,6 +2,7 @@
namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Modules\OpenAI\Services\UploadsOpenAIFiles;
use App\Classes\Modules\OpenAI\Services\CreatesChatGPTResponseWithFiles;
use App\Classes\Exceptions\ResourceNotFoundException;
@@ -19,7 +20,7 @@ use Illuminate\Support\Facades\Log;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
class ETLPDFPurchaseOrderTransactionProcessor
class ETLPDFPurchaseOrderChatGPTProcessor
{
/** @var FetchesBooking */
private $fetchesBooking;
@@ -37,7 +38,7 @@ class ETLPDFPurchaseOrderTransactionProcessor
private $createsChatGPTResponseWithFiles;
/**
* ETLPDFPurchaseOrderTransactionProcessor constructor.
* ETLPDFPurchaseOrderChatGPTProcessor constructor.
* @param FetchesBooking $fetchesBooking
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
@@ -0,0 +1,177 @@
<?php
namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Modules\Anthropic\Services\UploadsClaudeFiles;
use App\Classes\Modules\Anthropic\Services\CreatesClaudeResponseWithFiles;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\ValueObjects\Constants\KVPKey;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\KeyValuePair;
use App\Models\Booking;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\UploadedFile;
class ETLPDFPurchaseOrderClaudeProcessor
{
private FetchesBooking $fetchesBooking;
private GeneratesTransactionBillNumber $generatesTransactionBillNumber;
private CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor;
private UploadsClaudeFiles $uploadsClaudeFiles;
private CreatesClaudeResponseWithFiles $createsClaudeResponseWithFiles;
public function __construct(
FetchesBooking $fetchesBooking,
GeneratesTransactionBillNumber $generatesTransactionBillNumber,
CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor,
UploadsClaudeFiles $uploadsClaudeFiles,
CreatesClaudeResponseWithFiles $createsClaudeResponseWithFiles
) {
$this->fetchesBooking = $fetchesBooking;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
$this->uploadsClaudeFiles = $uploadsClaudeFiles;
$this->createsClaudeResponseWithFiles = $createsClaudeResponseWithFiles;
}
/**
* @param UploadedFile[] $files
* @param int|null $bookingId
* @return array|null
*/
public function execute(array $files, ?int $bookingId = null): ?array
{
$tempPaths = [];
try {
foreach ($files as $index => $fileBase64) {
if (preg_match('/^data:application\/pdf;base64,/', $fileBase64)) {
$fileBase64 = preg_replace('/^data:application\/pdf;base64,/', '', $fileBase64);
}
$fileContent = base64_decode($fileBase64);
if ($fileContent === false) {
Log::warning("Invalid Base64 for file index {$index}");
continue;
}
$tempDir = storage_path('app/documents');
if (!file_exists($tempDir)) {
mkdir($tempDir, 0755, true);
}
$filePath = $tempDir . '/' . uniqid("file_{$index}_") . '.pdf';
file_put_contents($filePath, $fileContent);
$tempPaths[] = $filePath;
}
Log::info('Claude tempPaths', $tempPaths);
// Upload PDFs to Claude
$fileIds = $this->uploadsClaudeFiles->execute($tempPaths);
$kvp = KeyValuePair::where(
'key',
KVPKey::CLAUDE_PROMPT_PREFIX . 'EXTRACT_1'
)->first();
if (!$kvp) {
throw new ResourceNotFoundException('AI Setup incomplete!');
}
$userPrompt = $kvp->value;
$response = $this->createsClaudeResponseWithFiles->execute($userPrompt, $fileIds);
$textContent = $response['content'][0]['text'] ?? '[]';
// Strip ```json fences
if (preg_match('/```json\s*(.*?)\s*```/s', $textContent, $matches)) {
$jsonString = $matches[1];
} else {
$jsonString = $textContent;
}
$data = json_decode($jsonString, true);
if (json_last_error() !== JSON_ERROR_NONE) {
Log::error('Failed to decode Claude JSON response', [
'error' => json_last_error_msg(),
'textContent' => $textContent,
]);
$data = [];
}
// Normalize keys to snake_case
$data = $this->convertKeysToSnakeCase($data);
if ($bookingId) {
/** @var Booking $booking */
$booking = $this->fetchesBooking->execute(['id' => $bookingId]);
$billNumber = $this->generatesTransactionBillNumber->execute('PO-');
$total = collect($data)->sum(function ($product) {
return $product['quantity']
* floatval(str_replace(',', '', $product['unit_price']));
});
$object = new TransactionObject(
$billNumber,
TransactionType::PURCHASE_ORDER,
$booking->company->id,
1,
1,
PaymentMethodType::CASH,
$total,
$total,
$booking->fix_currency_id,
$booking->fix_currency_id,
1,
0,
0,
null,
ApprovalStatus::PENDING_SUBMISSION,
$data
);
$this->createPurchaseOrderTransactionProcessor
->execute($booking, $object);
}
return $data;
} catch (\Exception $e) {
Log::error('Error processing PDF files with Claude', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
throw $e;
} finally {
foreach ($tempPaths as $path) {
if (file_exists($path)) {
unlink($path);
}
}
}
}
private function convertKeysToSnakeCase(array $items): array
{
return array_map(function ($item) {
$newItem = [];
foreach ($item as $key => $value) {
$newKey = strtolower(str_replace(' ', '_', $key));
$newItem[$newKey] = $value;
}
return $newItem;
}, $items);
}
}
@@ -4,6 +4,7 @@ namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Modules\Anthropic\Services\CreatesClaudeResponse;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
@@ -17,6 +18,7 @@ use App\Models\KeyValuePair;
use App\Models\Booking;
use Illuminate\Support\Facades\Log;
//This solution has partial dependency on pdf table extractor on front end (output not ideal)
class ETLPurchaseOrderTransactionProcessor
{
/** @var CreatesChatGPTResponse */
@@ -31,19 +33,24 @@ class ETLPurchaseOrderTransactionProcessor
/** @var CreatePurchaseOrderTransactionProcessor */
private $createPurchaseOrderTransactionProcessor;
/** @var CreatesClaudeResponse */
private $createsClaudeResponse;
/**
* ETLPurchaseOrderTransactionProcessor constructor.
* @param FetchesBooking $fetchesBooking
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
* @param CreatesChatGPTResponse $createsChatGPTResponse
* @param CreatesClaudeResponse $createsClaudeResponse
*/
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, CreatesChatGPTResponse $createsChatGPTResponse)
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor, CreatesChatGPTResponse $createsChatGPTResponse, CreatesClaudeResponse $createsClaudeResponse)
{
$this->fetchesBooking = $fetchesBooking;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
$this->createsChatGPTResponse = $createsChatGPTResponse;
$this->createsClaudeResponse = $createsClaudeResponse;
}
/**
@@ -65,7 +72,7 @@ class ETLPurchaseOrderTransactionProcessor
// Translate descriptions if they contain Chinese characters
$products = collect($products)->map(function ($product) {
if (isset($product['description']) && preg_match("/\p{Han}+/u", $product['description'])) {
$kvp = KeyValuePair::where('key', KVPKey::CHATGPT_PROMPT_PREFIX . "TRANSLATE_1")->first();
$kvp = KeyValuePair::where('key', KVPKey::CLAUDE_PROMPT_PREFIX . "TRANSLATE_1")->first();
$userPrompt = '';
if($kvp){
$templateFromDb = $kvp->value;
@@ -76,7 +83,8 @@ class ETLPurchaseOrderTransactionProcessor
throw new ResourceNotFoundException('AI Setup incomplete!');
}
$result = $this->createsChatGPTResponse->execute($userPrompt);
// $result = $this->createsChatGPTResponse->execute($userPrompt);
$result = $this->createsClaudeResponse->execute($userPrompt);
$content = $result['choices'][0]['message']['content'] ?? null;
// 1. Try raw JSON
@@ -89,7 +97,7 @@ class ETLPurchaseOrderTransactionProcessor
$json = $matches[1];
}
else {
Log::warning('No JSON found in ChatGPT response', ['content' => $content]);
Log::warning('No JSON found in AI service response', ['content' => $content]);
return;
}
@@ -103,7 +111,7 @@ class ETLPurchaseOrderTransactionProcessor
})->toArray();
if (is_array($productsMetadata) && count($productsMetadata) > 0) {
$kvp = KeyValuePair::where('key', KVPKey::CHATGPT_PROMPT_PREFIX . "EXTRACT_1")->first();
$kvp = KeyValuePair::where('key', KVPKey::CLAUDE_PROMPT_PREFIX . "EXTRACT_1")->first();
$prompt = '';
if($kvp){
$templateFromDb = $kvp->value;
@@ -114,7 +122,8 @@ class ETLPurchaseOrderTransactionProcessor
throw new ResourceNotFoundException('AI Setup incomplete!');
}
$result = $this->createsChatGPTResponse->execute($prompt);
// $result = $this->createsChatGPTResponse->execute($prompt);
$result = $this->createsClaudeResponse->execute($prompt);
$content = $result['choices'][0]['message']['content'] ?? null;
$freight = 0;
@@ -29,5 +29,5 @@ class KVPKey
public const AUTOCOUNT_DOCNO_SALES_DEPOSIT_BY_WALLET_REFUND = 'AUTOCOUNT_DOCNO_SALES_DEPOSIT_BY_WALLET_RF';
public const CHATGPT_PROMPT_PREFIX = 'CHATGPT_PROMPT_';
public const CLAUDE_PROMPT_PREFIX = 'CLAUDE_PROMPT_';
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\KeyValuePairs;
use App\Classes\Modules\KeyValuePairs\ControllersLogic\CreateClaudePromptKVPLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CreateClaudePromptKVPController
{
/**
* @param Request $request
* @param CreateClaudePromptKVPLogic $logic
* @return JsonResponse
*/
public function create(Request $request, CreateClaudePromptKVPLogic $logic): JsonResponse
{
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\KeyValuePairs;
use App\Classes\Modules\KeyValuePairs\ControllersLogic\DeleteClaudePromptKVPLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DeleteClaudePromptKVPController
{
/**
* @param Request $request
* @param DeleteClaudePromptKVPLogic $logic
* @return JsonResponse
*/
public function delete(Request $request, DeleteClaudePromptKVPLogic $logic): JsonResponse
{
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\KeyValuePairs;
use App\Classes\Modules\KeyValuePairs\ControllersLogic\ListAiPromptsKVPLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListAiPromptsKVPController
{
/**
* @param Request $request
* @param ListAiPromptsKVPLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListAiPromptsKVPLogic $logic): JsonResponse
{
return $logic->execute($request);
}
}
@@ -1,21 +0,0 @@
<?php
namespace App\Http\Controllers\KeyValuePairs;
use App\Classes\Modules\KeyValuePairs\ControllersLogic\ListChatGPTPromptsKVPLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListChatGPTPromptsKVPController
{
/**
* @param Request $request
* @param ListChatGPTPromptsKVPLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListChatGPTPromptsKVPLogic $logic): JsonResponse
{
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\KeyValuePairs;
use App\Classes\Modules\KeyValuePairs\ControllersLogic\UpdateClaudePromptKVPLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UpdateClaudePromptKVPController
{
/**
* @param Request $request
* @param UpdateClaudePromptKVPLogic $logic
* @return JsonResponse
*/
public function update(Request $request, UpdateClaudePromptKVPLogic $logic): JsonResponse
{
return $logic->execute($request);
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
class ClaudePromptResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'key' => $this->key,
'value' => $this->value,
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'),
];
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
return [
'base_url' => env('ANTHROPIC_BASE_URL', 'https://api.anthropic.com'),
'api_key' => env('ANTHROPIC_API_KEY', ''),
'api_version' => env('ANTHROPIC_API_VERSION', '2023-06-01'),
// 'is_enabled' => env('OPENAI_IS_ENABLED', 'true'),
];
+1 -1
View File
@@ -3,5 +3,5 @@
return [
'base_url' => env('OPENAI_BASE_URL', 'https://api.openai.com'),
'api_key' => env('OPENAI_API_KEY', ''),
'is_enabled' => env('OPENAI_IS_ENABLED', 'true'),
// 'is_enabled' => env('OPENAI_IS_ENABLED', 'true'),
];
@@ -38,7 +38,7 @@
</div>
</div>
</div>
<div class="col-auto parentContainer">
<div class="col-auto parentContainer" v-if="item.key.startsWith('CHATGPT_PROMPT_')">
<div class="row align-items-center">
<template v-if="$store.getters.isAdmin">
<div class="col-auto no-padding">
@@ -47,20 +47,45 @@
</button>
</div>
<div class="col-auto no-padding">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="deleteChatGPTPrompt" v-if="item.key !== 'CHATGPT_PROMPT_TRANSLATE_1' && item.key !== 'CHATGPT_PROMPT_EXTRACT_1'">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="deleteChatGPTPrompt" v-if="item.key !== 'CHATGPT_PROMPT_EXTRACT_1'">
<i class="fa fa-times"></i>
</button>
</div>
</template>
</div>
<modal-form-component :data="data" size="large" section="allChatGPTPromptsSection">
<modal-form-component :data="data" size="large" section="allAIPromptsSection">
<template slot="form" slot-scope="{section}">
<chat-gpt-form-component :data="data" :section="section"></chat-gpt-form-component>
<ai-prompt-form-component :data="data" :section="section"></ai-prompt-form-component>
</template>
</modal-form-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteChatGPTPrompt">
<delete-chat-gpt-form-component :data="item" section="allChatGPTPromptsSection" class="text-center"></delete-chat-gpt-form-component>
<delete-chat-gpt-form-component :data="item" section="allAIPromptsSection" class="text-center"></delete-chat-gpt-form-component>
</modal-component>
</div>
<div class="col-auto parentContainer" v-if="item.key.startsWith('CLAUDE_PROMPT_')">
<div class="row align-items-center">
<template v-if="$store.getters.isAdmin">
<div class="col-auto no-padding">
<button class="btn btn-xs btn-complete b-rad-none m-r-5 requestModal" data-type="editModal">
Edit
</button>
</div>
<div class="col-auto no-padding">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="deleteClaudePrompt" v-if="item.key !== 'CLAUDE_PROMPT_EXTRACT_1'">
<i class="fa fa-times"></i>
</button>
</div>
</template>
</div>
<modal-form-component :data="data" size="large" section="allAIPromptsSection">
<template slot="form" slot-scope="{section}">
<ai-prompt-form-component :data="data" :section="section"></ai-prompt-form-component>
</template>
</modal-form-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteClaudePrompt">
<delete-claude-form-component :data="item" section="allAIPromptsSection" class="text-center"></delete-claude-form-component>
</modal-component>
</div>
</div>
@@ -0,0 +1,143 @@
<template>
<div class="row">
<div class="col">
<loading-component style="height: 50px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
<div class="row" v-show="!$store.getters.isLoading(section)">
<div class="col">
<div class="row m-b-20">
<div class="col">
<h6 class="all-caps m-b-5 bold no-margin">
{{ data ? "Update" : "Create" }}
<span v-if="!data">
{{ selectedModel === "chatgpt" ? "ChatGPT" : "Claude" }}
</span>
Prompt
</h6>
</div>
</div>
<div class="row m-b-5 animate__animated animate__fadeInUpBig animate__fast" v-if="error">
<div class="col">
<small class="bold fs-10 text-danger">{{error}}</small>
</div>
</div>
<div class="row m-b-10" v-if="!data">
<div class="col">
<label>AI Model</label>
<div>
<div class="form-check form-check-inline">
<input
class="form-check-input"
type="radio"
id="chatgptRadio"
value="chatgpt"
v-model="selectedModel"
/>
<label class="form-check-label" for="chatgptRadio">
ChatGPT
</label>
</div>
<div class="form-check form-check-inline">
<input
class="form-check-input"
type="radio"
id="claudeRadio"
value="claude"
v-model="selectedModel"
/>
<label class="form-check-label" for="claudeRadio">
Claude
</label>
</div>
</div>
</div>
</div>
<div class="row m-b-10">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.key">
<label>Title</label>
<input type="text" class="form-control" v-model="parameters.key" :disabled="this.data">
</validation-wrapper-component>
</div>
</div>
<div class="row">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.value">
<label>Prompt</label>
<textarea class="form-control h-75" v-model="parameters.value" rows="20"></textarea>
</validation-wrapper-component>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div class="btn btn-sm btn-default bg-master-lightest btn-block b-rad-none" :data-dismiss="closable ? 'modal' : ''" @click="$emit('close')">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-success btn-block b-rad-none" @click="submitForm()">{{this.data?"Update":"Create"}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ModalFormHandler from '../../../general/mixins/modalFormHandler';
import { required } from "vuelidate/lib/validators";
export default {
props : {
closable: {
type: Boolean,
default: true
},
},
data(){
return {
parameters: {
key: '',
value: '',
},
selectedModel: "claude", // default
}
},
validations: {
parameters: {
key: {
required
},
value: {
required
},
}
},
computed:{},
created(){},
methods: {
submitForm() {
let routeUrl;
let method;
if(this.data) {
if (this.data.key.startsWith("CHATGPT_PROMPT_")) {
routeUrl = this.route("api.kvp.chatgpt.prompt.update", this.data.id);
method = "put";
} else if (this.data.key.startsWith("CLAUDE_PROMPT_")) {
routeUrl = this.route("api.kvp.claude.prompt.update", this.data.id);
method = "put";
}
}
else{
if (this.selectedModel === "chatgpt") {
routeUrl = this.route("api.kvp.chatgpt.prompt.create");
method = "post";
} else if (this.selectedModel === "claude") {
routeUrl = this.route("api.kvp.claude.prompt.create");
method = "post";
}
}
this.submit(routeUrl, method, this.section, true, false);
},
},
mixins: [ModalFormHandler],
}
</script>
@@ -1,83 +0,0 @@
<template>
<div class="row">
<div class="col">
<loading-component style="height: 50px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
<div class="row" v-show="!$store.getters.isLoading(section)">
<div class="col">
<div class="row m-b-20">
<div class="col">
<h6 class="all-caps m-b-5 bold no-margin">{{this.data?"Update":"Create"}} ChatGPT Prompt</h6>
</div>
</div>
<div class="row m-b-5 animate__animated animate__fadeInUpBig animate__fast" v-if="error">
<div class="col">
<small class="bold fs-10 text-danger">{{error}}</small>
</div>
</div>
<div class="row m-b-10">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.key">
<label>Title</label>
<input type="text" class="form-control" v-model="parameters.key" :disabled="this.data">
</validation-wrapper-component>
</div>
</div>
<div class="row">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.value">
<label>Prompt</label>
<textarea class="form-control h-75" v-model="parameters.value" rows="20"></textarea>
</validation-wrapper-component>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div class="btn btn-sm btn-default bg-master-lightest btn-block b-rad-none" :data-dismiss="closable ? 'modal' : ''" @click="$emit('close')">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-success btn-block b-rad-none" @click="submitForm()">{{this.data?"Update":"Create"}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ModalFormHandler from '../../../general/mixins/modalFormHandler';
import { required } from "vuelidate/lib/validators";
export default {
props : {
closable: {
type: Boolean,
default: true
},
},
data(){
return {
parameters: {
key: '',
value: '',
}
}
},
validations: {
parameters: {
key: {
required
},
value: {
required
},
}
},
computed:{},
created(){},
methods: {
submitForm(){
this.submit((this.data ? this.route('api.kvp.prompt.update', this.data.id) : this.route('api.kvp.prompt.create')), (this.data ? 'put' : 'post'), this.section, true, false)
}
},
mixins: [ModalFormHandler],
}
</script>
@@ -15,7 +15,7 @@
<div class="btn btn-sm btn-success btn-block b-rad-none" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-danger btn-block b-rad-none" @click="submit(route('api.kvp.prompt.delete', item.id), 'delete', section, true, true)">Delete</div>
<div class="btn btn-sm btn-danger btn-block b-rad-none" @click="submit(route('api.kvp.chatgpt.prompt.delete', item.id), 'delete', section, true, true)">Delete</div>
</div>
</div>
</div>
@@ -0,0 +1,32 @@
<template>
<div class="row">
<div class="col">
<loading-component style="height: 300px; top: 0;" key="1" color="success" v-show="isLoading" ></loading-component>
<div class="row justify-content-center" v-show="!isLoading">
<div class="col">
<div class="row m-b-20">
<div class="col">
<h3 class="all-caps">Are you Sure?</h3>
<div class="fs-11">Are you sure you want to delete Claude prompt with title <b>{{item.key}}</b> ?</div>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div class="btn btn-sm btn-success btn-block b-rad-none" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-danger btn-block b-rad-none" @click="submit(route('api.kvp.claude.prompt.delete', item.id), 'delete', section, true, true)">Delete</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
import ModalFormHandler from '../../../general/mixins/modalFormHandler';
export default {
mixins: [componentHandler, ModalFormHandler]
}
</script>
+6 -6
View File
@@ -687,26 +687,26 @@
<div class="row p-b-5 m-b-20 b-b b-grey align-items-center parentContainer">
<div class="col">
<div class="font-heading all-caps fs-10 hint-text">
ChatGPT Prompts
AI Prompts
</div>
</div>
<div class="col-auto">
<button class="btn btn-xs btn-primary b-rad-none requestModal" data-type="createModal">
<i class="fa fa-plus m-r-5"></i>
Create ChatGPT Prompt
Create AI Prompt
</button>
<modal-form-component section="allChatGPTPromptsSection">
<modal-form-component section="allAIPromptsSection" >
<template slot="form" slot-scope="{section}">
<chat-gpt-form-component :section="section"></chat-gpt-form-component>
<ai-prompt-form-component :section="section"></ai-prompt-form-component>
</template>
</modal-form-component>
</div>
</div>
<div class="row m-b-20">
<div class="col">
<list-component section="allChatGPTPromptsSection" :endpoint="route('api.kvp.prompts.list')" :options="{}">
<list-component section="allAIPromptsSection" :endpoint="route('api.kvp.prompts.list')" :options="{}">
<template slot="list" slot-scope="{data}">
<single-chat-gpt-prompt-component :data="data"></single-chat-gpt-prompt-component>
<single-prompt-component :data="data"></single-prompt-component>
</template>
</list-component>
</div>
+13 -5
View File
@@ -1,14 +1,22 @@
<?php
use App\Http\Controllers\KeyValuePairs\CreateChatGPTPromptKVPController;
use App\Http\Controllers\KeyValuePairs\ListChatGPTPromptsKVPController;
use App\Http\Controllers\KeyValuePairs\ListAiPromptsKVPController;
use App\Http\Controllers\KeyValuePairs\UpdateChatGPTPromptKVPController;
use App\Http\Controllers\KeyValuePairs\DeleteChatGPTPromptKVPController;
use App\Http\Controllers\KeyValuePairs\CreateClaudePromptKVPController;
use App\Http\Controllers\KeyValuePairs\UpdateClaudePromptKVPController;
use App\Http\Controllers\KeyValuePairs\DeleteClaudePromptKVPController;
use Illuminate\Support\Facades\Route;
Route::group(['prefix' => 'kvp', 'as' => 'kvp.', 'namespace' => 'KeyValuePairs'], function () {
Route::get('/prompts/list', [ListChatGPTPromptsKVPController::class, 'list'])->name('prompts.list');
Route::put('/prompt/update/{id}', [UpdateChatGPTPromptKVPController::class, 'update'])->name('prompt.update');
Route::post('/prompt/create', [CreateChatGPTPromptKVPController::class, 'create'])->name('prompt.create');
Route::delete('/prompt/delete/{id}', [DeleteChatGPTPromptKVPController::class, 'delete'])->name('prompt.delete');
Route::get('/prompts/list', [ListAiPromptsKVPController::class, 'list'])->name('prompts.list');
Route::put('/chatgpt/prompt/update/{id}', [UpdateChatGPTPromptKVPController::class, 'update'])->name('chatgpt.prompt.update');
Route::post('/chatgpt/prompt/create', [CreateChatGPTPromptKVPController::class, 'create'])->name('chatgpt.prompt.create');
Route::delete('/chatgpt/prompt/delete/{id}', [DeleteChatGPTPromptKVPController::class, 'delete'])->name('chatgpt.prompt.delete');
Route::put('/claude/prompt/update/{id}', [UpdateClaudePromptKVPController::class, 'update'])->name('claude.prompt.update');
Route::post('/claude/prompt/create', [CreateClaudePromptKVPController::class, 'create'])->name('claude.prompt.create');
Route::delete('/claude/prompt/delete/{id}', [DeleteClaudePromptKVPController::class, 'delete'])->name('claude.prompt.delete');
});