Admin Workflow - Admin UI to search and export data

This commit is contained in:
Dillon Ngo
2025-01-09 02:33:06 +08:00
parent b096b3f93a
commit eab335d34c
18 changed files with 632 additions and 103 deletions
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class AnswerLike implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('answer', 'like', '%' . $value . '%')
->orWhereHas('answer', function ($subquery) use($value){
$subquery->where('display_text', 'like', '%' . $value . '%');
});
}
}
@@ -2,9 +2,8 @@
namespace App\Classes\General\Eloquent\Filters;
use App\Classes\ValueObjects\Constants\QuestionGroup;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Builder;
class WithAnswers implements Filter
{
@@ -16,12 +15,7 @@ class WithAnswers implements Filter
public static function apply(Builder $builder, $value)
{
return $builder->where(function ($query) use($value){
if($value){
$query->where('answer', '=', $value);
}
else{
$query->whereIn('answer', QuestionGroup::QUESTION_GROUPS);
}
$query->whereIn('answer', $value);
});
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Classes\Modules\Exports\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Exports\Services\ExportsQAWithGroups;
use App\Classes\Modules\Exports\Standards\Rules\CanExportQuestionsAnswers;
use Illuminate\Http\Request;
use App\Classes\General\AWSS3Helper;
use App\Classes\General\Helper;
use Illuminate\Support\Facades\Storage;
use Illuminate\Http\JsonResponse;
class ExportQAWithGroupsLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Export Questions & Answers',
'message' => 'You have successfully exported data'
];
}
/** @var ExportsQAWithGroups */
private $exportsQAWithGroups;
/** @var CanExportQuestionsAnswers */
private $canExport;
/**
* ExportQAWithGroupsLogic constructor.
* @param ExportsQAWithGroups $exportsQAWithGroups
* @param CanExportQuestionsAnswers $canExport
*/
public function __construct(ExportsQAWithGroups $exportsQAWithGroups, CanExportQuestionsAnswers $canExport)
{
$this->exportsQAWithGroups = $exportsQAWithGroups;
$this->canExport = $canExport;
}
/**
* @param Request $request
* @return Response
*/
public function logic(Request $request) : JsonResponse
{
$this->canExport->passes();
$this->exportsQAWithGroups->setFilters(Helper::deserializeFilters($request->input('filters')));
$exportFileName = 'qas.xls';
$filesystemDriver = Storage::getDefaultDriver();
if($filesystemDriver === 's3'){
return $this->response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $this->exportsQAWithGroups) ]);
}
return $this->response([ 'src' => null ]);
}
}
@@ -4,7 +4,7 @@ namespace App\Classes\Modules\Exports\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Exports\Services\ExportsQuestionsAnswers;
use App\Classes\Modules\Exports\Services\ExportsQAWithoutGroups;
use App\Classes\Modules\Exports\Standards\Rules\CanExportQuestionsAnswers;
use Illuminate\Http\Request;
use App\Classes\General\AWSS3Helper;
@@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Storage;
use Illuminate\Http\JsonResponse;
class ExportQuestionsAnswersLogic extends AbstractControllerLogic
class ExportQAWithoutGroupsLogic extends AbstractControllerLogic
{
/**
* @return array
@@ -25,20 +25,20 @@ class ExportQuestionsAnswersLogic extends AbstractControllerLogic
];
}
/** @var ExportsQuestionsAnswers */
private $exportsQuestionsAnswers;
/** @var ExportsQAWithoutGroups */
private $exportsQAWithoutGroups;
/** @var CanExportQuestionsAnswers */
private $canExport;
/**
* ExportFeedbackDataLogic constructor.
* @param ExportsQuestionsAnswers $exportsQuestionsAnswers
* ExportQAWithoutGroupsLogic constructor.
* @param ExportsQAWithoutGroups $exportsQAWithoutGroups
* @param CanExportQuestionsAnswers $canExport
*/
public function __construct(ExportsQuestionsAnswers $exportsQuestionsAnswers, CanExportQuestionsAnswers $canExport)
public function __construct(ExportsQAWithoutGroups $exportsQAWithoutGroups, CanExportQuestionsAnswers $canExport)
{
$this->exportsQuestionsAnswers = $exportsQuestionsAnswers;
$this->exportsQAWithoutGroups = $exportsQAWithoutGroups;
$this->canExport = $canExport;
}
@@ -51,15 +51,14 @@ class ExportQuestionsAnswersLogic extends AbstractControllerLogic
{
$this->canExport->passes();
$this->exportsQuestionsAnswers->setFilters(Helper::deserializeFilters($request->input('filters')));
$this->exportsQAWithoutGroups->setFilters(Helper::deserializeFilters($request->input('filters')));
$exportFileName = 'qas.xls';
$filesystemDriver = Storage::getDefaultDriver();
if($filesystemDriver === 's3'){
return $this->response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $this->exportsQuestionsAnswers) ]);
return $this->response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $this->exportsQAWithoutGroups) ]);
}
return $this->response([ 'src' => null ]);
}
}
@@ -15,7 +15,7 @@ use App\Classes\General\Eloquent\ApplyFiltersToQuery;
use App\Classes\ValueObjects\Constants\QuestionGroup;
use Carbon\Carbon;
class ExportsQuestionsAnswers implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
class ExportsQAWithGroups implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
{
use Exportable;
@@ -63,6 +63,7 @@ class ExportsQuestionsAnswers implements FromQuery, WithHeadings, WithHeadingRow
$source = $userAnswer->userSource;
$user = $userAnswer->source_id === 0 ? $userAnswer->user : null;
$answer = $userAnswer->answer()->first();
$questionGroups = explode(',', $userAnswer->question->questionnaire->group);
$rows = [[
$userAnswer->id,
$userAnswer->question->questionnaire->version,
@@ -78,7 +79,7 @@ class ExportsQuestionsAnswers implements FromQuery, WithHeadings, WithHeadingRow
$nextId = QAUserAnswerSelected::where('id', '>', $userAnswer->id)
->whereIn('answer', QuestionGroup::QUESTION_GROUPS)
->whereIn('answer', $questionGroups)
->orderBy('id', 'asc')
->limit(1)
->value('id');
@@ -0,0 +1,80 @@
<?php
namespace App\Classes\Modules\Exports\Services;
use App\Models\QAUserAnswerSelected;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use App\Classes\General\Eloquent\ApplyFiltersToQuery;
use Carbon\Carbon;
class ExportsQAWithoutGroups implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
{
use Exportable;
private $filters;
public function headings(): array
{
return [
'Id',
'Version',
'Question Set',
'Question Text',
'Answer Text',
'Answer Value',
'Source Email',
'Time(Seconds)',
'Created Date',
'MAIN?'
];
}
public function setFilters(array $filters = []): void
{
$this->filters = $filters;
}
/**
* @return \Illuminate\Support\Collection|mixed
*/
public function query()
{
$data = (new ApplyFiltersToQuery())->execute(QAUserAnswerSelected::query(), $this->filters, true);
return $data;
}
/**
* @param QAUserAnswerSelected $userAnswer
*
* @return array
*/
public function map($userAnswer): array
{
$source = $userAnswer->userSource;
$user = $userAnswer->source_id === 0 ? $userAnswer->user : null;
$answer = $userAnswer->answer()->first();
$questionGroups = explode(',', $userAnswer->question->questionnaire->group);
$rows = [[
$userAnswer->id,
$userAnswer->question->questionnaire->version,
$userAnswer->question->questionnaire->description,
$userAnswer->question->question_title,
$answer ? $answer->display_text : null,
$answer ? $answer->value : null,
$user ? $user->email : $source->email,
$userAnswer->time_used_seconds,
Carbon::parse($userAnswer->created_at)->format('d-m-Y'),
$answer ? in_array($answer->value, $questionGroups) : false,
]];
return $rows;
}
}
@@ -4,19 +4,19 @@ namespace App\Classes\ValueObjects\Constants;
final class QuestionGroup {
public const _1688 = '1688';
public const APPROVE_PO = 'approve_po';
public const FILL_PO = 'fill_po';
// public const _1688 = '1688';
// public const APPROVE_PO = 'approve_po';
// public const FILL_PO = 'fill_po';
const OPTIONS_QUESTION_GROUP = [
['text' => '1688', 'id' => QuestionGroup::_1688],
['text' => 'approve_po', 'id' => QuestionGroup::APPROVE_PO],
['text' => 'fill_po', 'id' => QuestionGroup::FILL_PO],
];
// const OPTIONS_QUESTION_GROUP = [
// ['text' => '1688', 'id' => QuestionGroup::_1688],
// ['text' => 'approve_po', 'id' => QuestionGroup::APPROVE_PO],
// ['text' => 'fill_po', 'id' => QuestionGroup::FILL_PO],
// ];
const QUESTION_GROUPS = [
QuestionGroup::_1688,
QuestionGroup::APPROVE_PO,
QuestionGroup::FILL_PO,
];
// const QUESTION_GROUPS = [
// QuestionGroup::_1688,
// QuestionGroup::APPROVE_PO,
// QuestionGroup::FILL_PO,
// ];
}
@@ -3,7 +3,8 @@
namespace App\Http\Controllers\Exports;
use App\Classes\Modules\Exports\ControllersLogic\ExportQuestionsAnswersLogic;
use App\Classes\Modules\Exports\ControllersLogic\ExportQAWithGroupsLogic;
use App\Classes\Modules\Exports\ControllersLogic\ExportQAWithoutGroupsLogic;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Illuminate\Http\JsonResponse;
@@ -12,11 +13,21 @@ class ExportQuestionsAnswersController
{
/**
* @param Request $request
* @param ExportQuestionsAnswersLogic $logic
* @param ExportQAWithGroupsLogic $logic
* @return BinaryFileResponse
* @return JsonResponse
*/
public function export(Request $request, ExportQuestionsAnswersLogic $logic) {
public function exportWithGroups(Request $request, ExportQAWithGroupsLogic $logic) {
return $logic->execute($request);
}
/**
* @param Request $request
* @param ExportQAWithoutGroupsLogic $logic
* @return BinaryFileResponse
* @return JsonResponse
*/
public function exportWithoutGroups(Request $request, ExportQAWithoutGroupsLogic $logic) {
return $logic->execute($request);
}
}
@@ -19,7 +19,7 @@ class QuestionnaireSetsResource extends JsonResource
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'group' => $this->group,
'group' => explode(',', $this->group),
'version' => $this->version,
];
}
@@ -4,7 +4,6 @@ namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\QuestionGroup;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Models\QAAnswerOptions;
use App\Models\QAQuestions;
@@ -24,17 +23,13 @@ class QuestionsAnswersResource extends JsonResource
$source = new UserSourceResource($this->userSource);
$user = $this->source_id === 0 ? new UserResource($this->user) : null;
$answerOption = QAAnswerOptions::where('id', $this->answer_option_id)->first();
$questionGroups = explode(',', $this->question->questionnaire->group);
$user_marking = '';
// if($user){
// $companyModule = $user->companyModule()->first();
// $user_marking = $companyModule ? $companyModule->inviters()->withPivot('invitee_reference')->first()->pivot->invitee_reference : "";
// }
return [
'id' => $this->id,
'question_id' => $this->question_id,
'question_groups' => QuestionGroup::QUESTION_GROUPS,
'question_groups' => $questionGroups,
'questionnaire' => new QuestionnaireSetsResource($this->question->questionnaire),
'question_title' => $question ? $question->question_title : null,
'question_type' => $question ? $question->question_type : 0,
+1 -1
View File
@@ -22,7 +22,7 @@ class QAWorkFlowSeeder extends Seeder
[
'name' => 'Admin Work Flow',
'description' => 'Admin Work Flow',
'group' => 'workflow',
'group' => '1688,approve_po,fill_po',
'version' => 1,
'questions' => [
[
@@ -0,0 +1,172 @@
<template>
<div class="row parentContainer m-b-10">
<div class="col p-l-0">
<div class="row m-l-15 m-r-5 p-t-10 b-a align-items-center pointer shadow bg-white rounded b-white">
<div class="col-12">
<div class="row m-b-10">
<div class="col-2">{{ item.id }} <br> {{ item.created_at }} <br> {{ item.source_email }}</div>
<div class="col-1">{{ item.questionnaire.version }} <br> {{ item.questionnaire.description }}</div>
<div class="col-2">{{ item.question_title }}</div>
<div class="col-1">{{ parsedAnswer === 'go_back' ? '' : item.answer }}</div>
<div class="col-2">{{ parsedAnswer }}</div>
<div class="col-1" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div v-if="item.documents != null" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div
v-for="file in item.documents.files"
v-bind:key="file.id"
class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
</div>
</div>
</div>
<div class="col-1">{{ item.time }}</div>
<div class="col-1">
<a :href="route('booking.details', JSON.parse(item.question_metadata).marking)" target="_blank" v-if="JSON.parse(item.question_metadata)">
{{ JSON.parse(item.question_metadata).marking }}
</a>
</div>
<div class="col parentContainer position-static">
<div class="row align-items-center justify-content-end">
<!-- <div class="col-auto position-static no-padding requestModal" data-type="deleteBillGroup">
<div class="btn bg-grey no-border">
<i class="fa fa-times text-danger"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="deleteBillGroup">
<general-confirmation-form-component
contentText="Are you sure you want to delete this Bill Group?"
modalType="delete"
class="text-center"
:apiRoute="route('api.transaction.group.bill.delete', item.id)"
apiMethod="delete"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div> -->
<div class="col-auto no-padding" v-show="showExpandedIcon">
<div class="btn btn-sm btn-default b-rad-none no-border" @click="expand()">
<i class="fa fa-download" :class="[{'fa-angle-up': expanded}, {'fa-angle-down': !expanded}]"></i>
</div>
</div>
<div class="col-auto no-padding" v-if="loading && item.question_groups.includes(item.answer_value)">
<div class="btn btn-sm btn-default b-rad-none no-border">
<span class="spinner"></span>
</div>
</div>
</div>
</div>
</div>
<div class="row m-b-10" v-show="expanded" v-if="item.question_groups.includes(item.answer_value)">
<div class="col">
<div class="row">
<div class="col">
<div class="row align-items-center m-t-10 p-t-10 p-b-10 b-t b-grey muted all-caps fs-10">
<div class="col-1">Id</div>
<div class="col-2">Question Text</div>
<div class="col-1">Order/Booking/Transfer</div>
<div class="col-1">Answer Text</div>
<div class="col-2">Answer Value</div>
<div class="col-1">Documents Uploaded</div>
<div class="col-1">Time(seconds)</div>
</div>
</div>
</div>
<list-component style="min-height: auto;" :section="section" :endpoint="route('api.questionnaires.qa.list')" :options="options">
<template slot="list" slot-scope="{data}">
<question-answer-inner-component :data="data"></question-answer-inner-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
data(){
return {
section: 'qaListQuestions-' + this.data.reference + '-' + this.data.id,
expanded: false,
options: {
'order_by': {column: 'created_at', DESC: false},
'per_page': 20,
},
showExpandedIcon: false,
loading: true
}
},
watch: {
pendingQueue(inComplete){
if(!inComplete){
this.loading = false;
}
},
expandedList(newValue){
if (Array.isArray(newValue)) {
if(newValue.length > 0){
this.showExpandedIcon = true;
}
}
}
},
computed: {
expandedList () {
return this.$store.getters.getListData(this.section);
},
pendingQueue() {
return this.$store.getters.isInCompleteQueue(this.section);
},
parsedAnswer() {
try {
const parsed = this.item && this.item.answer_value ? JSON.parse(this.item.answer_value) : null;
return parsed && parsed.text ? parsed.text : this.item.answer_value;
} catch (error) {
return this.item.answer_value;
}
},
},
created(){
this.options['reference'] = this.item.reference;
this.options['id_after'] = [this.item.id, this.item.question_groups];
},
methods: {
expand(){
this.expanded = !this.expanded;
},
},
mixins: [componentHandler]
}
</script>
<style scoped>
.spinner {
border: 2px solid rgba(0, 0, 0, 0.1);
border-left-color: #000;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
</style>
@@ -1,7 +1,7 @@
<template>
<div class="row parentContainer m-b-10">
<div class="col p-l-0">
<div class="row m-l-15 p-t-10 b-a align-items-center pointer shadow bg-white rounded b-white">
<div class="row m-l-15 m-r-5 p-t-10 b-a align-items-center pointer shadow bg-white rounded b-white">
<div class="col-12">
<div class="row m-b-10">
<div class="col-1">{{ item.id }}</div>
@@ -75,7 +75,7 @@
export default {
data(){
return {
section: 'listQuestionAnswerInner-' + this.data.reference + '-' + this.data.id,
section: 'qaListQuestionGroups-' + this.data.reference + '-' + this.data.id,
expanded: false,
options: {
'order_by': {column: 'created_at', DESC: false},
@@ -1,45 +1,39 @@
<template>
<div class="row parentContainer">
<div class="col-1">{{ item.id }}</div>
<div class="col-2">{{ item.question_title }}</div>
<div class="col-1">
<a :href="route('booking.details', JSON.parse(item.question_metadata).marking)"
target="_blank">{{ JSON.parse(item.question_metadata).marking }}
</a>
</div>
<div class="col-1">{{ parsedAnswer === 'go_back' ? '' : item.answer }}</div>
<div class="col-2">{{ parsedAnswer }}</div>
<div class="col-1" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div v-if="item.documents != null" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div
v-for="file in item.documents.files"
v-bind:key="file.id"
class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
<div class="row parentContainer m-b-10">
<div class="col">
<div class="row m-l-0 m-r-0 p-t-10 b-a align-items-center pointer shadow bg-white rounded b-white">
<div class="col-12">
<div class="row m-b-10">
<div class="col-1">{{ item.id }}</div>
<div class="col-2">{{ item.question_title }}</div>
<div class="col-1">
<a :href="route('booking.details', JSON.parse(item.question_metadata).marking)" target="_blank" v-if="JSON.parse(item.question_metadata)">
{{ JSON.parse(item.question_metadata).marking }}
</a>
</div>
<div class="col-1">{{ parsedAnswer === 'go_back' ? '' : item.answer }}</div>
<div class="col-2">{{ parsedAnswer }}</div>
<div class="col-1" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div v-if="item.documents != null" style="display: flex; flex-wrap: nowrap; gap: 5px;">
<div
v-for="file in item.documents.files"
v-bind:key="file.id"
class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
</div>
</div>
</div>
<div class="col-1">{{ item.time }}</div>
</div>
</div>
</div>
</div>
<div class="col-1">{{ item.time }}</div>
<!-- <div class="col-1">
<div v-if="item.documents != null">
<div v-for="file in item.documents.files" v-bind:key="file.id" class="col-auto no-padding m-r-5">
<document-file-viewer-component :file="file">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-download"></i>
</button>
</template>
</document-file-viewer-component>
</div>
</div>
</div> -->
</div>
</template>
<script>
@@ -0,0 +1,154 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div class="row">
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterQuestionnaireSet">
<label>Questionnaire Set Version</label>
<selectable-component :endpoint="route('api.questionnaires.list')" :section="section + 'QuestionnaireSet'" valueColumn="id" :labelColumn="['name', 'version']" v-model="filterQuestionnaireSet"></selectable-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component selectable :validator="$v.filterUserId">
<label>Users</label>
<selectable-component :endpoint="route('api.account.user.list') + '?filters=' + JSON.stringify(userListOptions)" :section="section + 'UserList'" valueColumn="id" :labelColumn="['name', 'email']" v-model="filterUserId"></selectable-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterAnswerText">
<label class="all-caps">Answer Text</label>
<input type="text" class="form-control" v-model.lazy="filterAnswerText">
</validation-wrapper-component>
</div>
</div>
<div class="row m-t-10">
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterStartDate">
<label class="all-caps">Start Date</label>
<date-picker-component v-model.lazy="filterStartDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-md-4">
<validation-wrapper-component :validator="$v.filterEndDate">
<label class="all-caps">End Date</label>
<date-picker-component v-model.lazy="filterEndDate"></date-picker-component>
</validation-wrapper-component>
</div>
</div>
<div class="row justify-content-end m-t-10">
<div class="col-auto p-l-0">
<open-link-in-new-tab-component custom-class="btn btn-sm b-rad-none fs-14" :url="route('api.questionnaires.qa.export.without.groups') + '?filters=' + JSON.stringify(this.options)" :is-url-protected="true" :onClick="exportAction">
<i class="fa fa-download lh-40"></i> Download
</open-link-in-new-tab-component>
</div>
<div class="col-auto p-l-0">
<div class="btn b-rad-none" @click="resetSarch">
<i class="fa fa-refresh lh-40"></i> Clear
</div>
</div>
<div class="col-auto p-l-0">
<div class="btn btn-primary b-rad-none" @click="onSelected">
<i class="fa fa-search lh-40"></i> Search
</div>
</div>
</div>
<div class="row align-items-center m-t-10 p-t-10 p-b-10 b-t b-grey muted all-caps fs-10">
<div class="col-2">Id</div>
<div class="col-1">Version/Question Set</div>
<div class="col-2">Question Text</div>
<div class="col-1">Answer Text</div>
<div class="col-2">Answer Value</div>
<div class="col-1">Documents Uploaded</div>
<div class="col-1">Time(seconds)</div>
<div class="col-1">Order/Booking/Transfer</div>
</div>
</div>
<list-component style="min-height: 600px;" :key="currentKey" :section="section + 'ListQuestions'" :endpoint="route('api.questionnaires.qa.list')" :options="options">
<template slot="list" slot-scope="{data}">
<question-answer-2-component :key="data.id" :data="data"></question-answer-2-component>
</template>
</list-component>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
export default {
props: {
section:{
type: String,
required: true
},
},
data(){
return {
filterUserId: "",
filterQuestionnaireSet: "",
filterStartDate: "",
filterEndDate: "",
filterAnswerText: "",
userListOptions: {
'type_in': this.$store.getters.isSuperAdmin ? [1, 2] : [2],
},
options: {
'per_page': 10,
'order_by': {column: 'created_at', DESC: true},
},
currentKey: 1,
}
},
validations: {
filterUserId: {},
filterQuestionnaireSet: {},
filterStartDate: {},
filterEndDate: {},
filterAnswerText: {},
},
methods: {
onSelected(userId){
this.updateOptions();
this.currentKey+=1;
},
updateOptions(){
if(this.filterUserId){
this.options['user_id'] = this.filterUserId;
}
if(this.filterQuestionnaireSet){
this.options['has_questionnaire'] = this.filterQuestionnaireSet;
}
if(this.filterStartDate){
this.options['start_date'] = this.filterStartDate;
}
if(this.filterEndDate){
this.options['end_date'] = this.filterEndDate;
}
if(this.filterAnswerText){
this.options['answer_like'] = this.filterAnswerText;
}
},
exportAction(){
this.updateOptions();
return route('api.questionnaires.qa.export.without.groups') + '?filters=' + JSON.stringify(this.options);
},
resetSarch() {
this.filterUserId = "",
this.filterQuestionGroup = "",
this.filterQuestionnaireSet = "",
this.filterStartDate = "",
this.filterEndDate = "",
this.filterAnswerText = "",
delete this.options['user_id'];
delete this.options['has_questionnaire'];
delete this.options['start_date'];
delete this.options['end_date'];
delete this.options['answer_like'];
this.currentKey+=1;
},
},
}
</script>
@@ -40,7 +40,7 @@
</div>
<div class="row justify-content-end m-t-10">
<div class="col-auto p-l-0">
<open-link-in-new-tab-component custom-class="btn btn-sm b-rad-none fs-14" :url="route('api.questionnaires.qa.export') + '?filters=' + JSON.stringify(this.options)" :is-url-protected="true" :onClick="exportAction">
<open-link-in-new-tab-component custom-class="btn btn-sm b-rad-none fs-14" :url="route('api.questionnaires.qa.export.with.groups') + '?filters=' + JSON.stringify(this.options)" :is-url-protected="true" :onClick="exportAction">
<i class="fa fa-download lh-40"></i> Download
</open-link-in-new-tab-component>
</div>
@@ -66,7 +66,7 @@
<div class="col-1">Created Date</div>
</div>
</div>
<list-component style="min-height: 600px;" :key="currentKey" :section="'list' + section" :endpoint="route('api.questionnaires.qa.list')" :options="options">
<list-component style="min-height: 600px;" :key="currentKey" :section="section + 'ListQuestionGroups'" :endpoint="route('api.questionnaires.qa.list')" :options="options" v-if="questionGroups.length > 0">
<template slot="list" slot-scope="{data}">
<question-answer-component :key="data.id" :data="data"></question-answer-component>
</template>
@@ -84,10 +84,6 @@
type: String,
required: true
},
questionGroups: {
type: Array,
required: true
},
},
data(){
return {
@@ -102,13 +98,14 @@
options: {
'per_page': 10,
'order_by': {column: 'created_at', DESC: true},
'with_answers': "",
'with_answers': [],
},
currentKey: 1,
questionGroups: [],
}
},
created(){
this.submit(route('api.company.list') + '?filters=' + JSON.stringify({'business_type': 3, 'status_in': [1, 2, 0]}), 'get', 'transactionGroupsListPaymentSection', false, false)
this.fetchQuestionnaireSets();
},
validations: {
filterUserId: {},
@@ -118,17 +115,23 @@
filterEndDate: {},
},
methods: {
fetchQuestionnaireSets(){
this.submit(route('api.questionnaires.list') +'?filters=' + JSON.stringify({ 'per_page': 10, order_by: {column: 'id', DESC: true} }), 'get', this.section, false, false)
},
successHandler(response){
this.questionGroups = response.payload.data[0].group;
this.options.with_answers = this.questionGroups;
},
onSelected(userId){
this.updateOptions();
this.currentKey+=1;
// this.updateList();
},
updateOptions(){
if(this.filterUserId){
this.options['user_id'] = this.filterUserId;
}
if(this.filterQuestionGroup){
this.options['with_answers'] = this.filterQuestionGroup;
this.options['with_answers'] = [this.filterQuestionGroup];
}
if(this.filterQuestionnaireSet){
this.options['has_questionnaire'] = this.filterQuestionnaireSet;
@@ -142,7 +145,7 @@
},
exportAction(){
this.updateOptions();
return route('api.questionnaires.qa.export') + '?filters=' + JSON.stringify(this.options);
return route('api.questionnaires.qa.export.with.groups') + '?filters=' + JSON.stringify(this.options);
},
resetSarch() {
this.filterUserId = "",
@@ -150,16 +153,13 @@
this.filterQuestionnaireSet = "",
this.filterStartDate = "",
this.filterEndDate = "",
this.options['with_answers'] = "";
this.options['with_answers'] = this.questionGroups;
delete this.options['user_id'];
delete this.options['has_questionnaire'];
delete this.options['start_date'];
delete this.options['end_date'];
this.currentKey+=1;
},
// updateList(){
// this.$refs.questionsAnswersList.updateFilters({'per_page': 10, 'has_questionnaire_group_in':['feedback'], 'user_id': this.filterUserId, order_by: {column: 'created_at', DESC: false}});
// },
},
}
</script>
@@ -8,7 +8,49 @@
<div class="row d-none" :class="[{'d-flex': $store.getters.isAdmin}]" v-if="$store.getters.isAdmin">
<div class="col no-padding">
<question-answer-section-component section="questionsAnswersSection" :question-groups="{{ json_encode(\App\Classes\ValueObjects\Constants\QuestionGroup::OPTIONS_QUESTION_GROUP) }}"></question-answer-section-component>
<div class="row tabsContainer">
<div class="col">
<div class="row m-l-0 m-r-0 d-flex">
<div class="col m-r-5">
<div class="row fs-12 text-center">
<div class="col p-t-20 p-b-20 bg-master-lighter active tabButton" tab-name="questionGroups">
<div class="row">
<div class="col">
<div class="fs-12 m-t-5 all-caps">Question Groups</div>
</div>
</div>
</div>
</div>
</div>
<div class="col m-r-5">
<div class="row fs-12 text-center">
<div class="col p-t-20 p-b-20 bg-master-lighter tabButton" tab-name="questions">
<div class="row">
<div class="col">
<div class="fs-12 m-t-5 all-caps">Questions</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row no-margin">
<div class="col bg-white padding-25">
<div class="row tabsContainer tabContent padding-25" tab-name="questionGroups">
<div class="col">
<question-answer-section-component section="qaQuestionGroupsSection"></question-answer-section-component>
</div>
</div>
<div class="row tabsContainer hide tabContent padding-25" tab-name="questions">
<div class="col">
<question-answer-section-2-component section="qaQuestionsWithoutGroupSection"></question-answer-section-component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
+2 -1
View File
@@ -9,6 +9,7 @@ Route::group(['prefix' => 'questionnaires', 'as' => 'questionnaires.', 'namespac
Route::group(['prefix' => 'qa', 'as' => 'qa.'], function () {
Route::get('/list', 'ListQuestionsAnswersController@list')->name('list');
Route::get('/export', '\\App\\Http\\Controllers\\Exports\\ExportQuestionsAnswersController@export')->name('export');
Route::get('/export1', '\\App\\Http\\Controllers\\Exports\\ExportQuestionsAnswersController@exportWithGroups')->name('export.with.groups');
Route::get('/export2', '\\App\\Http\\Controllers\\Exports\\ExportQuestionsAnswersController@exportWithoutGroups')->name('export.without.groups');
});
});