mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/exchange-2.0.git
synced 2026-08-19 04:23:55 +00:00
Admin Workflow Initial Commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\General\Eloquent\Filters;
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class IsPrevious implements Filter
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Builder $builder
|
||||
* @param $value
|
||||
* @return Builder|mixed
|
||||
*/
|
||||
public static function apply(Builder $builder, $value)
|
||||
{
|
||||
return $builder->where('is_previous', $value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\General\Eloquent\Filters;
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class QuestionId implements Filter
|
||||
{
|
||||
/**
|
||||
* @param Builder $builder
|
||||
* @param $value
|
||||
* @return Builder|mixed
|
||||
*/
|
||||
public static function apply(Builder $builder, $value)
|
||||
{
|
||||
return $builder->where('question_id', $value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\General\Eloquent\Filters;
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class QuestionNumber implements Filter
|
||||
{
|
||||
/**
|
||||
* @param Builder $builder
|
||||
* @param $value
|
||||
* @return Builder|mixed
|
||||
*/
|
||||
public static function apply(Builder $builder, $value)
|
||||
{
|
||||
return $builder->where('question_number', $value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\General\Eloquent\Filters;
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class QuestionnaireSetId implements Filter
|
||||
{
|
||||
/**
|
||||
* @param Builder $builder
|
||||
* @param $value
|
||||
* @return Builder|mixed
|
||||
*/
|
||||
public static function apply(Builder $builder, $value)
|
||||
{
|
||||
return $builder->where('questionnaire_set_id', $value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Documents\Processors;
|
||||
|
||||
|
||||
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
|
||||
use App\Classes\Modules\Documents\Services\CreatesDocument;
|
||||
use App\Classes\Modules\Documents\Services\CreatesFiles;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\DocumentType;
|
||||
use App\Models\Document;
|
||||
use App\Models\QAUserAnswerSelected;
|
||||
|
||||
class UploadDocumentProcessor
|
||||
{
|
||||
|
||||
/** @var CreatesDocument */
|
||||
private $createsDocument;
|
||||
|
||||
/** @var CreatesFiles */
|
||||
private $createsFiles;
|
||||
|
||||
/**
|
||||
* UploadDocumentProcessor constructor.
|
||||
* @param CreatesDocument $createsDocument
|
||||
* @param CreatesFiles $createsFiles
|
||||
*/
|
||||
public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFiles)
|
||||
{
|
||||
$this->createsDocument = $createsDocument;
|
||||
$this->createsFiles = $createsFiles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $filesUpload
|
||||
* @param QAUserAnswerSelected qaUserAnswerSelected
|
||||
* @param string $path
|
||||
* @throws \App\Classes\Exceptions\MalformedRequestException
|
||||
*/
|
||||
public function execute($filesUpload, QAUserAnswerSelected $qaUserAnswerSelected, string $path) {
|
||||
$object = new DocumentObject(DocumentType::ADMIN_WORK_FLOW, $filesUpload, '', ApprovalStatus::PENDING_VERIFICATION, $path);
|
||||
|
||||
/** @var Document $document */
|
||||
$document = $this->createsDocument->execute($qaUserAnswerSelected, $object);
|
||||
|
||||
$result = $this->createsFiles->execute($document, $object);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
|
||||
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
|
||||
use App\Classes\Modules\Questionnaires\Processors\FetchFirstQuestionV1AdminWFProcessor;
|
||||
use App\Http\Resources\QuestionResource;
|
||||
use ErrorException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class FetchQuestionV1AdminWFLogic extends AbstractControllerLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function notification():array {
|
||||
return [
|
||||
'title' => 'Retrieved Admin Workflow First Question',
|
||||
'message' => 'You have successfully retrieved a Admin Workflow First Question'
|
||||
];
|
||||
}
|
||||
|
||||
/** @var CanFetchQuestion */
|
||||
private $canFetchQuestion;
|
||||
|
||||
/** @var FetchFirstQuestionV1AdminWFProcessor */
|
||||
private $fetchFirstQuestionQAProcessor;
|
||||
|
||||
/**
|
||||
* FetchQuestionV1AdminWFLogic constructor.
|
||||
* @param CanFetchQuestion $canFetchQuestion
|
||||
* @param FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor
|
||||
*/
|
||||
public function __construct(CanFetchQuestion $canFetchQuestion, FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor)
|
||||
{
|
||||
$this->canFetchQuestion = $canFetchQuestion;
|
||||
$this->fetchFirstQuestionQAProcessor = $fetchFirstQuestionQAProcessor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public function logic(Request $request) : JsonResponse
|
||||
{
|
||||
$this->canFetchQuestion->passes();
|
||||
|
||||
$userId = Auth::user()->id;
|
||||
|
||||
$query = $this->fetchFirstQuestionQAProcessor->execute($request);
|
||||
|
||||
return $this->resourceResponse(new QuestionResource($query, $userId));
|
||||
}
|
||||
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\ControllersLogic;
|
||||
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Questionnaires\Standards\Rules\CanFetchQuestion;
|
||||
use App\Classes\Modules\Questionnaires\Processors\SaveAnswerV1AdminWFProcessor;
|
||||
use App\Classes\Modules\Questionnaires\Processors\FetchNextQuestionV1AdminWFProcessor;
|
||||
use App\Classes\Modules\Questionnaires\Processors\FetchFirstQuestionV1AdminWFProcessor;
|
||||
use App\Classes\Modules\Questionnaires\Services\ListsUserAnswerSelected;
|
||||
use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair;
|
||||
use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Http\Resources\QuestionResource;
|
||||
use App\Models\Booking;
|
||||
|
||||
class UpdateNextQuestionV1AdminWFLogic extends AbstractControllerLogic
|
||||
{
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function notification():array {
|
||||
return [
|
||||
'title' => 'Retrieved Admin Workflow Next Question',
|
||||
'message' => 'You have successfully retrieved a Admin Workflow Next Question'
|
||||
];
|
||||
}
|
||||
|
||||
/** @var CanFetchQuestion */
|
||||
private $canFetchQuestion;
|
||||
|
||||
/** @var SaveAnswerV1AdminWFProcessor */
|
||||
private $saveAnswerProcessor;
|
||||
|
||||
/** @var FetchNextQuestionV1AdminWFProcessor */
|
||||
private $fetchNextQuestionQAProcessor;
|
||||
|
||||
/** @var FetchFirstQuestionV1AdminWFProcessor */
|
||||
private $fetchFirstQuestionQAProcessor;
|
||||
|
||||
/** @var ListsUserAnswerSelected */
|
||||
private $listsUserAnswerSelected;
|
||||
|
||||
/** @var CreatesKeyValuePair */
|
||||
private $createsKeyValuePair;
|
||||
|
||||
/**
|
||||
* UpdateNextQuestionV1AdminWFLogic constructor.
|
||||
* @param CanFetchQuestion $canFetchQuestion
|
||||
* @param SaveAnswerV1AdminWFProcessor $saveAnswerProcessor
|
||||
* @param FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionQAProcessor
|
||||
* @param FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor
|
||||
* @param ListsUserAnswerSelected $listsUserAnswerSelected
|
||||
* @param CreatesKeyValuePair $createsKeyValuePair
|
||||
*/
|
||||
public function __construct(CanFetchQuestion $canFetchQuestion, SaveAnswerV1AdminWFProcessor $saveAnswerProcessor, FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionQAProcessor, FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor, ListsUserAnswerSelected $listsUserAnswerSelected, CreatesKeyValuePair $createsKeyValuePair)
|
||||
{
|
||||
$this->canFetchQuestion = $canFetchQuestion;
|
||||
$this->saveAnswerProcessor = $saveAnswerProcessor;
|
||||
$this->fetchNextQuestionQAProcessor = $fetchNextQuestionQAProcessor;
|
||||
$this->fetchFirstQuestionQAProcessor = $fetchFirstQuestionQAProcessor;
|
||||
$this->listsUserAnswerSelected = $listsUserAnswerSelected;
|
||||
$this->createsKeyValuePair = $createsKeyValuePair;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws MalformedRequestException
|
||||
* @throws ResourceNotFoundException
|
||||
*/
|
||||
public function logic(Request $request) : JsonResponse
|
||||
{
|
||||
$questionId = 0;
|
||||
$userId = Auth::user()->id;
|
||||
$currentQuestion = $request->question;
|
||||
$questionId = $currentQuestion['id'];
|
||||
$questionType = $currentQuestion['question_type'];
|
||||
$currentQuestionNumber = $currentQuestion['question_number'];
|
||||
$answerNextQuestionNumber = null;
|
||||
$answerValue = null;
|
||||
$timeUsedSeconds = 0;
|
||||
$reference = null;
|
||||
$questionMetadata = null;
|
||||
|
||||
$this->canFetchQuestion->passes();
|
||||
|
||||
Log::info('currentQuestion: '. json_encode($currentQuestion));
|
||||
|
||||
if ($request->has('answerObj')) {
|
||||
$answer = $request->answerObj;
|
||||
$answerNextQuestionNumber = $answer['next_question_number'];
|
||||
$answerValue = $answer['value'];
|
||||
Log::info('answerNextQuestionNumber: '. $answerNextQuestionNumber);
|
||||
Log::info('answerValue: '. $answerValue);
|
||||
}
|
||||
|
||||
if ($request->has('extra')) {
|
||||
$extra = $request->extra;
|
||||
$questionMetadata = $extra['questionMetadata'] ?? null;
|
||||
$timeUsedSeconds = intval($extra['timeUsedSeconds']) ?? 0;
|
||||
$reference = $extra['session_id'];
|
||||
}
|
||||
|
||||
//Save answer given by user (both next and previous)
|
||||
$filesUpload = $request->input('files');
|
||||
$answerOptionId = 0;
|
||||
if ($request->has('answerObj')) {
|
||||
$answerOptionId = intval($request->answerObj['id']);
|
||||
}
|
||||
$answerInText = $request->answer;
|
||||
if ($request->has('isPrevious')){
|
||||
$answerInText = "go_back";
|
||||
}
|
||||
$answer = $this->saveAnswerProcessor->execute($userId, 0, $questionId, $questionType, $questionMetadata, $answerInText, $answerOptionId, $filesUpload, $timeUsedSeconds, $request->has('isPrevious'), $reference);
|
||||
|
||||
//Marked data that has already been processed so that it does not appear again
|
||||
$isNoGoingBack = null;
|
||||
if($currentQuestion && $currentQuestion['is_end'] === 1){
|
||||
$booking = Booking::where('id',$questionMetadata['booking']['id'])->first();
|
||||
$kvp = $answer->attributesKVP()->where('key', 'App\Models\Bank')->where('value', $questionMetadata['booking']['id'])->latest()->first();
|
||||
|
||||
$key = "admin_workflow_processed";
|
||||
if (strpos($currentQuestion['question_number'], '1688') === 0) {
|
||||
$key = '1688_'.$key;
|
||||
}
|
||||
if (strpos($currentQuestion['question_number'], 'fill_po') === 0) {
|
||||
$key = 'fill_po_'.$key;
|
||||
}
|
||||
if (strpos($currentQuestion['question_number'], 'approve_po') === 0) {
|
||||
$key = 'approve_po_'.$key;
|
||||
}
|
||||
|
||||
if(!$kvp){
|
||||
$keyValuePairObject = new KeyValuePairObject($key, true);
|
||||
$this->createsKeyValuePair->execute($booking, $keyValuePairObject);
|
||||
}
|
||||
// $isNoGoingBack = 1;
|
||||
}
|
||||
|
||||
$previousAnswer = null;
|
||||
if ($request->has('extra')) {
|
||||
$extra = $request->extra;
|
||||
$reference = $extra['session_id'];
|
||||
$previousAnswer = $this->listsUserAnswerSelected->execute(['user_id' => $userId, 'reference' => $reference, 'is_previous' => 0, 'order_by' => (object)['column' => 'id','DESC' => true]])[0];
|
||||
}
|
||||
|
||||
//Get returned question (previous or next)
|
||||
$returnQuestion = $this->fetchNextQuestionQAProcessor->execute($request, $previousAnswer);
|
||||
|
||||
//When a questionnaire ended, return back the first question
|
||||
if(is_null($returnQuestion)){
|
||||
$returnQuestion = $this->fetchFirstQuestionQAProcessor->execute($request);
|
||||
}
|
||||
|
||||
return $this->resourceResponse(new QuestionResource($returnQuestion, $userId, $request->has('isPrevious'), $isNoGoingBack, $previousAnswer));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\DataTransferObjects;
|
||||
|
||||
|
||||
use App\Classes\General\Interfaces\DataTransferObject;
|
||||
|
||||
class QAUserSourceObject implements DataTransferObject
|
||||
{
|
||||
/** @var string */
|
||||
private $system;
|
||||
|
||||
/** @var string */
|
||||
private $marking;
|
||||
|
||||
/** @var string */
|
||||
private $email;
|
||||
|
||||
/**
|
||||
* QAUserSourceObject constructor.
|
||||
* @param string $system
|
||||
* @param string $marking
|
||||
* @param string $email
|
||||
*/
|
||||
public function __construct(string $system, string $marking, string $email)
|
||||
{
|
||||
$this->system = $system;
|
||||
$this->marking = $marking;
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSystem(): string
|
||||
{
|
||||
return $this->system;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMarking(): string
|
||||
{
|
||||
return $this->marking;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Processors;
|
||||
|
||||
|
||||
use App\Classes\Modules\Questionnaires\Services\FetchesQuestion;
|
||||
use App\Models\QAQuestionnaireSet;
|
||||
use ErrorException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
||||
class FetchFirstQuestionV1AdminWFProcessor
|
||||
{
|
||||
/** @var FetchesQuestion */
|
||||
private $fetchesQuestion;
|
||||
|
||||
/**
|
||||
* FetchFirstQuestionV1AdminWFProcessor constructor.
|
||||
* @param FetchesQuestion $fetchesQuestion
|
||||
*/
|
||||
public function __construct(FetchesQuestion $fetchesQuestion)
|
||||
{
|
||||
$this->fetchesQuestion = $fetchesQuestion;
|
||||
}
|
||||
|
||||
public function execute(Request $request){
|
||||
try {
|
||||
$setId = $request->route('set_id');
|
||||
if($setId === '0'){
|
||||
$set = QAQuestionnaireSet::where('group', 'workflow')->latest('id')->first();
|
||||
$setId = $set->id;
|
||||
}
|
||||
$query = $this->fetchesQuestion->execute(['questionnaire_set_id' => $setId]);
|
||||
return $query;
|
||||
} catch (\Exception $exception){
|
||||
throw new ErrorException($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Processors;
|
||||
|
||||
|
||||
use App\Classes\Modules\Questionnaires\Services\FetchesQuestion;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Classes\ValueObjects\Constants\QAType;
|
||||
|
||||
class FetchNextQuestionV1AdminWFProcessor
|
||||
{
|
||||
/** @var FetchesQuestion */
|
||||
private $fetchesQuestion;
|
||||
|
||||
/**
|
||||
* FetchNextQuestionV1AdminWFProcessor constructor.
|
||||
* @param FetchesQuestion $fetchesQuestion
|
||||
*/
|
||||
public function __construct(FetchesQuestion $fetchesQuestion)
|
||||
{
|
||||
$this->fetchesQuestion = $fetchesQuestion;
|
||||
}
|
||||
|
||||
public function execute(Request $request, $previousAnswer){
|
||||
$isEnd = 0;
|
||||
$questionId = 0;
|
||||
$nextQuestionNumber = 0;
|
||||
$nextNestedQuestion = 0;
|
||||
$nextMainQuestion= 0;
|
||||
$questionnaireSetId = 0;
|
||||
$currentQuestion = $request->question;
|
||||
$questionType = QAType::DEFAULT;
|
||||
$returnQuestion = null;
|
||||
|
||||
if ($currentQuestion && array_key_exists('id', $currentQuestion)) {
|
||||
$questionId = $currentQuestion['id'];
|
||||
$isEnd = $currentQuestion['is_end'];
|
||||
$questionType = $currentQuestion['question_type'];
|
||||
$nextNestedQuestion = $currentQuestion['next_nested_question'];
|
||||
$nextMainQuestion = $currentQuestion['next_main_question'];
|
||||
$questionnaireSetId = $currentQuestion['questionnaire_set_id'];
|
||||
}
|
||||
|
||||
if ($request->has('answerObj') && $request->answerObj['next_question_number']) {
|
||||
$nextQuestionNumber = $request->answerObj['next_question_number'];
|
||||
}
|
||||
|
||||
if ($request->has('isPrevious')) {
|
||||
if($previousAnswer)
|
||||
{
|
||||
$previousAnswer->delete();
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Get previous question
|
||||
$previousQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'id' => $previousAnswer['question_id']]);
|
||||
$returnQuestion = $previousQuestion;
|
||||
}
|
||||
else{
|
||||
//Get next question
|
||||
$nextQuestion = null;
|
||||
$question_number = 0;
|
||||
if($nextQuestionNumber !== 0){
|
||||
$question_number = $nextQuestionNumber;
|
||||
}
|
||||
else if($nextNestedQuestion !== 0){
|
||||
$question_number = $nextNestedQuestion;
|
||||
}
|
||||
else {
|
||||
$question_number = $nextMainQuestion;
|
||||
}
|
||||
|
||||
if($question_number !== 0){
|
||||
$nextQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'question_number' => $question_number]);
|
||||
$returnQuestion = $nextQuestion;
|
||||
}
|
||||
else if (array_key_exists('id', $request->input('question'))) {
|
||||
$nextQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'id_next' => $questionId, 'next_main_question' => null]);
|
||||
$returnQuestion = $nextQuestion;
|
||||
}
|
||||
}
|
||||
|
||||
return $returnQuestion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Processors;
|
||||
|
||||
|
||||
use App\Models\QAUserAnswerSelected;
|
||||
use App\Classes\Modules\Questionnaires\Services\FetchesUserAnswerSelected;
|
||||
use App\Classes\Modules\Documents\Processors\UploadDocumentProcessor;
|
||||
use App\Classes\ValueObjects\Constants\QAType;
|
||||
|
||||
|
||||
class SaveAnswerV1AdminWFProcessor
|
||||
{
|
||||
/** @var FetchesUserAnswerSelected */
|
||||
private $fetchesUserAnswerSelected;
|
||||
|
||||
/** @var UploadDocumentProcessor */
|
||||
private $uploadDocumentForProcessor;
|
||||
|
||||
/**
|
||||
* SaveAnswerV1AdminWFProcessor constructor.
|
||||
* @param FetchesUserAnswerSelected $fetchesUserAnswerSelected
|
||||
* @param UploadDocumentProcessor $uploadDocumentForProcessor
|
||||
*/
|
||||
public function __construct(FetchesUserAnswerSelected $fetchesUserAnswerSelected, UploadDocumentProcessor $uploadDocumentForProcessor)
|
||||
{
|
||||
$this->fetchesUserAnswerSelected = $fetchesUserAnswerSelected;
|
||||
$this->uploadDocumentForProcessor = $uploadDocumentForProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return QAUserAnswerSelected|\Illuminate\Database\Eloquent\Model
|
||||
*/
|
||||
public function execute($userId, $sourceId, $questionId, $questionType, $questionMetadata, $answerInText, $answerOptionId, $filesUpload, $timeUsedSeconds, $isPrevious, $reference = null){
|
||||
|
||||
$answer = null;
|
||||
$attachments = null;
|
||||
|
||||
if(is_null($answer))
|
||||
{
|
||||
$answer = new QAUserAnswerSelected;
|
||||
}
|
||||
$answer->user_id = $userId;
|
||||
$answer->source_id = $sourceId;
|
||||
$answer->question_id = $questionId;
|
||||
$answer->question_metadata = json_encode($questionMetadata);
|
||||
$answer->answer_option_id = $answerOptionId;
|
||||
$answer->time_used_seconds = $timeUsedSeconds;
|
||||
$answer->is_previous = $isPrevious;
|
||||
if($reference){
|
||||
$answer->reference = $reference;
|
||||
}
|
||||
$answer->save(); //cief todo: why 2 save() in this file, this is wrong
|
||||
|
||||
if($filesUpload){
|
||||
$result = $this->uploadDocumentForProcessor->execute($filesUpload, $answer, 'questionnaires');
|
||||
$attachments = array_map(function ($item) {
|
||||
return [
|
||||
'document_id' => $item->document_id,
|
||||
'file_id' => $item->id,
|
||||
];
|
||||
}, $result);
|
||||
}
|
||||
|
||||
if($questionType === QAType::REMARKS_WITH_DOCUMENT_UPLOAD){
|
||||
$structuredAnswer = [
|
||||
'text' => $answerInText,
|
||||
'files' => $attachments,
|
||||
];
|
||||
$answer->answer = json_encode($structuredAnswer);
|
||||
}
|
||||
else if($questionType === QAType::DOCUMENT_UPLOAD){
|
||||
$structuredAnswer = [
|
||||
'files' => $attachments,
|
||||
];
|
||||
$answer->answer = json_encode($structuredAnswer);
|
||||
}
|
||||
else {
|
||||
$answer->answer = $answerInText;
|
||||
}
|
||||
$answer->save();
|
||||
|
||||
return $answer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Services;
|
||||
|
||||
|
||||
use App\Classes\General\Eloquent\AbstractUpdateRecord;
|
||||
use App\Classes\Modules\Questionnaires\DataTransferObjects\QAUserSourceObject;
|
||||
use App\Models\QAUserSource;
|
||||
|
||||
class CreatesQAUserSource extends AbstractUpdateRecord
|
||||
{
|
||||
/**
|
||||
* @param QAUserSourceObject $object
|
||||
* @return \Illuminate\Database\Eloquent\Model
|
||||
* @throws \App\Classes\Exceptions\MalformedRequestException
|
||||
*/
|
||||
public function execute(QAUserSourceObject $object) {
|
||||
$model = new QAUserSource();
|
||||
$model->system = $object->getSystem();
|
||||
$model->marking = $object->getMarking();
|
||||
$model->email = $object->getEmail();
|
||||
|
||||
return $this->handler($model);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Services;
|
||||
|
||||
|
||||
use App\Classes\General\Eloquent\AbstractFetchRecord;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\QAUserSource;
|
||||
|
||||
class FetchesQAUserSource extends AbstractFetchRecord
|
||||
{
|
||||
|
||||
/** @var QAUserSource */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* FetchesQAUserSource constructor.
|
||||
* @param QAUserSource $repository
|
||||
*/
|
||||
public function __construct(QAUserSource $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRepository(): Builder
|
||||
{
|
||||
return $this->repository->newQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Services;
|
||||
|
||||
|
||||
use App\Classes\General\Eloquent\AbstractFetchRecord;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\QAQuestions;
|
||||
|
||||
class FetchesQuestion extends AbstractFetchRecord
|
||||
{
|
||||
|
||||
/** @var QAQuestions */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* FetchesQuestion constructor.
|
||||
* @param QAQuestions $repository
|
||||
*/
|
||||
public function __construct(QAQuestions $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRepository(): Builder
|
||||
{
|
||||
return $this->repository->newQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Services;
|
||||
|
||||
|
||||
use App\Classes\General\Eloquent\AbstractFetchRecord;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\QAUserAnswerSelected;
|
||||
|
||||
class FetchesUserAnswerSelected extends AbstractFetchRecord
|
||||
{
|
||||
|
||||
/** @var QAUserAnswerSelected */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* FetchesUserAnswerSelected constructor.
|
||||
* @param QAUserAnswerSelected $repository
|
||||
*/
|
||||
public function __construct(QAUserAnswerSelected $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRepository(): Builder
|
||||
{
|
||||
return $this->repository->newQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Services;
|
||||
|
||||
|
||||
use App\Classes\General\Eloquent\AbstractListRecord;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\QAQuestions;
|
||||
|
||||
class ListsQuestions extends AbstractListRecord
|
||||
{
|
||||
|
||||
/** @var QAQuestions */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ListsQuestions constructor.
|
||||
* @param QAQuestions $repository
|
||||
*/
|
||||
public function __construct(QAQuestions $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRepository(): Builder
|
||||
{
|
||||
return $this->repository->newQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Services;
|
||||
|
||||
|
||||
use App\Classes\General\Eloquent\AbstractListRecord;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\QAUserAnswerSelected;
|
||||
|
||||
class ListsUserAnswerSelected extends AbstractListRecord
|
||||
{
|
||||
|
||||
/** @var QAUserAnswerSelected */
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ListsUserAnswerSelected constructor.
|
||||
* @param QAUserAnswerSelected $repository
|
||||
*/
|
||||
public function __construct(QAUserAnswerSelected $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Builder
|
||||
*/
|
||||
public function getRepository(): Builder
|
||||
{
|
||||
return $this->repository->newQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Standards\Rules;
|
||||
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractRule;
|
||||
|
||||
class CanFetchQuestion extends AbstractRule
|
||||
{
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function authorized($object): bool
|
||||
{
|
||||
// TODO Set Authorization rules
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function validators($object): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function criteria($object): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Questionnaires\Standards\Rules;
|
||||
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractRule;
|
||||
use App\Classes\ValueObjects\Constants\RoleTypes;
|
||||
|
||||
class CanListQuestions extends AbstractRule
|
||||
{
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function authorized($object): bool
|
||||
{
|
||||
$roleToCheck = Auth()->user()->type;
|
||||
if (in_array($roleToCheck, RoleTypes::ADMIN_ROLES)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function validators($object): bool
|
||||
{
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $object
|
||||
* @return bool
|
||||
*/
|
||||
protected function criteria($object): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Classes\ValueObjects\Constants;
|
||||
|
||||
|
||||
final class DocumentType {
|
||||
|
||||
public const PROFILE_PICTURE = 'PROFILE_PICTURE';
|
||||
@@ -27,4 +28,5 @@ final class DocumentType {
|
||||
public const BULK_PURCHASE_ORDER = 'BULK_PURCHASE_ORDER';
|
||||
|
||||
public const BILL_GROUP_PAYMENT_PROOF = 'BILL_GROUP_PAYMENT_PROOF';
|
||||
public const ADMIN_WORK_FLOW = 'ADMIN_WORK_FLOW';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\ValueObjects\Constants;
|
||||
|
||||
|
||||
final class QAType {
|
||||
|
||||
public const DEFAULT = 0;
|
||||
|
||||
public const MULTIPLE_CHOICES = 1;
|
||||
|
||||
// public const FREE_TEXT = 2;
|
||||
|
||||
public const DOCUMENT_UPLOAD = 3;
|
||||
|
||||
// public const ANSWER = 4;
|
||||
|
||||
// public const TICKET_CRM = 5;
|
||||
|
||||
// public const RATING_5_STARS = 6;
|
||||
|
||||
public const REMARKS_WITH_DOCUMENT_UPLOAD = 7;
|
||||
|
||||
public const SUBMIT_1688_3_TYPES_DOCUMENTS = 8;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Questionnaires;
|
||||
|
||||
|
||||
use App\Classes\Modules\Questionnaires\ControllersLogic\FetchQuestionV1AdminWFLogic;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FetchQuestionV1AdminWFController
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param FetchQuestionV1AdminWFLogic $logic
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function fetch(Request $request, FetchQuestionV1AdminWFLogic $logic): JsonResponse {
|
||||
return $logic->execute($request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Questionnaires;
|
||||
|
||||
|
||||
use App\Classes\Modules\Questionnaires\ControllersLogic\UpdateNextQuestionV1AdminWFLogic;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\QAQuestionnaireSet;
|
||||
|
||||
class UpdateNextQuestionV1AdminWFController
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param FetchQAQuestionListLogic $logic
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function fetch(Request $request, UpdateNextQuestionV1AdminWFLogic $logic): JsonResponse {
|
||||
// To deploy different logic by versioning
|
||||
// $currentQuestion = $request->question;
|
||||
// $questionnaireSetId = $currentQuestion['questionnaire_set_id'];
|
||||
// $questionnaireSet = QAQuestionnaireSet::where('id', $questionnaireSetId)->first();
|
||||
// $group = '';
|
||||
// $version = 0;
|
||||
// if($questionnaireSet){
|
||||
// $group = $questionnaireSet->group;
|
||||
// $version = $questionnaireSet->version;
|
||||
// }
|
||||
return $logic->execute($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class AnswerOptionsResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'display_text' => $this->display_text,
|
||||
'value' => $this->value,
|
||||
'order' => $this->order,
|
||||
'question_number' => $this->question_number,
|
||||
'next_question_number' => $this->next_question_number,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use App\Models\QAQuestions;
|
||||
use App\Models\QAAnswerOptions;
|
||||
use App\Models\QAUserAnswerSelected;
|
||||
|
||||
class QuestionResource extends JsonResource
|
||||
{
|
||||
/** @var integer */
|
||||
private $userId;
|
||||
|
||||
/** @var QAQuestions*/
|
||||
private $question;
|
||||
|
||||
/** @var bool */
|
||||
private $isPrevious;
|
||||
|
||||
/** @var integer */
|
||||
private $isNoGoingBack;
|
||||
|
||||
/** @var integer */
|
||||
private $previousAnswer;
|
||||
|
||||
public function __construct($question, $userId, $isPrevious = false, $isNoGoingBack = null, $previousAnswer = null) {
|
||||
$this->question = $question;
|
||||
$this->userId = $userId;
|
||||
$this->isPrevious = $isPrevious;
|
||||
$this->isNoGoingBack = $isNoGoingBack;
|
||||
$this->previousAnswer = $previousAnswer;
|
||||
}
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
if($this->question){
|
||||
$q = (object)$this->question;
|
||||
return [
|
||||
'id' => $q->id,
|
||||
'question_number' => $q->question_number,
|
||||
'question_title' => $q->question_title,
|
||||
'question_description' => $q->question_description,
|
||||
'question_type' => $q->question_type,
|
||||
'question_answers' => AnswerOptionsResource::collection(QAAnswerOptions::where('question_number', $q->question_number)->where('questionnaire_set_id', $q->questionnaire_set_id)->orderBy('order', 'ASC')->get()),
|
||||
'questionnaire_set_id' => $q->questionnaire_set_id,
|
||||
// 'questionnaire' => new QuestionnaireSetsResource($q->questionnaire),
|
||||
// 'questionnaire_answers' => $q->is_end ? QuestionnaireAnswersResource::collection(QAUserAnswerSelected::where('user_id', $this->userId)->get()) : null,
|
||||
'next_nested_question' => $q->next_nested_question,
|
||||
'next_main_question' => $q->next_main_question,
|
||||
'is_start' => $q->is_start,
|
||||
'is_end' => $q->is_end,
|
||||
'end_text' => $q->end_text,
|
||||
'order' => $q->order,
|
||||
'answer' => $this->previousAnswer,
|
||||
'url' => $q->url,
|
||||
'is_no_going_back' => $this->isNoGoingBack,
|
||||
'is_previous' => $this->isPrevious,
|
||||
];
|
||||
}
|
||||
else{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use App\Models\QAQuestions;
|
||||
|
||||
class QuestionnaireAnswersResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
$question = QAQuestions::where('id', $this->question_id)->first();
|
||||
return [
|
||||
'question_title' => $question ? $question->question_title : null,
|
||||
'answer' => $this->answer
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class QuestionnaireSetsResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
// 'name' => $this->name,
|
||||
// 'description' => $this->description,
|
||||
'group' => $this->group,
|
||||
'version' => $this->version,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use App\Models\QAAnswerOptions;
|
||||
use App\Models\QAQuestions;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class QuestionsAnswersResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
$question = QAQuestions::where('id', $this->question_id)->first();
|
||||
$source = new UserSourceResource($this->userSource);
|
||||
$user = $this->source_id === 0 ? new UserResource($this->user) : null;
|
||||
$answerOption = QAAnswerOptions::where('id', $this->answer_option_id)->first();
|
||||
|
||||
$user_marking = '';
|
||||
if($user){
|
||||
$companyModule = $user->companyModule()->first();
|
||||
$user_marking = $companyModule ? $companyModule->inviters()->withPivot('invitee_reference')->first()->pivot->invitee_reference : "";
|
||||
}
|
||||
|
||||
return [
|
||||
'question_id' => $this->question_id,
|
||||
'questionnaire' => new QuestionnaireSetsResource($this->question->questionnaire),
|
||||
'question_title' => $question ? $question->question_title : null,
|
||||
'answer' => $answerOption ? $answerOption->display_text : null,
|
||||
'answer_value' => $answerOption ? $answerOption->value : null,
|
||||
'source_system' => null,
|
||||
'source_marking' => $user ? $user_marking : $source->marking,
|
||||
'source_email' => $user ? $user->email : $source->email,
|
||||
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserSourceResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'system' => $this->system,
|
||||
'email' => $this->email,
|
||||
'marking' => $this->marking,
|
||||
];
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
|
||||
use App\Classes\General\Interfaces\Documentable;
|
||||
use App\Classes\General\Interfaces\Transactionable;
|
||||
use App\Classes\General\Traits\LogData;
|
||||
use App\Classes\ValueObjects\Constants\RoleTypes;
|
||||
use App\Classes\General\Interfaces\KeyValueInterface;
|
||||
use App\Scopes\CustomerBookingsScope;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
@@ -25,7 +27,7 @@ use Staudenmeir\EloquentHasManyDeep\HasRelationships;
|
||||
* @property int convertible_currency_id
|
||||
* @property int conversion_currency_id
|
||||
*/
|
||||
class Booking extends AbstractModel implements Documentable, Transactionable
|
||||
class Booking extends AbstractModel implements Documentable, Transactionable, KeyValueInterface
|
||||
{
|
||||
use HasRelationships;
|
||||
use SoftDeletes;
|
||||
@@ -121,5 +123,11 @@ class Booking extends AbstractModel implements Documentable, Transactionable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return MorphMany
|
||||
*/
|
||||
public function attributesKVP(): MorphMany
|
||||
{
|
||||
return $this->morphMany(KeyValuePair::class, 'owner');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
|
||||
class QAAnswerOptions extends AbstractModel
|
||||
{
|
||||
protected $table = 'qa_answer_options';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
|
||||
class QAQuestionnaireSet extends AbstractModel
|
||||
{
|
||||
protected $table = 'qa_questionnaire_sets';
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class QAQuestions extends AbstractModel
|
||||
{
|
||||
protected $table = 'qa_questions';
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function questionnaire(): BelongsTo
|
||||
{
|
||||
return $this->BelongsTo(QAQuestionnaireSet::class, 'questionnaire_set_id', 'id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
|
||||
use App\Classes\General\Interfaces\Documentable;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Classes\General\Interfaces\KeyValueInterface;
|
||||
|
||||
/**
|
||||
* Class Contact
|
||||
* @package App\Models
|
||||
*/
|
||||
class QAUserAnswerSelected extends AbstractModel implements Documentable, KeyValueInterface
|
||||
{
|
||||
protected $table = 'qa_user_answer_selected';
|
||||
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* @return MorphMany
|
||||
*/
|
||||
public function documents(): morphMany
|
||||
{
|
||||
return $this->morphMany(Document::class, 'owner');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function userSource(): BelongsTo
|
||||
{
|
||||
return $this->BelongsTo(QAUserSource::class, 'source_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->BelongsTo(User::class, 'user_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function question(): BelongsTo
|
||||
{
|
||||
return $this->BelongsTo(QAQuestions::class, 'question_id', 'id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function answer(): BelongsTo
|
||||
{
|
||||
return $this->BelongsTo(QAAnswerOptions::class, 'answer_option_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MorphMany
|
||||
*/
|
||||
public function attributesKVP(): MorphMany
|
||||
{
|
||||
return $this->morphMany(KeyValuePair::class, 'owner');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
|
||||
class QAUserSource extends AbstractModel
|
||||
{
|
||||
protected $table = 'qa_user_source';
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ChangeValueColumnToTextInKeyValuePairsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('key_value_pairs', function (Blueprint $table) {
|
||||
$table->text('value')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('key_value_pairs', function (Blueprint $table) {
|
||||
$table->string('value', 191)->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateQAAnswerOptionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('qa_answer_options', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->text('display_text');
|
||||
$table->string('value');
|
||||
$table->unsignedBigInteger('order')->default(0);
|
||||
$table->string('question_number');
|
||||
$table->string('next_question_number')->nullable();
|
||||
$table->unsignedBigInteger('questionnaire_set_id');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->foreign('questionnaire_set_id')->references('id')->on('qa_questionnaire_sets');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('qa_answer_options');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateQAQuestionnaireSetsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('qa_questionnaire_sets', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('description')->nullable();
|
||||
$table->string('group')->nullable();
|
||||
$table->unsignedBigInteger('order')->default(0);
|
||||
$table->unsignedBigInteger('next_set')->nullable();
|
||||
$table->unsignedBigInteger('version')->default(1);
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('qa_questionnaire_sets');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateQAQuestionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('qa_questions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('question_number');
|
||||
$table->text('question_title');
|
||||
$table->text('question_description')->nullable();
|
||||
$table->unsignedBigInteger('questionnaire_set_id');
|
||||
$table->string('next_nested_question')->nullable();
|
||||
$table->string('next_main_question')->nullable();
|
||||
$table->unsignedBigInteger('question_type');
|
||||
$table->tinyInteger('is_start');
|
||||
$table->tinyInteger('is_end');
|
||||
$table->string('end_text')->nullable();
|
||||
$table->unsignedBigInteger('order')->default(0);
|
||||
$table->string('url')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->foreign('questionnaire_set_id')->references('id')->on('qa_questionnaire_sets');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('qa_questions');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateQAUserAnswerSelectedTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('qa_user_answer_selected', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('user_id')->default(0);
|
||||
$table->unsignedBigInteger('source_id')->default(0);
|
||||
$table->unsignedBigInteger('question_id');
|
||||
$table->text('question_metadata')->nullable();
|
||||
$table->unsignedBigInteger('answer_option_id');
|
||||
$table->text('answer')->nullable();
|
||||
$table->string('reference')->nullable();
|
||||
$table->integer('time_used_seconds')->nullable();
|
||||
$table->tinyInteger('is_previous')->default(0);
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->foreign('question_id')->references('id')->on('qa_questions');
|
||||
// $table->foreign('answer_option_id')->references('id')->on('qa_answer_options');
|
||||
// $table->foreign('user_id')->references('id')->on('users');
|
||||
// $table->foreign('source_id')->references('id')->on('qa_user_source');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('qa_user_answer_selected');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateQaUserSourceTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('qa_user_source', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('system');
|
||||
$table->string('email');
|
||||
$table->string('marking');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('qa_user_source');
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,9 @@ class DatabaseSeeder extends Seeder
|
||||
$this->call(DummyDataSeeder::class);
|
||||
}
|
||||
|
||||
// Admin Work Flow
|
||||
// $this->call(QAWorkFlow2Seeder::class);
|
||||
|
||||
// DB::commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,437 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\QAQuestionnaireSet;
|
||||
use App\Models\QAQuestions;
|
||||
use App\Models\QAAnswerOptions;
|
||||
use App\Classes\ValueObjects\Constants\QAType;
|
||||
|
||||
class QAWorkFlow2Seeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$questionnaireSets = [
|
||||
[
|
||||
'name' => 'Admin Work Flow',
|
||||
'description' => 'Admin Work Flow',
|
||||
'group' => 'workflow',
|
||||
'version' => 1,
|
||||
'questions' => [
|
||||
[
|
||||
'question_number' => 'node_0',
|
||||
'question_title' => 'Are you ready to work today?',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => true,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Yes', 'value' => 'start_work', 'next_question_number' => 'start_work'],
|
||||
['display_text' => 'No', 'value' => 'no_work', 'next_question_number' => 'no_work'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'start_work',
|
||||
'question_title' => 'What will you work on?',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => '1688', 'value' => '1688', 'next_question_number' => '1688'],
|
||||
['display_text' => 'Approve PO', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
|
||||
['display_text' => 'Fill PO', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'no_work',
|
||||
'question_title' => 'Come back when you are ready to work',
|
||||
'question_type' => QAType::DEFAULT,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
],
|
||||
[
|
||||
'question_number' => '1688',
|
||||
'question_title' => '1688',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'url' => 'api.admin_work_flow.fetch_oldest_order', //'http://localhost:8082/api/v1/admin-work-flow/fetch-oldest-order', //this.route("api.admin_work_flow.fetch_oldest_order"),
|
||||
'is_start' => true,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Login Issue', 'value' => '1688_login_issue', 'btn_color' => 'warning', 'next_question_number' => '1688_login_issue'],
|
||||
['display_text' => 'Login Successful', 'value' => '1688_login_successful', 'next_question_number' => '1688_login_successful'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_login_issue',
|
||||
'question_title' => '1688_login_issue',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Need TAC', 'value' => 'Need Tac', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'Wrong login details', 'value' => 'Wrong Login Details', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'Others', 'value' => '1688_login_issue_others', 'next_question_number' => '1688_login_issue_others'],
|
||||
['display_text' => 'Order Cancelled', 'value' => '1688_refund_request', 'next_question_number' => '1688_refund_request'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_login_successful',
|
||||
'question_title' => '1688_login_successful',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'url' => 'api.admin_work_flow.fetch_model_attributes', //'http://localhost:8082/api/v1/admin-work-flow/{booking_id}/fetch-model-attributes',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => "Can't verify order?", 'value' => '1688_login_successful_cannot_verify', 'next_question_number' => '1688_login_successful_cannot_verify'],
|
||||
['display_text' => 'Order Verified', 'value' => '1688_order_verify', 'next_question_number' => '1688_order_verify'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_login_successful_cannot_verify',
|
||||
'question_title' => '1688_login_successful_cannot_verify',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Amount not found', 'value' => 'Amount not found', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'Plus Member', 'value' => 'Plus Member', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'Others', 'value' => '1688_login_successful_cannot_verify_others', 'next_question_number' => '1688_login_successful_cannot_verify_others'],
|
||||
['display_text' => 'Customer did not verify 1688 account', 'value' => 'Customer did not verify 1688 account', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'WorldFirst account linked another account', 'value' => 'WorldFirst account linked another account', 'next_question_number' => '1688_issue_submit'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_login_successful_cannot_verify_others',
|
||||
'question_title' => 'What other issues did you encounter? Upload documents if needed',
|
||||
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
|
||||
'next_nested_question' => '1688_issue_submit',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_order_verify',
|
||||
'question_title' => 'Order Amount',
|
||||
'question_description' => 'Please key in the order amount',
|
||||
'question_' => 'Order Amount',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'url' => 'api.admin_work_flow.fetch_model_attributes',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Yes', 'value' => '1688_order_verification', 'next_question_number' => '1688_order_verification'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_order_verification',
|
||||
'question_title' => 'Are You Sure this is the correct amount?',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Yes', 'value' => '1688_proceed_order', 'next_question_number' => '1688_proceed_order'],
|
||||
['display_text' => 'No', 'value' => 'insufficient_order', 'next_question_number' => 'insufficient_order'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_proceed_order',
|
||||
'question_title' => 'Proceed the order on 1688?',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Yes', 'value' => '1688_submit', 'next_question_number' => '1688_submit'],
|
||||
['display_text' => 'Got Issue', 'value' => '1688_proceed_order_issue', 'next_question_number' => '1688_proceed_order_issue'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_proceed_order_issue',
|
||||
'question_title' => '1688_proceed_order_issue',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Wrong Pin Number', 'value' => 'Wrong Pin Number', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'Not Enough Stock', 'value' => 'Not Enough Stock', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'Others', 'value' => 'Others', 'next_question_number' => '1688_proceed_order_issue_others'],
|
||||
['display_text' => 'No CrossBoarder', 'value' => 'No CrossBoarder', 'next_question_number' => '1688_issue_submit'],
|
||||
['display_text' => 'AngPau', 'value' => 'AngPau', 'next_question_number' => '1688_issue_submit'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_proceed_order_issue_others',
|
||||
'question_title' => 'What other issues did you encounter? Upload documents if needed',
|
||||
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
|
||||
'next_nested_question' => '1688_issue_submit',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_login_issue_others',
|
||||
'question_title' => 'What other issues did you encounter? Upload documents if needed',
|
||||
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
|
||||
'next_nested_question' => '1688_issue_submit',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_submit',
|
||||
'question_title' => '1688_submit',
|
||||
'question_type' => QAType::SUBMIT_1688_3_TYPES_DOCUMENTS,
|
||||
'next_nested_question' => '1688_issue_submit',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_issue_submit',
|
||||
'question_title' => 'Issue has been submitted',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => '1688_refund_request',
|
||||
'question_title' => 'Refund Request sent',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next Order', 'value' => '1688', 'next_question_number' => '1688'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'approve_po',
|
||||
'question_title' => 'approve_po',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'url' => "api.admin_work_flow.fetch_pending_approve_po",
|
||||
'is_start' => true,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Approve', 'value' => 'approve_po_approved', 'next_question_number' => 'approve_po_approved'],
|
||||
['display_text' => 'Edit PO', 'value' => 'approve_po_edit', 'next_question_number' => 'approve_po_edit'],
|
||||
['display_text' => 'Reject', 'value' => 'approve_po_reject', 'next_question_number' => 'approve_po_reject'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'fill_po',
|
||||
'question_title' => 'fill_po',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'url' => "api.admin_work_flow.fetch_pending_fill_po",
|
||||
'is_start' => true,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Edit PO', 'value' => 'fill_po_edit', 'next_question_number' => 'fill_po_edit'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'approve_po_filled',
|
||||
'question_title' => 'PO Filled',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
|
||||
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'approve_po_approved',
|
||||
'question_title' => 'PO Approved',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
|
||||
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'approve_po_edit',
|
||||
'question_title' => 'approve_po_edit',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'url' => "api.booking.show",
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Issue?', 'value' => 'approve_po_edit_po_issue', 'next_question_number' => 'approve_po_edit_po_issue'],
|
||||
['display_text' => 'Done', 'value' => 'approve_po_filled', 'next_question_number' => 'approve_po_filled'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'approve_po_reject',
|
||||
'question_title' => 'PO Rejected',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Sensitive Goods', 'value' => 'Sensitive Goods'],
|
||||
['display_text' => 'Others', 'value' => 'PO Others', 'next_question_number' => 'reject_po_others'],
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next Order', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'reject_po_others',
|
||||
'question_title' => 'What other issues did you encounter? Upload documents if needed',
|
||||
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
|
||||
'next_nested_question' => 'reject_po_others_complete',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
],
|
||||
[
|
||||
'question_number' => 'reject_po_others_complete',
|
||||
'question_title' => 'Issue has been submitted',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
|
||||
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'approve_po_edit_po_issue',
|
||||
'question_title' => 'approve_po_edit_po_issue',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Sensitive Goods', 'value' => 'Sensitive Goods', 'next_question_number' => 'fill_po_issue_submit'],
|
||||
['display_text' => 'Others', 'value' => 'fill_po_issue_others', 'next_question_number' => 'fill_po_issue_others'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'fill_po_issue_others',
|
||||
'question_title' => 'What other issues did you encounter? Upload documents if needed',
|
||||
'question_type' => QAType::REMARKS_WITH_DOCUMENT_UPLOAD,
|
||||
'next_nested_question' => 'fill_po_issue_submit',
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
],
|
||||
[
|
||||
'question_number' => 'fill_po_issue_submit',
|
||||
'question_title' => 'Issue has been submitted',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
|
||||
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'stop_working',
|
||||
'question_title' => 'Thank you. Reload page to restart.',
|
||||
'question_type' => QAType::DEFAULT,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
],
|
||||
[
|
||||
'question_number' => 'insufficient_order',
|
||||
'question_title' => 'Are You Sure this is the correct amount?',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Underpaid Order', 'value' => 'underpaird_order_1', 'next_question_number' => ''],
|
||||
['display_text' => 'Underpaid Order', 'value' => 'underpaird_order_2', 'next_question_number' => ''],
|
||||
['display_text' => 'Overpaid Order', 'value' => 'underpaird_order_2', 'next_question_number' => ''],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'fill_po_edit',
|
||||
'question_title' => 'fill_po_edit',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'url' => "api.booking.show",
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Issue?', 'value' => 'fill_po_edit_issue', 'next_question_number' => 'fill_po_edit_issue'],
|
||||
['display_text' => 'Done', 'value' => 'fill_po_edit_filled', 'next_question_number' => 'fill_po_edit_filled'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'fill_po_edit_issue',
|
||||
'question_title' => 'fill_po_edit_issue',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => false,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Sensitive Goods', 'value' => 'Sensitive Goods', 'next_question_number' => 'fill_po_issue_submit'],
|
||||
['display_text' => 'Others', 'value' => 'fill_po_issue_others', 'next_question_number' => 'fill_po_issue_others'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'question_number' => 'fill_po_edit_filled',
|
||||
'question_title' => 'PO Filled',
|
||||
'question_type' => QAType::MULTIPLE_CHOICES,
|
||||
'is_start' => false,
|
||||
'is_end' => true,
|
||||
'answer_options' => [
|
||||
['display_text' => 'Stop Working', 'value' => 'stop_working', 'next_question_number' => 'stop_working'],
|
||||
['display_text' => 'Next PO (Fill)', 'value' => 'fill_po', 'next_question_number' => 'fill_po'],
|
||||
['display_text' => 'Next PO (Approve)', 'value' => 'approve_po', 'next_question_number' => 'approve_po'],
|
||||
],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($questionnaireSets as $set) {
|
||||
$questionnaireSet = QAQuestionnaireSet::create([
|
||||
'name' => $set['name'],
|
||||
'description' => $set['description'],
|
||||
'group' => $set['group'],
|
||||
]);
|
||||
|
||||
foreach ($set['questions'] as $key => $questionData) {
|
||||
$question = new QAQuestions;
|
||||
$question->question_number = $questionData['question_number'];
|
||||
$question->question_title = $questionData['question_title'];
|
||||
if (isset($questionData['question_description'])) {
|
||||
$question->question_description = $questionData['question_description'];
|
||||
}
|
||||
$question->question_type = $questionData['question_type'];
|
||||
$question->questionnaire_set_id = $questionnaireSet->id;
|
||||
|
||||
if (isset($questionData['next_nested_question'])) {
|
||||
$question->next_nested_question = $questionData['next_nested_question'];
|
||||
}
|
||||
if (isset($questionData['next_main_question'])) {
|
||||
$question->next_main_question = $questionData['next_main_question'];
|
||||
}
|
||||
|
||||
$question->is_start = $questionData['is_start'];
|
||||
$question->is_end = $questionData['is_end'];
|
||||
if (isset($questionData['end_text'])) {
|
||||
$question->end_text = $questionData['end_text'];
|
||||
}
|
||||
|
||||
if (isset($questionData['url'])) {
|
||||
$question->url = $questionData['url'];
|
||||
}
|
||||
|
||||
$question->order = $key + 1;
|
||||
$question->save();
|
||||
|
||||
if (isset($questionData['answer_options'])) {
|
||||
foreach ($questionData['answer_options'] as $optionData) {
|
||||
$answerOption = new QAAnswerOptions;
|
||||
$answerOption->display_text = $optionData['display_text'];
|
||||
$answerOption->value = $optionData['value'];
|
||||
$answerOption->question_number = $questionData['question_number'];
|
||||
$answerOption->next_question_number = $optionData['next_question_number'] ?? null;
|
||||
$answerOption->questionnaire_set_id = $questionnaireSet->id;
|
||||
$answerOption->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,9 @@
|
||||
this.parameters = {
|
||||
products: this.products
|
||||
};
|
||||
|
||||
this.$emit('update-parameters', {
|
||||
parameters: this.parameters,
|
||||
});
|
||||
this.submit(route('api.transaction.po.create', this.data.id), 'post', this.section, true, true);
|
||||
},
|
||||
uploadProducts() {
|
||||
@@ -291,7 +293,9 @@
|
||||
this.parameters = {
|
||||
files: this.files
|
||||
};
|
||||
|
||||
this.$emit('update-parameters', {
|
||||
parameters: this.parameters,
|
||||
});
|
||||
this.submit(route('api.transaction.po.import', this.data.id), 'post', this.section, true, true);
|
||||
},
|
||||
successHandler(){
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row no-margin">
|
||||
<div class="col p-b-15 p-l-0 p-r-0">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row" v-for="(product, index) in products" :key="product.id">
|
||||
<div class="col p-b-10 p-t-10 " :class="[{'b-grey' : index !== Object.keys(products).length - 1}, {'b-b' : index !== Object.keys(products).length - 1}]">
|
||||
<purchase-order-item-form-component :data="product" :index="index" :currency="data.fixed_currency.short_code" :editable="false" :section="section"></purchase-order-item-form-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12" v-if="data.company.address">
|
||||
<div class="row justify-content-center align-items-center text-center">
|
||||
<h6 class="font-heading all-caps bold">Total: </h6>
|
||||
<h6><span v-if="!submitted" class="bold m-r-5" :class="[{'text-danger' : (Math.round((poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3) !== (Math.round((data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}, {'text-success' : (Math.round((this.poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3) === (Math.round((this.data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}]">{{(Math.round(( poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3)}}/</span><span class="text-primary bold m-l-5">{{(Math.round((data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{data.fixed_currency.short_code}}</span></h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import formHandler from '../../../general/mixins/formHandler';
|
||||
import { required, requiredIf } from "vuelidate/lib/validators";
|
||||
|
||||
export default {
|
||||
props:{
|
||||
companySegmentIds: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
interval:false,
|
||||
submitted: false,
|
||||
useUploadCsvPo: false,
|
||||
product: {
|
||||
stockCode: '',
|
||||
description: '',
|
||||
quantity: 1,
|
||||
unit_price: 0,
|
||||
},
|
||||
products: [],
|
||||
files: [],
|
||||
uploadFiles: false,
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
files: {
|
||||
required: requiredIf(function () { return this.uploadFiles })
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.products = this.data.purchase_order ? this.data.purchase_order.details : [];
|
||||
this.submitted = this.data.purchase_order ? this.data.purchase_order.status === 1 || this.data.purchase_order.status === 2: false;
|
||||
},
|
||||
computed: {
|
||||
productTotal(){
|
||||
return this.product.quantity * parseFloat((this.product.unit_price).toString().replaceAll(',', ''));
|
||||
},
|
||||
poTotal(){
|
||||
return this.products.reduce(function(last, product) {
|
||||
return last + product.total;
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'data': function () {
|
||||
if (this.data && this.data.purchase_order && this.data.purchase_order.details) {
|
||||
this.products = this.data.purchase_order.details;
|
||||
this.submitted = this.data.purchase_order ? this.data.purchase_order.status === 1 || this.data.purchase_order.status === 2: false;
|
||||
} else {
|
||||
this.products = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
mixins: [formHandler]
|
||||
}
|
||||
</script>
|
||||
@@ -35,7 +35,7 @@
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto">
|
||||
<p class="m-b-0 small muted">Unit Price</p>
|
||||
<p class="m-b-0 bold">{{product.unit_price}}</p>
|
||||
<p class="m-b-0 bold">{{product.unit_price.toFixed(3)}}</p>
|
||||
</div>
|
||||
<div class="col-auto text-center">
|
||||
<p class="m-b-0 small muted">Quantity</p>
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
created() {
|
||||
this.product = this.data;
|
||||
this.product.unit_price = (Math.round((this.product.unit_price+ Number.EPSILON) * 1000) / 1000).toFixed(3)
|
||||
this.product.unit_price = (Math.round((this.product.unit_price+ Number.EPSILON) * 1000) / 1000);
|
||||
},
|
||||
computed: {
|
||||
productTotal(){
|
||||
|
||||
@@ -0,0 +1,640 @@
|
||||
<template>
|
||||
<div class="row justify-content-center align-items-center text-center" style="min-height: 80vh;">
|
||||
<div class="col">
|
||||
<h1 class="m-b-50" :class="{ 'text-success': timer }">{{ formattedTime }}</h1>
|
||||
<loading-component style="height: 50px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
|
||||
<div class="row" v-if="question" v-show="!isLoading">
|
||||
<div class="col">
|
||||
|
||||
<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>
|
||||
|
||||
<!-- MAIN QUESTION AND INFO -->
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
<h3>{{questionTitle}}</h3>
|
||||
<h4>{{question.question_description}}</h4>
|
||||
<div v-if="question.question_number === '1688'">
|
||||
<h1>Login Information</h1>
|
||||
<div v-if="externalApiResponse.data.booking"
|
||||
class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
|
||||
<div class="col-auto text-left">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">ORDER Marking: </span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3><a :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">{{ externalApiResponse.data.booking.marking
|
||||
}}</a></h3>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN ID/EMAIL/PHONE:
|
||||
</span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3>{{ externalApiResponse.data.account_no }}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN PASSWORD:
|
||||
</span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3>{{ externalApiResponse.data.holder_name }}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">ALIPAY 6-DIGIT PAYMENT PIN:
|
||||
</span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3>{{ externalApiResponse.data.pin }}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="question.question_number === '1688_login_successful'">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a v-if="externalApiResponse.data.booking" :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">
|
||||
{{ externalApiResponse.data.booking.marking }}
|
||||
</a>
|
||||
</h3>
|
||||
<div v-if="externalApiResponse.data.booking_attributes && externalApiResponse.data.booking_attributes.length > 0" class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
|
||||
<div class="col-auto text-left">
|
||||
<h4>
|
||||
<span class="bold d-inline-block m-r-15">Additional Info: </span>
|
||||
</h4>
|
||||
<div v-for="(attribute, index) in externalApiResponse.data.booking_attributes"
|
||||
:key="index">
|
||||
<h2>
|
||||
{{ index + 1 + '. #' + attribute.value }}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
|
||||
<div class="col-auto text-left">
|
||||
<h4>
|
||||
<span class="bold d-inline-block m-r-15">Additional Info: -</span>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="question.question_number === '1688_order_verify'">
|
||||
<h3 v-if="externalApiResponse.data.booking">
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">
|
||||
{{ externalApiResponse.data.booking.marking }}
|
||||
</a>
|
||||
</h3>
|
||||
<div v-if="externalApiResponse.data.booking" class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
|
||||
<div class="col-auto text-left">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Reference(s): </span>
|
||||
</h3>
|
||||
<div v-for="(attribute, index) in externalApiResponse.data.booking_attributes"
|
||||
:key="index">
|
||||
<h2>
|
||||
{{ index + 1 + '. #' + attribute.value }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-auto text-center">
|
||||
<h4>
|
||||
Total CNY: <!-- {{ attribute.cost }} -->
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="question.question_number === 'approve_po'">
|
||||
<div v-if="externalApiResponse.data.booking" class="row">
|
||||
<div class="col">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">{{ externalApiResponse.data.booking.marking }}</a>
|
||||
</h3>
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<purchase-order-form-read-only-component :data="externalApiResponse.data.booking" :companySegmentIds="companySegmentIds" :section="section"></purchase-order-form-read-only-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="question.question_number === 'fill_po'">
|
||||
<div v-if="externalApiResponse.data.booking" class="row">
|
||||
<div class="col">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">{{ externalApiResponse.data.booking.marking }}</a>
|
||||
</h3>
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<purchase-order-form-read-only-component :data="externalApiResponse.data.booking" :companySegmentIds="companySegmentIds" :section="section"></purchase-order-form-read-only-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="question.question_number === 'approve_po_edit' || question.question_number === 'fill_po_edit'">
|
||||
<div v-if="externalApiResponse.data.booking" class="row">
|
||||
<div class="col">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">{{ externalApiResponse.data.booking.marking }}</a>
|
||||
</h3>
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<purchase-order-form-component :data="externalApiResponse.data.booking" :companySegmentIds="companySegmentIds" :section="section + 'GetExternalApiResponse'"
|
||||
@update-parameters="handleParametersUpdate"></purchase-order-form-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- QUESTION TYPE -->
|
||||
<div class="row m-b-10" v-if="question.question_type === 8">
|
||||
<div class="col">
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<h2>
|
||||
Upload the English PO
|
||||
</h2>
|
||||
<!-- <file-upload-component :data="externalApiResponse.data" section="section"></file-upload-component> -->
|
||||
<file-input-component :validator="$v.files" v-model="files">
|
||||
<template slot="label">
|
||||
<div class="font-heading fs-11 all-caps">Photo or File</div>
|
||||
</template>
|
||||
</file-input-component>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h2>
|
||||
Upload the China PO
|
||||
</h2>
|
||||
<file-input-component :validator="$v.files" v-model="files">
|
||||
<template slot="label">
|
||||
<div class="font-heading fs-11 all-caps">Photo or File</div>
|
||||
</template>
|
||||
<template slot="tips">
|
||||
<div class="row" v-show="false">
|
||||
<div class="col">
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
<div class="font-heading fs-11 text-warning m-b-10">{{ documentType === 0 ? 'IC': 'SSM registration'}} number shown in the photo should tally with {{ documentType === 0 ? 'IC': 'SSM registration'}} number provided above.</div>
|
||||
<div class="font-heading fs-11 muted">Tips on how your photo should look</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</file-input-component>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h2>
|
||||
Upload Bank Slip
|
||||
</h2>
|
||||
<file-input-component :validator="$v.files" v-model="files">
|
||||
<template slot="label">
|
||||
<div class="font-heading fs-11 all-caps">Photo or File</div>
|
||||
</template>
|
||||
<template slot="tips">
|
||||
<div class="row" v-show="false">
|
||||
<div class="col">
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
<div class="font-heading fs-11 text-warning m-b-10">{{ documentType === 0 ? 'IC': 'SSM registration'}} number shown in the photo should tally with {{ documentType === 0 ? 'IC': 'SSM registration'}} number provided above.</div>
|
||||
<div class="font-heading fs-11 muted">Tips on how your photo should look</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</file-input-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-b-10" v-else-if="question.question_type === 7">
|
||||
<div class="col">
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<div class="m-t-25">
|
||||
<!-- <remark-comment-form-component :data="externalApiResponse.data" :id="externalApiResponse.data.booking_id" :section="section" module_type="Booking"></remark-comment-form-component> -->
|
||||
<validation-wrapper-component :validator="$v.answer">
|
||||
<label>Answer</label>
|
||||
<input type="text" class="form-control" v-model="answer" @input="formTouched = true">
|
||||
</validation-wrapper-component>
|
||||
</div>
|
||||
<div class="m-t-25">
|
||||
<!-- <file-upload-component v-model="files" :value="value" v-on:input="$emit('input', $event)"></file-upload-component> -->
|
||||
<file-input-component :validator="$v.files" v-model="files">
|
||||
<template slot="label">
|
||||
<div class="font-heading fs-11 all-caps">Photo or File</div>
|
||||
</template>
|
||||
<template slot="tips">
|
||||
<div class="row" v-show="false">
|
||||
<div class="col">
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
<div class="font-heading fs-11 text-warning m-b-10">{{ documentType === 0 ? 'IC': 'SSM registration'}} number shown in the photo should tally with {{ documentType === 0 ? 'IC': 'SSM registration'}} number provided above.</div>
|
||||
<div class="font-heading fs-11 muted">Tips on how your photo should look</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</file-input-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-b-10" v-else-if="question.question_type === 4" >
|
||||
<div class="col" v-for="ans in question.question_answers">
|
||||
<div v-html="ans.display_text"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-b-15" v-else-if="question.question_type === 3">
|
||||
<div class="col">
|
||||
<file-input-component :validator="$v.files" v-model="files">
|
||||
<template slot="label">
|
||||
<div class="font-heading fs-11 all-caps">Photo or File</div>
|
||||
</template>
|
||||
<template slot="tips">
|
||||
<div class="row" v-show="false">
|
||||
<div class="col">
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
<div class="font-heading fs-11 text-warning m-b-10">{{ documentType === 0 ? 'IC': 'SSM registration'}} number shown in the photo should tally with {{ documentType === 0 ? 'IC': 'SSM registration'}} number provided above.</div>
|
||||
<div class="font-heading fs-11 muted">Tips on how your photo should look</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</file-input-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-b-10" v-else-if="question.question_type === 2">
|
||||
<div class="col" >
|
||||
<validation-wrapper-component :validator="$v.answer">
|
||||
<label>Answer</label>
|
||||
<input type="text" class="form-control" v-model="answer" @input="formTouched = true">
|
||||
</validation-wrapper-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-b-10" v-else-if="question.question_type === 1" >
|
||||
<div class="col question.answers">
|
||||
<div class="w-100 d-block text-center">
|
||||
<validation-wrapper-component :validator="$v.answer" class="col">
|
||||
<button v-for="(ans, index) in question.question_answers" :key="index"
|
||||
:answerValue="ans.value" :answerId=ans.id
|
||||
@click="addClass($event, 'question.answers')"
|
||||
style="margin: 15px; padding: 15px 40px; min-width: 210px;">
|
||||
{{ ans.display_text }}
|
||||
</button>
|
||||
<!-- <div class="col" v-for="ans in question.question_answers">
|
||||
<div class="col question.answers">
|
||||
<div class="btn btn-xs btn-block" :answerValue="ans.value" :answerId=ans.id @click="addClass($event, 'question.answers')">{{ ans.display_text }}</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</validation-wrapper-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- QUESTION NAVIGATION -->
|
||||
<div class="row" v-if="question.question_type !== 0">
|
||||
<div class="col p-r-5">
|
||||
<button class="btn btn-sm btn-default bg-master-lightest btn-block b-rad-none" :disabled="hidePrevious" @click="formTouched = true; submitForm('previous')">Previous</button>
|
||||
</div>
|
||||
<div class="col p-l-5">
|
||||
<button class="btn btn-sm btn-primary btn-block b-rad-none" @click="formTouched = true; submitForm('next')">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-else>
|
||||
<div class="col p-l-5">
|
||||
<button class="btn btn-sm btn-primary btn-block b-rad-none" @click="reloadPage();">Reload Page</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Debug box -->
|
||||
<div class="debug-box" v-if="question">
|
||||
<div id="debug-meta">
|
||||
ID: {{question.question_number}} <br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { required } from "vuelidate/lib/validators";
|
||||
export default {
|
||||
data(){
|
||||
return {
|
||||
section: 'adminWorkFlowForm',
|
||||
showAnswerInput: false,
|
||||
showUploadInput: false,
|
||||
formTouched: false,
|
||||
question: null,
|
||||
answer: '',
|
||||
answerId: 0,
|
||||
direction: 'next',
|
||||
documentType: 1,
|
||||
files: [],
|
||||
error: null,
|
||||
|
||||
// From AdminWorkFlowSectionComponent
|
||||
sessionId: null,
|
||||
externalApiResponse: { data: null },
|
||||
externalApiUrl: "",
|
||||
timer: null,
|
||||
elapsedTime: 0,
|
||||
stepTime: 0,
|
||||
interval: 1000,
|
||||
isFetching: false,
|
||||
}
|
||||
},
|
||||
validations() {
|
||||
return {
|
||||
answer: {
|
||||
required: this.formTouched && this.showAnswerInput ? required : false,
|
||||
},
|
||||
files: {
|
||||
required: this.formTouched && this.showUploadInput ? required : false,
|
||||
}
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.sessionId == null) {
|
||||
this.sessionId = this.generateSessionId();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pendingQueue () {
|
||||
return this.$store.getters.isInCompleteQueue(this.section);
|
||||
},
|
||||
pendingQueueForSecondApiCall () {
|
||||
return this.$store.getters.isInCompleteQueue(this.section+ 'GetExternalApiResponse');
|
||||
},
|
||||
formattedTime() {
|
||||
const hours = String(Math.floor(this.elapsedTime / 3600)).padStart(2, '0');
|
||||
const minutes = String(Math.floor((this.elapsedTime % 3600) / 60)).padStart(2, '0');
|
||||
const seconds = String(this.elapsedTime % 60).padStart(2, '0');
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
},
|
||||
companySegmentIds() {
|
||||
return this.externalApiResponse.data?.booking?.company?.segments?.map(obj => parseInt(obj.id)) ?? [];
|
||||
},
|
||||
hidePrevious(){
|
||||
// return (this.question && this.question.is_start) || (this.question && this.question.is_no_going_back && this.question.is_no_going_back === 1)
|
||||
return (this.question && this.question.question_number === 'node_0') || (this.question && this.question.is_no_going_back && this.question.is_no_going_back === 1)
|
||||
},
|
||||
isLoading(){
|
||||
return this.$store.getters.isLoading(this.section) || this.$store.getters.isLoading(this.section + 'GetExternalApiResponse') || this.isFetching;
|
||||
},
|
||||
questionTitle(){
|
||||
return !this.question.question_title.includes('_') ? this.question.question_title : '';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
pendingQueue(inComplete, oldValue){
|
||||
if(inComplete){
|
||||
this.fetchFirstQuestion();
|
||||
}
|
||||
},
|
||||
pendingQueueForSecondApiCall(inComplete, oldValue){
|
||||
if(inComplete){
|
||||
this.fetchAdditionalQuestionInfo();
|
||||
}
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.$store.dispatch('updateListQueue', {'name': this.section});
|
||||
this.$store.dispatch('updateListQueue', {'name': this.section + 'GetExternalApiResponse'});
|
||||
},
|
||||
methods: {
|
||||
fetchFirstQuestion(){
|
||||
this.submit(route('api.questionnaires.first.question', 0), 'get', this.section, false, false);
|
||||
},
|
||||
fetchAdditionalQuestionInfo(){
|
||||
// console.log('fetchAdditionalQuestionInfo: ', JSON.stringify(this.question));
|
||||
if(this.externalApiUrl){
|
||||
this.isFetching = true;
|
||||
this.externalApiResponse.data = {};
|
||||
this.submit(this.externalApiUrl, 'get', this.section + 'GetExternalApiResponse', false, false);
|
||||
}
|
||||
},
|
||||
successHandler(response, section){
|
||||
if(section === 'adminWorkFlowFormGetExternalApiResponse'){
|
||||
// console.log('adminWorkFlowFormGetExternalApiResponse: ', JSON.stringify(section), ' response: ', JSON.stringify(response));
|
||||
this.$store.dispatch('completeList', {'name': this.section + 'GetExternalApiResponse', 'data': []});
|
||||
if(response.data)
|
||||
{
|
||||
this.externalApiResponse.data = response.data;
|
||||
}
|
||||
else if(response.payload.data)
|
||||
{
|
||||
this.externalApiResponse.data.booking = response.payload.data;
|
||||
}
|
||||
this.isFetching = false;
|
||||
}
|
||||
else{
|
||||
this.$store.dispatch('completeList', {'name': this.section, 'data': []});
|
||||
this.question = response.payload.data;
|
||||
this.reset();
|
||||
this.removeClass('question.answers');
|
||||
|
||||
// if(this.question.answer != null && this.direction == "previous"){
|
||||
// console.log("previous: " + this.question.answer.answer);
|
||||
// this.answer = this.question.answer.answer;
|
||||
// }
|
||||
|
||||
//if there is only 1 answer, select answer by default
|
||||
if(this.question.question_answers.length === 1){
|
||||
this.answer = this.question.question_answers[0].value;
|
||||
this.answerId = this.question.question_answers[0].id;
|
||||
}
|
||||
|
||||
if(this.question.is_previous && this.question.answer){
|
||||
this.externalApiResponse.data = JSON.parse(this.question.answer.question_metadata);
|
||||
}
|
||||
else {
|
||||
if(this.question && this.question.url){
|
||||
let url = this.question.url;
|
||||
if(!this.isURL(this.question.url)){
|
||||
if(this.question.url === 'api.admin_work_flow.fetch_model_attributes'){
|
||||
url = this.route(this.question.url, this.externalApiResponse.data.booking.id);
|
||||
}
|
||||
else if(this.question.url === 'api.booking.show'){
|
||||
url = this.route(this.question.url, this.externalApiResponse.data.booking.marking);
|
||||
}
|
||||
else{
|
||||
url = this.route(this.question.url);
|
||||
}
|
||||
}
|
||||
|
||||
this.externalApiUrl = url;
|
||||
}
|
||||
if(this.question.is_start){
|
||||
this.elapsedTime = 0;
|
||||
}
|
||||
this.fetchAdditionalQuestionInfo();
|
||||
}
|
||||
}
|
||||
},
|
||||
errorHandler(error){
|
||||
this.error = 'We are sorry, something went wrong. Please inform tech team.';
|
||||
this.isFetching = false;
|
||||
},
|
||||
submitForm(direction) {
|
||||
this.parameters = {}
|
||||
this.parameters.question = this.question;
|
||||
this.parameters.answer = this.answer;
|
||||
this.parameters.answerId = this.answerId;
|
||||
this.parameters.answerObj = this.question.question_answers.find(item => item.id === Number(this.answerId));
|
||||
this.parameters.files = this.files;
|
||||
this.parameters.extra = {};
|
||||
|
||||
this.error = null;
|
||||
this.direction = direction;
|
||||
if(this.question.question_type === 3){
|
||||
this.showAnswerInput = false;
|
||||
this.showUploadInput = true;
|
||||
}
|
||||
else if(this.question.question_type === 1 || (this.question.question_type === 2)){
|
||||
this.showAnswerInput = true;
|
||||
this.showUploadInput = false;
|
||||
}
|
||||
else if(this.question.question_type === 7){
|
||||
this.showAnswerInput = true;
|
||||
this.showUploadInput = true;
|
||||
}
|
||||
|
||||
this.parameters.extra.timeUsedSeconds = this.stepTime;
|
||||
this.parameters.extra.questionMetadata = this.externalApiResponse.data;
|
||||
this.parameters.extra.user_id = this.$store.getters.getUserId;
|
||||
this.parameters.extra.session_id = this.sessionId;
|
||||
|
||||
if(direction === 'next'){
|
||||
|
||||
if (this.parameters.answer === 'start_work') this.startWork();
|
||||
if (this.parameters.answer === 'stop_working') this.stopTimer();
|
||||
|
||||
this.submit(this.route('api.questionnaires.next.question'), 'post', this.section, false, false);
|
||||
}
|
||||
else if(direction === 'previous'){
|
||||
this.formTouched = false;
|
||||
this.parameters.isPrevious = true;
|
||||
this.submit(this.route('api.questionnaires.next.question'), 'post', this.section, false, false);
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.parameters = null;
|
||||
this.answer = '';
|
||||
this.answerId = 0;
|
||||
this.files = [];
|
||||
this.formTouched = false;
|
||||
this.showAnswerInput = false;
|
||||
this.showUploadInput = false;
|
||||
this.error = null;
|
||||
this.stepTime = 0;
|
||||
this.externalApiUrl = "";
|
||||
},
|
||||
addClass(event, cls) {
|
||||
const div = event.target;
|
||||
this.answer = div.getAttribute('answerValue');
|
||||
this.answerId = div.getAttribute('answerId');
|
||||
this.removeClass(cls);
|
||||
div.classList.add('btn-primary');
|
||||
},
|
||||
removeClass(cls) {
|
||||
document.getElementsByClassName(cls).forEach(el => {
|
||||
const btnPrimaryEl = el.getElementsByClassName('btn-primary')[0];
|
||||
if (btnPrimaryEl) {
|
||||
btnPrimaryEl.classList.remove('btn-primary');
|
||||
}
|
||||
});
|
||||
},
|
||||
startTimer() {
|
||||
if (!this.timer) {
|
||||
this.timer = setInterval(() => {
|
||||
this.elapsedTime++;
|
||||
this.stepTime++;
|
||||
}, this.interval);
|
||||
} else {
|
||||
console.log("time has already started");
|
||||
}
|
||||
},
|
||||
stopTimer() { clearInterval(this.timer); this.timer = null; },
|
||||
startWork() { this.startTimer(); },
|
||||
endWork() { this.stopTimer(); console.log("work is ended"); },
|
||||
generateSessionId() {
|
||||
return 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
isURL(string) {
|
||||
try {
|
||||
new URL(string);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
reloadPage() {
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
handleParametersUpdate({ parameters }) {
|
||||
console.log('Parameters:', JSON.stringify(parameters));
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.debug-box {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 200px;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,670 @@
|
||||
<template>
|
||||
<div class="row justify-content-center align-items-center text-center" style="min-height: 80vh;">
|
||||
<div class="col">
|
||||
<h1 class="m-b-50" :class="{ 'text-success': timer }">{{ formattedTime }}</h1>
|
||||
<div v-if="isLoading" class="row">
|
||||
<div class="col">
|
||||
<loading-component></loading-component>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row">
|
||||
<div class="col">
|
||||
<div v-if="currentQuestion && Object.keys(currentQuestion).length && !apiFailed">
|
||||
<h2 v-if="currentQuestion.text">{{ currentQuestion.text }}</h2>
|
||||
|
||||
<!-- <p>currentQuestionId - {{ currentQuestionId }}</p> -->
|
||||
<!-- <p>currentOrder - {{ currentOrder? currentOrder.id : 'null' }}</p> -->
|
||||
|
||||
<div v-if="currentQuestionId === '1688'">
|
||||
<h1>Login Information</h1>
|
||||
<div v-if="externalApiResponse.data"
|
||||
class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
|
||||
<div class="col-auto text-left">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">ORDER Marking: </span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3><a :href="route('booking.details', externalApiResponse.data.booking_marking)"
|
||||
target="_blank">{{ externalApiResponse.data.booking_marking
|
||||
}}</a></h3>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN ID/EMAIL/PHONE:
|
||||
</span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3>{{ externalApiResponse.data.account_no }}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">1688 LOGIN PASSWORD:
|
||||
</span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3>{{ externalApiResponse.data.holder_name }}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h3><span class="bold d-inline-block m-r-15">ALIPAY 6-DIGIT PAYMENT PIN:
|
||||
</span></h3>
|
||||
</td>
|
||||
<td>
|
||||
<h3>{{ externalApiResponse.data.pin }}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentQuestionId === 'login_successful'">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">
|
||||
{{ externalApiResponse.data.booking.marking }}
|
||||
</a>
|
||||
</h3>
|
||||
<div v-if="externalApiResponse.data" class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
|
||||
<div class="col-auto text-left">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Reference(s): </span>
|
||||
</h3>
|
||||
<div v-for="(attribute, index) in externalApiResponse.data.booking_attributes"
|
||||
:key="index">
|
||||
<h2>
|
||||
{{ index + 1 + '. #' + attribute.value }}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentQuestionId === 'order_verify'">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.booking.marking)"
|
||||
target="_blank">
|
||||
{{ externalApiResponse.data.booking.marking }}
|
||||
</a>
|
||||
</h3>
|
||||
<div v-if="externalApiResponse.data" class="row bg-master-light m-t-15 m-b-15 rounded padding-30 justify-content-center">
|
||||
<div class="col-auto text-left">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Reference(s): </span>
|
||||
</h3>
|
||||
<div v-for="(attribute, index) in externalApiResponse.data.booking_attributes"
|
||||
:key="index">
|
||||
<h2>
|
||||
{{ index + 1 + '. #' + attribute.value }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-auto text-center">
|
||||
<h4>
|
||||
Total CNY: <!-- {{ attribute.cost }} -->
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentQuestionId === '1688_submit'">
|
||||
<div v-if="externalApiResponse.data" class="row">
|
||||
<div class="col">
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<h2>
|
||||
Upload the English PO
|
||||
</h2>
|
||||
<file-upload-component :data="externalApiResponse.data" section="section"></file-upload-component>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h2>
|
||||
Upload the China PO
|
||||
</h2>
|
||||
<file-upload-component :data="externalApiResponse.data" section="section"></file-upload-component>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h2>
|
||||
Upload Bank Slip
|
||||
</h2>
|
||||
<file-upload-component :data="externalApiResponse.data" section="section"></file-upload-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentQuestionId === 'approve_po'">
|
||||
<div v-if="externalApiResponse.data" class="row">
|
||||
<div class="col">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.marking)"
|
||||
target="_blank">{{ externalApiResponse.data.marking }}</a>
|
||||
</h3>
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<purchase-order-form-component :data="externalApiResponse.data"
|
||||
:section="section"></purchase-order-form-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentQuestionId === 'fill_po'">
|
||||
<div v-if="externalApiResponse.data" class="row">
|
||||
<div class="col">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.marking)"
|
||||
target="_blank">{{ externalApiResponse.data.marking }}</a>
|
||||
</h3>
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<purchase-order-form-component :data="externalApiResponse.data"
|
||||
section="section"></purchase-order-form-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentQuestionId === 'edit_po'">
|
||||
<div v-if="externalApiResponse.data" class="row">
|
||||
<div class="col">
|
||||
<h3>
|
||||
<span class="bold d-inline-block m-r-15">ORDER Marking: </span>
|
||||
<a :href="route('booking.details', externalApiResponse.data.marking)"
|
||||
target="_blank">{{ externalApiResponse.data.marking }}</a>
|
||||
</h3>
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<purchase-order-form-component :data="externalApiResponse.data"
|
||||
section="section"></purchase-order-form-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentQuestionId === '1688_issue_others' || currentQuestionId === 'po_others'">
|
||||
<div v-if="externalApiResponse.data" class="row">
|
||||
<div class="col">
|
||||
<div class="row b-a b-primary padding-30 bg-white m-t-25">
|
||||
<div class="col">
|
||||
<h2>
|
||||
What issues did you encounter?
|
||||
</h2>
|
||||
<div class="m-t-25">
|
||||
<remark-comment-form-component @remark-submitted="handleRemarkSubmitted" :data="externalApiResponse.data" :id="externalApiResponse.data.booking_id" :section="section" module_type="Booking"></remark-comment-form-component>
|
||||
</div>
|
||||
<div class="m-t-25">
|
||||
<file-upload-component v-model="files" :value="value" v-on:input="$emit('input', $event)"></file-upload-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">Api Failed. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="currentQuestion.answers">
|
||||
<div class="col">
|
||||
<div class="w-100 d-block text-center">
|
||||
<button @click="goBack()"
|
||||
v-if="questionIds.length && currentQuestion.show_back_btn != false"
|
||||
class="btn btn-lg btn-default b-a b-grey d-inline-block rounded fs-20"
|
||||
style="margin: 15px; padding: 15px 40px; min-width: 210px;">
|
||||
Go Back
|
||||
</button>
|
||||
<button v-for="(answer, index) in currentQuestion.answers" :key="index"
|
||||
@click="selectAnswer(answer.next)" :data-attr-next-step="answer.next"
|
||||
class="btn btn-lg btn-primary d-inline-block rounded fs-20"
|
||||
:class="getbuttonClass(answer.btn_color)"
|
||||
style="margin: 15px; padding: 15px 40px; min-width: 210px;">
|
||||
{{ answer.text }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="row bg-danger-light m-t-15 m-b-15 rounded padding-30">
|
||||
<div class="col">
|
||||
<h1 class="text-white">
|
||||
"{{ currentQuestionId }}" {{ apiFailed ? 'api Failed' : "is empty" }}
|
||||
. Please contact Tech Support</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-lg btn-primary d-inline-block rounded fs-20"
|
||||
style="margin: 15px; padding: 15px 40px; min-width: 210px;"
|
||||
@click="selectAnswer('refresh_page')">Refresh Page</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
section: 'initial_section',
|
||||
questions: {
|
||||
"node_0": {
|
||||
"text": "Are you ready to work today?",
|
||||
"answers": [
|
||||
{ "text": "Yes", "next": "start_work" },
|
||||
{ "text": "No", "next": "no_work" }
|
||||
]
|
||||
},
|
||||
"start_work": {
|
||||
"text": "What will you work on?",
|
||||
"answers": [
|
||||
{ "text": "1688", "next": "1688" },
|
||||
{ "text": "Approve PO", "next": "approve_po" },
|
||||
{ "text": "Fill PO", "next": "fill_po" }
|
||||
]
|
||||
},
|
||||
"no_work": {
|
||||
"text": "Come back when you are ready to work",
|
||||
"answers": [
|
||||
{ "text": "Refresh Page", "next": "refresh_page" }
|
||||
]
|
||||
},
|
||||
"1688": {
|
||||
"load_api": this.route("api.admin_work_flow.fetch_oldest_order"),
|
||||
"answers": [
|
||||
{ "text": "Login Issue", "next": "login_issue", 'btn_color': "warning" },
|
||||
{ "text": "Login Successful", "next": "login_successful" }
|
||||
],
|
||||
},
|
||||
"login_issue": {
|
||||
"text": "Login Issues",
|
||||
"answers": [
|
||||
{ "text": "Need TAC", "next": "Need Tac" },
|
||||
{ "text": "Wrong login details", "next": "Wrong Login Details" },
|
||||
{ "text": "Others", "next": "1688_issue_others" },
|
||||
{ "text": "Order Cancelled", "next": "refund_request" }
|
||||
]
|
||||
},
|
||||
"login_successful": {
|
||||
"load_api": 'fetch_model_attributes',
|
||||
"answers": [
|
||||
{ "text": "Can't verify order?", "next": "1688_issue_order" },
|
||||
{ "text": "Order Verified", "next": "order_verify" },
|
||||
]
|
||||
},
|
||||
"1688_issue_order": {
|
||||
"answers": [
|
||||
{ "text": "Amount not found", "next": "Amount not found" },
|
||||
{ "text": "Plus Member", "next": "Plus Member" },
|
||||
{ "text": "Others", "next": "Others" },
|
||||
{ "text": "Customer did not verify 1688 account", "next": "Customer did not verify 1688 account" },
|
||||
{ "text": "WorldFirst account linked another account", "next": "WorldFirst account linked another account" },
|
||||
]
|
||||
},
|
||||
"order_verify": {
|
||||
// fetch order amount
|
||||
"load_api": 'fetch_model_attributes',
|
||||
"answers": [
|
||||
{ "text": "Yes", "next": "order_verification" }
|
||||
]
|
||||
},
|
||||
"order_verification": {
|
||||
"text": "Are You Sure this is the correct amount?",
|
||||
"answers": [
|
||||
{ "text": "No", "next": "insufficient_order" },
|
||||
{ "text": "Yes", "next": "proceed_order" }
|
||||
]
|
||||
},
|
||||
"proceed_order": {
|
||||
"text": "Please proceed the order on 1688",
|
||||
"answers": [
|
||||
{ "text": "Got Issue", "next": "1688_order_issue" },
|
||||
{ "text": "Done", "next": "1688_submit" }
|
||||
]
|
||||
},
|
||||
"1688_order_issue": {
|
||||
"answers": [
|
||||
{ "text": "Wrong Pin Number", "next": "Wrong Pin Number" },
|
||||
{ "text": "Not Enough Stock", "next": "Not Enough Stock" },
|
||||
{ "text": "Others", "next": "Others" },
|
||||
{ "text": "No CrossBoarder", "next": "No CrossBoarder" },
|
||||
{ "text": "AngPau", "next": "AngPau" }
|
||||
]
|
||||
},
|
||||
"1688_issue_others": {
|
||||
"text": "Others",
|
||||
"answers": [
|
||||
{ "text": "Submit Issue", "next": "1688_issue_submit"}
|
||||
]
|
||||
},
|
||||
"1688_submit": {
|
||||
"answers": [
|
||||
{ "text": "Stop Working", "next": "stop_working"},
|
||||
{ "text": "Proceed to next order", "next": "1688"}
|
||||
]
|
||||
},
|
||||
"1688_issue_submit": {
|
||||
"text": "Issue has been submitted",
|
||||
"answers": [
|
||||
{ "text": "Stop Working", "next": "stop_working" },
|
||||
{ "text": "Next Order", "next": "1688" }
|
||||
]
|
||||
},
|
||||
"refund_request": {
|
||||
"text": "Refund Request sent",
|
||||
"answers": [
|
||||
{ "text": "Stop Working", "next": "stop_working" },
|
||||
{ "text": "Next PO", "next": "1688" }
|
||||
]
|
||||
},
|
||||
"approve_po": {
|
||||
// "text": "Order Number",
|
||||
"load_api": this.route("api.admin_work_flow.fetch_pending_approve_po"),
|
||||
"answers": [
|
||||
{ "text": "Approve", "next": "po_approved" },
|
||||
{ "text": "Edit PO", "next": "edit_po" },
|
||||
{ "text": "Reject", "next": "reject_po" }
|
||||
]
|
||||
},
|
||||
"fill_po": {
|
||||
// "text": "Order Number",
|
||||
"load_api": this.route("api.admin_work_flow.fetch_pending_fill_po"),
|
||||
"answers": [
|
||||
{ "text": "Edit PO", "next": "edit_po" },
|
||||
]
|
||||
},
|
||||
"po_filled": {
|
||||
"text": "PO Filled",
|
||||
"answers": [
|
||||
{ "text": "Stop Working", "next": "stop_working" },
|
||||
{ "text": "Next PO", "next": "approve_po" }
|
||||
]
|
||||
},
|
||||
"po_approved": {
|
||||
"text": "PO Approved",
|
||||
"answers": [
|
||||
{ "text": "Stop Working", "next": "stop_working" },
|
||||
{ "text": "Next PO", "next": "approve_po" }
|
||||
]
|
||||
},
|
||||
"edit_po": {
|
||||
"answers": [
|
||||
{ "text": "Issue?", "next": "issue_po"},
|
||||
{ "text": "Done", "next": "po_filled" },
|
||||
]
|
||||
},
|
||||
"po_others": {
|
||||
"text": "Others",
|
||||
"answers": [
|
||||
{ "text": "Submit Issue", "next": "issue_submit"}
|
||||
]
|
||||
},
|
||||
"reject_po": {
|
||||
"text": "PO Rejected",
|
||||
"answers": [
|
||||
{ "text": "Issue", "next": "issue_po" },
|
||||
]
|
||||
},
|
||||
"issue_po": {
|
||||
"answers": [
|
||||
{ "text": "Sensitive Goods", "next": "Sensitive Goods" },
|
||||
{ "text": "Others", "next": "PO Others" },
|
||||
// { "text": "Stop Working", "next": "stop_working" },
|
||||
// { "text": "Next PO", "next": "approve_po" }
|
||||
]
|
||||
},
|
||||
"issue_submit": {
|
||||
"text": "Issue has been submitted",
|
||||
"answers": [
|
||||
{ "text": "Stop Working", "next": "stop_working" },
|
||||
{ "text": "Next Order", "next": "approve_po" }
|
||||
]
|
||||
},
|
||||
"stop_working": {
|
||||
"text": "Thank you for your work!",
|
||||
"answers": [
|
||||
{ "text": "Refresh Page", "next": "refresh_page" }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
currentQuestionId: null,
|
||||
questionIds: [],
|
||||
isLoading: false,
|
||||
externalApiResponse: { data: null },
|
||||
timer: null,
|
||||
elapsedTime: 0,
|
||||
stepTime: 0,
|
||||
interval: 1000,
|
||||
sessionId: null,
|
||||
|
||||
currentOrder: null,
|
||||
apiFailed: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentQuestion() { return this.questions[this.currentQuestionId] || {}; },
|
||||
formattedTime() {
|
||||
const hours = String(Math.floor(this.elapsedTime / 3600)).padStart(2, '0');
|
||||
const minutes = String(Math.floor((this.elapsedTime % 3600) / 60)).padStart(2, '0');
|
||||
const seconds = String(this.elapsedTime % 60).padStart(2, '0');
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.currentQuestionId = 'node_0';
|
||||
if (this.sessionId == null) {
|
||||
this.sessionId = this.generateSessionId();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
startTimer() {
|
||||
if (!this.timer) {
|
||||
this.timer = setInterval(() => {
|
||||
this.elapsedTime++;
|
||||
this.stepTime++;
|
||||
}, this.interval);
|
||||
} else {
|
||||
console.log("time has already started");
|
||||
}
|
||||
},
|
||||
stopTimer() { clearInterval(this.timer); this.timer = null; },
|
||||
startWork() { this.startTimer(); },
|
||||
endWork() { this.stopTimer(); console.log("work is ended"); },
|
||||
handleRemarkSubmitted(remarkContent) {
|
||||
this.callApi(this.route('api.admin_work_flow.create_issue_remark'), 'post', 'section', {
|
||||
'booking_id': this.currentOrder.booking_id,
|
||||
'remark': remarkContent,
|
||||
'user_id': this.$store.getters.getUserId,
|
||||
}, false, false);
|
||||
|
||||
alert('Remark has been submitted');
|
||||
},
|
||||
|
||||
selectAnswer(nextQuestionId, goBack = false) {
|
||||
if (nextQuestionId === 'refresh_page') return window.location.reload();
|
||||
if (this.currentQuestionId === 'issue_po') {
|
||||
if (nextQuestionId === 'po_others') {
|
||||
this.callApi(this.route('api.admin_work_flow.create_po_issue_remark'), 'post', 'section', {
|
||||
'booking_id': this.currentOrder.id,
|
||||
'remark': 'PO Others',
|
||||
'user_id': this.$store.getters.getUserId,
|
||||
}, false, false);
|
||||
|
||||
} else {
|
||||
this.callApi(this.route('api.admin_work_flow.create_po_issue_remark'), 'post', 'section', {
|
||||
'booking_id': this.currentOrder.id,
|
||||
'remark': nextQuestionId,
|
||||
'user_id': this.$store.getters.getUserId,
|
||||
}, false, false);
|
||||
nextQuestionId = 'issue_submit';
|
||||
}
|
||||
}
|
||||
|
||||
if (['login_issue', '1688_issue_order', '1688_order_issue'].includes(this.currentQuestionId)) {
|
||||
if (nextQuestionId === 'refund_request') {
|
||||
this.callApi(this.route('api.admin_work_flow.create_issue_remark'), 'post', 'section', {
|
||||
'booking_id': this.currentOrder.booking_id,
|
||||
'remark': nextQuestionId,
|
||||
'user_id': this.$store.getters.getUserId,
|
||||
}, false, false);
|
||||
|
||||
} else if(nextQuestionId === '1688_issue_others') {
|
||||
// this.callApi(this.route('api.admin_work_flow.create_issue_remark'), 'post', 'section', {
|
||||
// 'booking_id': this.currentOrder.booking_id,
|
||||
// 'remark': nextQuestionId,
|
||||
// 'user_id': this.$store.getters.getUserId,
|
||||
// }, false, false);
|
||||
|
||||
} else {
|
||||
this.callApi(this.route('api.admin_work_flow.create_issue_remark'), 'post', 'section', {
|
||||
'booking_id': this.currentOrder.booking_id,
|
||||
'remark': nextQuestionId,
|
||||
'user_id': this.$store.getters.getUserId,
|
||||
}, false, false);
|
||||
nextQuestionId = '1688_issue_submit';
|
||||
}
|
||||
}
|
||||
if (!Object.keys(this.questions[nextQuestionId]).length) this.endWork();
|
||||
if (nextQuestionId === 'start_work') this.startWork();
|
||||
if (nextQuestionId === 'stop_working') this.stopTimer();
|
||||
|
||||
// exclude some node to click back
|
||||
console.log('selectAnswer goBack: ', goBack);
|
||||
if (!['node_0'].includes(this.currentQuestionId) && !goBack) {
|
||||
console.log('exclude some node 1: ', goBack);
|
||||
this.questionIds.push(this.currentQuestionId);
|
||||
this.callLogApi(this.currentQuestionId, nextQuestionId);
|
||||
}
|
||||
if (!['node_0'].includes(this.currentQuestionId) && goBack) {
|
||||
console.log('exclude some node 2: ', goBack);
|
||||
this.callLogApi(this.currentQuestionId, 'goBack');
|
||||
}
|
||||
|
||||
if (this.questions[nextQuestionId]?.load_api) this.callApi(this.questions[nextQuestionId].load_api, 'get', 'section');
|
||||
this.currentQuestionId = nextQuestionId;
|
||||
},
|
||||
callLogApi(currentNode, nextNode) {
|
||||
console.log('callLogApi currentNode: ', currentNode);
|
||||
console.log('callLogApi nextNode: ', nextNode);
|
||||
// call api to recorrd timestamp
|
||||
var logBody = {
|
||||
'current_node': currentNode,
|
||||
'next_node': nextNode,
|
||||
'seconds': this.stepTime,
|
||||
'user_id': this.$store.getters.getUserId,
|
||||
'session_id': this.sessionId,
|
||||
};
|
||||
this.callApi(this.route('api.admin_work_flow.add_workflow_timestamp'), 'post', 'section', logBody, false, false);
|
||||
this.stepTime = 0;
|
||||
},
|
||||
goBack() {
|
||||
var prevId = this.questionIds.pop();
|
||||
this.selectAnswer(prevId, true);
|
||||
},
|
||||
generateSessionId() {
|
||||
return 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
callApi(url, method, section, parameters = null, showLoadingAnimation = true, storeResponseBody = true) {
|
||||
console.log('callApi url: ', url);
|
||||
if (url == 'fetch_model_attributes') {
|
||||
url = this.route("api.admin_work_flow.fetch_model_attributes", this.currentOrder.booking_id)
|
||||
} else if (url == 'fetch_pending_approve_po') {
|
||||
url = this.route("api.admin_work_flow.fetch_pending_approve_po", this.currentOrder)
|
||||
}
|
||||
|
||||
if (showLoadingAnimation) this.isLoading = true;
|
||||
this.$store.dispatch('crudRequest', { endpoint: url, method, parameters: parameters })
|
||||
.then(response => response.json().then(data => ({ data, ok: response.ok, status: response.status })))
|
||||
.then(apiResponse => {
|
||||
if (storeResponseBody) {
|
||||
this.externalApiResponse = apiResponse.data;
|
||||
}
|
||||
|
||||
if (apiResponse.ok) {
|
||||
this.successHandler(apiResponse.data);
|
||||
} else {
|
||||
// Improved error handling
|
||||
this.handleError(apiResponse.data, apiResponse.status);
|
||||
}
|
||||
|
||||
if (showLoadingAnimation) {
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
if ([this.route("api.admin_work_flow.fetch_oldest_order"), this.route("api.admin_work_flow.fetch_pending_approve_po"), this.route("api.admin_work_flow.fetch_pending_fill_po")].includes(url)) {
|
||||
this.currentOrder = apiResponse.data.data
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle network or other unexpected errors
|
||||
console.error('Unexpected error:', error);
|
||||
this.errorHandler({ message: 'An unexpected error occurred. Please try again later.' }, 500);
|
||||
this.apiFailed = true;
|
||||
if (showLoadingAnimation) {
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
getbuttonClass(className) {
|
||||
return 'btn-' + className;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
+1
-1
@@ -41,7 +41,7 @@ export default {
|
||||
}
|
||||
|
||||
successNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'success' }) : null;
|
||||
this.successHandler(response)
|
||||
this.successHandler(response, section);
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@extends('layouts.base_portal')
|
||||
@section('inner_content')
|
||||
<admin-work-flow-form-component></admin-work-flow-form-component>
|
||||
<!-- <admin-work-flow-section-component></admin-work-flow-section-component> -->
|
||||
@endsection
|
||||
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Classes\ValueObjects\Constants\BookingAttributeNames;
|
||||
use App\Http\Resources\BookingResource;
|
||||
use App\Models\Booking;
|
||||
use App\Models\KeyValuePair;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::group(['prefix' => 'admin-work-flow', 'as' => 'admin_work_flow.', 'namespace' => 'AdminWorkFlow'], function () {
|
||||
|
||||
// Generalized JSON response function
|
||||
function jsonResponse($data = null, $message = null, $status = 200)
|
||||
{
|
||||
$response = ['message' => $message];
|
||||
|
||||
if ($data !== null) {
|
||||
$response['data'] = $data;
|
||||
}
|
||||
|
||||
return response()->json($response, $status);
|
||||
}
|
||||
|
||||
|
||||
Route::get('/fetch-oldest-order', function () {
|
||||
$excludedBookingIds = KeyValuePair::where('owner_type', 'App\Models\Booking')
|
||||
->where('key', '1688_admin_workflow_processed')
|
||||
->pluck('owner_id')
|
||||
->filter(function ($value) {
|
||||
return is_numeric($value);
|
||||
})
|
||||
->toArray();
|
||||
|
||||
$booking = Booking::where('service_id', 4)
|
||||
->where('status', ApprovalStatus::APPROVED)
|
||||
->whereNotIn('id', $excludedBookingIds)
|
||||
->first();
|
||||
|
||||
return $booking ? jsonResponse([
|
||||
'passwords' => $booking->bank->holder_name ?? null,
|
||||
'account_no' => $booking->bank->account_no ?? null,
|
||||
'pin' => $booking->bank->bank_branch ?? null,
|
||||
'holder_name' => $booking->bank->holder_name ?? null,
|
||||
'booking' => $booking
|
||||
]) : jsonResponse(null, 'No booking found', 404);
|
||||
})->name('fetch_oldest_order');
|
||||
|
||||
|
||||
Route::get('/fetch-pending-approved-po', function () {
|
||||
$excludedBookingIds = KeyValuePair::where('owner_type', 'App\Models\Booking')
|
||||
->where('key', 'approve_po_admin_workflow_processed')
|
||||
->pluck('owner_id')
|
||||
->filter(function ($value) {
|
||||
return is_numeric($value);
|
||||
})
|
||||
->toArray();
|
||||
|
||||
$booking = Booking::with('transactions')
|
||||
->where('service_id', 4)
|
||||
->where('status', ApprovalStatus::APPROVED)
|
||||
->whereNotIn('id', $excludedBookingIds)
|
||||
->whereHas('transactions', fn ($query) => $query->where('type', TransactionType::PURCHASE_ORDER)->where('status', '<', ApprovalStatus::APPROVED))
|
||||
->first();
|
||||
|
||||
$result = new BookingResource($booking);
|
||||
return jsonResponse([
|
||||
'booking' => $result,
|
||||
]);
|
||||
|
||||
// return $booking ? jsonResponse(new BookingResource($booking)) : jsonResponse(null, 'No booking found', 404);
|
||||
})->name('fetch_pending_approve_po');
|
||||
|
||||
|
||||
|
||||
Route::get('/fetch-pending-fill-po', function () {
|
||||
$excludedBookingIds = KeyValuePair::where('owner_type', 'App\Models\Booking')
|
||||
->where('key', 'fill_po_admin_workflow_processed')
|
||||
->pluck('owner_id')
|
||||
->filter(function ($value) {
|
||||
return is_numeric($value);
|
||||
})
|
||||
->toArray();
|
||||
|
||||
$booking = Booking::with('transactions')
|
||||
->where('service_id', 4)
|
||||
->where('status', ApprovalStatus::APPROVED)
|
||||
->whereNotIn('id', $excludedBookingIds)
|
||||
->whereDoesntHave('transactions', fn ($query) => $query->where('type', TransactionType::PURCHASE_ORDER))
|
||||
->first();
|
||||
|
||||
$result = new BookingResource($booking);
|
||||
return jsonResponse([
|
||||
'booking' => $result,
|
||||
]);
|
||||
|
||||
// return $booking ? jsonResponse(new BookingResource($booking)) : jsonResponse(null, 'No booking found', 404);
|
||||
})->name('fetch_pending_fill_po');
|
||||
|
||||
|
||||
|
||||
Route::get('{booking_id}/fetch-model-attributes', function ($bookingId) {
|
||||
$booking = Booking::find($bookingId);
|
||||
|
||||
if (!$booking) {
|
||||
return jsonResponse(null, 'No approved booking found', 404);
|
||||
}
|
||||
|
||||
$attributes = $booking->modelAttributes()
|
||||
->where('name', BookingAttributeNames::ORDER_REFERENCE_NO)
|
||||
->get(['id', 'value'])
|
||||
->map(fn ($attr) => $attr->only(['id', 'value']));
|
||||
|
||||
return jsonResponse([
|
||||
'booking' => $booking,
|
||||
'booking_attributes' => $attributes,
|
||||
]);
|
||||
})->name('fetch_model_attributes');
|
||||
|
||||
|
||||
// Route::post('/add-workflow-timestamp', function (Request $request) {
|
||||
// $validator = Validator::make($request->all(), [
|
||||
// 'current_node' => 'required|string',
|
||||
// 'next_node' => 'required|string',
|
||||
// 'seconds' => 'required|integer',
|
||||
// 'user_id' => 'required|integer|exists:users,id',
|
||||
// 'session_id' => 'required|string',
|
||||
// ]);
|
||||
|
||||
// if ($validator->fails()) {
|
||||
// throw new RequestValidationException($validator->messages()->first());
|
||||
// }
|
||||
|
||||
// $workflowTimestamp = WorkflowTimestamp::create($validator->validated());
|
||||
|
||||
// return jsonResponse($workflowTimestamp, 'Workflow timestamp created successfully', 201);
|
||||
// })->name('add_workflow_timestamp');
|
||||
|
||||
|
||||
// Route::post('/create-1688-login-issue-remark', function (Request $request) {
|
||||
// $validator = Validator::make($request->all(), [
|
||||
// 'booking_id' => 'required',
|
||||
// 'remark' => 'required',
|
||||
// 'user_id' => 'required',
|
||||
// ]);
|
||||
|
||||
// if ($validator->fails()) {
|
||||
// throw new RequestValidationException($validator->messages()->first());
|
||||
// }
|
||||
|
||||
// $booking = Booking::find($request->booking_id);
|
||||
|
||||
// if (!$booking) {
|
||||
// return jsonResponse(null, 'Booking not found', 404);
|
||||
// }
|
||||
|
||||
// $remark = new Remark([
|
||||
// 'commenter_id' => $request->user_id,
|
||||
// 'content' => $request->remark,
|
||||
// 'owner_type' => get_class($booking),
|
||||
// 'owner_id' => $booking->id,
|
||||
// ]);
|
||||
|
||||
// $remark->save();
|
||||
|
||||
// return jsonResponse($remark, 'Remark created successfully', 201);
|
||||
// })->name('create_1688_login_issue_remark');
|
||||
|
||||
// Route::post('/create_issue_remark', function (Request $request) {
|
||||
// $validator = Validator::make($request->all(), [
|
||||
// 'booking_id' => 'required',
|
||||
// 'remark' => 'required',
|
||||
// 'user_id' => 'required',
|
||||
// ]);
|
||||
|
||||
// if ($validator->fails()) {
|
||||
// throw new RequestValidationException($validator->messages()->first());
|
||||
// }
|
||||
|
||||
// $booking = Booking::find($request->booking_id);
|
||||
|
||||
|
||||
// if (!$booking) {
|
||||
// return jsonResponse(null, 'Booking not found', 404);
|
||||
// }
|
||||
|
||||
// $remark = new Remark([
|
||||
// 'commenter_id' => $request->user_id,
|
||||
// 'content' => $request->remark,
|
||||
// 'owner_type' => get_class($booking),
|
||||
// 'owner_id' => $booking->id,
|
||||
// ]);
|
||||
|
||||
// $remark->save();
|
||||
|
||||
// return jsonResponse($remark, 'Remark created successfully', 201);
|
||||
// })->name('create_issue_remark');
|
||||
|
||||
// Route::post('/create_po_issue_remark', function (Request $request) {
|
||||
// $validator = Validator::make($request->all(), [
|
||||
// 'booking_id' => 'required',
|
||||
// 'remark' => 'required',
|
||||
// 'user_id' => 'required',
|
||||
// ]);
|
||||
|
||||
// if ($validator->fails()) {
|
||||
// return response()->json(['message' => $validator->messages()->first()], 400); // Return validation error
|
||||
// }
|
||||
|
||||
// $booking = Booking::find($request->booking_id);
|
||||
|
||||
// if (!$booking) {
|
||||
// return response()->json(['message' => 'Booking not found'], 404);
|
||||
// }
|
||||
|
||||
// $remark = new Remark([
|
||||
// 'commenter_id' => $request->user_id,
|
||||
// 'content' => $request->remark,
|
||||
// 'owner_type' => get_class($booking),
|
||||
// 'owner_id' => $booking->id,
|
||||
// ]);
|
||||
|
||||
// $remark->save();
|
||||
|
||||
// return response()->json(['data' => $remark, 'message' => 'Remark created successfully'], 201);
|
||||
// })->name('create_po_issue_remark');
|
||||
|
||||
});
|
||||
@@ -71,6 +71,10 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function
|
||||
|
||||
require __DIR__ . '/job.php';
|
||||
|
||||
require __DIR__ . '/questionnaires.php';
|
||||
|
||||
require __DIR__ . '/admin_work_flow.php';
|
||||
|
||||
// require __DIR__ . '/rate.php';
|
||||
// require __DIR__ . '/receipt.php';
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::group(['prefix' => 'questionnaires', 'as' => 'questionnaires.', 'namespace' => 'Questionnaires'], function () {
|
||||
Route::get('/{set_id}', 'FetchQuestionV1AdminWFController@fetch')->name('first.question');
|
||||
Route::post('/question', 'UpdateNextQuestionV1AdminWFController@fetch')->name('next.question');
|
||||
Route::get('/list', 'ListQuestionsAnswersQAController@list')->name('list.questions.answers');
|
||||
});
|
||||
+13
-9
@@ -1183,42 +1183,42 @@ Route::get('/show-white-form-transactions-in-date-range/{from_date}/{to_date}',
|
||||
const fileInput = form.querySelector("input[type=\'file\']");
|
||||
const files = fileInput.files;
|
||||
const userToken = localStorage.getItem("user-token");
|
||||
|
||||
|
||||
if (files.length === 0) {
|
||||
alert("Please select at least one file to upload.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const fileReaders = [];
|
||||
let totalFiles = files.length;
|
||||
|
||||
|
||||
// Create a promise for each file to read it as Base64
|
||||
Array.from(files).forEach((file) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
|
||||
// Create a promise that resolves when the file is read
|
||||
const fileReadPromise = new Promise((resolve, reject) => {
|
||||
reader.onload = (event) => {
|
||||
resolve(event.target.result);
|
||||
};
|
||||
|
||||
|
||||
reader.onerror = (error) => {
|
||||
reject(error);
|
||||
};
|
||||
|
||||
|
||||
reader.readAsDataURL(file); // Read the file as Data URL (Base64)
|
||||
});
|
||||
|
||||
|
||||
fileReaders.push(fileReadPromise);
|
||||
});
|
||||
|
||||
|
||||
// Wait for all files to be read
|
||||
Promise.all(fileReaders)
|
||||
.then((fileData) => {
|
||||
const payload = {
|
||||
files: fileData, // Array of objects with name, type, and Base64 content
|
||||
};
|
||||
|
||||
|
||||
// Send the payload as JSON
|
||||
return fetch(`/api/v1/transactions/${billId}/bill/verification`, {
|
||||
method: "POST",
|
||||
@@ -1339,3 +1339,7 @@ Route::get('/site.webmanifest', function () {
|
||||
$contents = File::get($filePath);
|
||||
return response($contents, 200)->header('Content-Type', 'application/manifest+json');
|
||||
});
|
||||
|
||||
Route::get('/admin-work-flow', function () {
|
||||
return view('pages.dashboards.admin_work_flow');
|
||||
})->name('admin-work-flow');
|
||||
|
||||
Reference in New Issue
Block a user