Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3a703 admin edit submission #305

Merged
merged 8 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions app/Http/Controllers/Forms/FormSubmissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
use App\Http\Resources\FormSubmissionResource;
use App\Models\Forms\Form;
use App\Exports\FormSubmissionExport;
use App\Http\Requests\AnswerFormRequest;
use App\Jobs\Form\StoreFormSubmissionJob;
use App\Models\Forms\FormSubmission;
use App\Service\Forms\FormSubmissionFormatter;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
Expand All @@ -27,6 +31,20 @@ public function submissions(string $id)
return FormSubmissionResource::collection($form->submissions()->paginate(100));
}

public function update(AnswerFormRequest $request, $id, $submissionId)
{
$form = $request->form;
$this->authorize('update', $form);
$job = new StoreFormSubmissionJob($request->form, $request->validated());
$job->setSubmissionId($submissionId)->handle();

$data = new FormSubmissionResource(FormSubmission::findOrFail($submissionId));
return $this->success([
'message' => 'Record successfully updated.',
'data' => $data
]);
}

public function export(string $id)
{
$form = Form::findOrFail((int) $id);
Expand Down
26 changes: 26 additions & 0 deletions app/Http/Middleware/Form/ResolveFormMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Http\Middleware\Form;

use App\Models\Forms\Form;
use Closure;
use Illuminate\Http\Request;

class ResolveFormMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, string $routeParamName = "id")
{
$form = Form::where($routeParamName,$request->route($routeParamName))->firstOrFail();
$request->merge([
'form' => $form,
]);
return $next($request);
}
}
9 changes: 9 additions & 0 deletions app/Jobs/Form/StoreFormSubmissionJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public function getSubmissionId()
return $this->submissionId;
}

public function setSubmissionId(int $id)
{
$this->submissionId = $id;
return $this;
}

private function storeSubmission(array $formData)
{
// Create or update record
Expand All @@ -76,6 +82,9 @@ private function storeSubmission(array $formData)
*/
private function submissionToUpdate(): ?FormSubmission
{
if($this->submissionId){
return $this->form->submissions()->findOrFail($this->submissionId);
}
if ($this->form->editable_submissions && isset($this->submissionData['submission_id']) && $this->submissionData['submission_id']) {
$submissionId = $this->submissionData['submission_id'] ? Hashids::decode($this->submissionData['submission_id']) : false;
$submissionId = $submissionId[0] ?? null;
Expand Down
42 changes: 42 additions & 0 deletions client/components/open/components/EditSubmissionModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<template>
<modal :show="show" max-width="lg" @close="emit('close')">
<open-form :theme="theme" :loading="false" :show-hidden="true" :form="form" :fields="form.properties" @submit="updateForm" :default-data-form="submission">
<template #submit-btn="{submitForm}">
<v-button :loading="loading" class="mt-2 px-8 mx-1" @click.prevent="submitForm">
Update Submission
</v-button>
</template>
</open-form>
</modal>
</template>
<script setup>
import {ref, defineProps, defineEmits, onMounted } from 'vue'
import OpenForm from '../forms/OpenForm.vue';
import { themes } from '~/lib/forms/form-themes.js'
const props = defineProps({
show: { type: Boolean, required: true },
form: { type: Object, required: true },
theme:{type:Object, default:themes.default},
submission:{type:Object}
})

let loading = ref(false)

const emit = defineEmits(['close', 'updated'])
const updateForm = (form, onFailure) =>{
loading.value = true
form.put('/open/forms/' + props.form.id + '/submissions/'+props.submission.id).then((res) => {
useAlert().success(res.message)
loading.value = false
emit('close')
emit('updated', res.data.data)

}).catch((error) => {
console.error(error)
loading.value = false
onFailure()
})

}

</script>
22 changes: 17 additions & 5 deletions client/components/open/components/RecordOperations.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<template>
<div class="flex items-center justify-center space-x-1">
<button v-track.delete_record_click
class="border rounded py-1 px-2 text-gray-500 dark:text-gray-400 hover:text-blue-700"
@click="showEditSubmissionModal=true"
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125" />
</svg>
</button>
<button v-track.delete_record_click
class="border rounded py-1 px-2 text-gray-500 dark:text-gray-400 hover:text-red-700"
@click="onDeleteClick"
Expand All @@ -13,12 +21,15 @@
</svg>
</button>
</div>
<EditSubmissionModal :show="showEditSubmissionModal" :form="form" :submission="submission" @close="showEditSubmissionModal=false" @updated="(submission)=>$emit('updated', submission)"/>
</template>

<script>
import EditSubmissionModal from './EditSubmissionModal.vue'

export default {
components: { },
components: { EditSubmissionModal },
emits: ["updated", "deleted"],
props: {
form: {
type: Object,
Expand All @@ -28,8 +39,8 @@ export default {
type: Array,
default: () => []
},
rowid: {
type: Number,
submission: {
type: Object,
default: () => {}
}
},
Expand All @@ -40,6 +51,7 @@ export default {
},
data () {
return {
showEditSubmissionModal:false,
}
},
computed: {
Expand All @@ -51,9 +63,9 @@ export default {
this.useAlert.confirm('Do you really want to delete this record?', this.deleteRecord)
},
async deleteRecord () {
opnFetch('/open/forms/' + this.form.id + '/records/' + this.rowid + '/delete', {method:'DELETE'}).then(async (data) => {
opnFetch('/open/forms/' + this.form.id + '/records/' + this.submission.id + '/delete', {method:'DELETE'}).then(async (data) => {
if (data.type === 'success') {
this.$emit('deleted')
this.$emit('deleted',this.submission)
this.useAlert.success(data.message)
} else {
this.useAlert.error('Something went wrong!')
Expand Down
5 changes: 1 addition & 4 deletions client/components/open/forms/OpenCompleteForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,7 @@ export default {
window.parent.postMessage(payload, '*')
}
window.postMessage(payload, '*')

try {
this.pendingSubmission.remove()
} catch (e) {}
this.pendingSubmission.remove()

if (data.redirect && data.redirect_url) {
window.location.href = data.redirect_url
Expand Down
10 changes: 8 additions & 2 deletions client/components/open/forms/OpenForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export default {
type: Array,
required: true
},
defaultDataForm:{},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of the defaultDataForm prop with an empty object as the default value is a good practice for Vue components, ensuring that the prop has a default state if not provided by the parent component. However, it's important to specify the type of the prop for better type checking and to avoid potential issues with unexpected prop types.

-    defaultDataForm:{},
+    defaultDataForm: {
+      type: Object,
+      default: () => ({})
+    },

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
defaultDataForm:{},
defaultDataForm: {
type: Object,
default: () => ({})
},

adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
},

Expand Down Expand Up @@ -297,12 +298,17 @@ export default {
}
await this.recordsStore.loadRecord(
opnFetch('/forms/' + this.form.slug + '/submissions/' + this.form.submission_id).then((data) => {
return { submission_id: this.form.submission_id, ...data.data }
return { submission_id: this.form.submission_id, id: this.form.submission_id,...data.data }
})
)
return this.recordsStore.getById(this.form.submission_id)
return this.recordsStore.getByKey(this.form.submission_id)
},
async initForm () {
if(this.defaultDataForm){
this.dataForm = useForm(this.defaultDataForm)
return;
}

if (this.isPublicFormPage && this.form.editable_submissions) {
const urlParam = new URLSearchParams(window.location.search)
if (urlParam && urlParam.get('submission_id')) {
Expand Down
70 changes: 33 additions & 37 deletions client/components/open/forms/components/FormSubmissions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
</div>
</modal>

<Loader v-if="!form || !formInitDone" class="h-6 w-6 text-nt-blue mx-auto"/>
<Loader v-if="!form" class="h-6 w-6 text-nt-blue mx-auto"/>
<div v-else>
<div v-if="form && tableData.length > 0" class="flex flex-wrap items-end">
<div class="flex-grow">
Expand Down Expand Up @@ -89,7 +89,8 @@
:data="filteredData"
:loading="isLoading"
@resize="dataChanged()"
@deleted="onDeleteRecord()"
@deleted="onDeleteRecord"
@updated="(submission)=>onUpdateRecord(submission)"
@update-columns="onColumnUpdated"
/>
</scroll-shadow>
Expand All @@ -102,7 +103,6 @@ import Fuse from 'fuse.js'
import clonedeep from 'clone-deep'
import VSwitch from '../../../forms/components/VSwitch.vue'
import OpenTable from '../../tables/OpenTable.vue'
import {now} from "@vueuse/core";

export default {
name: 'FormSubmissions',
Expand All @@ -111,17 +111,19 @@ export default {

setup() {
const workingFormStore = useWorkingFormStore()
const recordStore = useRecordsStore()
return {
workingFormStore,
runtimeConfig: useRuntimeConfig()
recordStore,
form: storeToRefs(workingFormStore).content,
tableData:storeToRefs(recordStore).getAll,
runtimeConfig: useRuntimeConfig(),
slug: useRoute().params.slug
}
},

data() {
return {
formInitDone: false,
isLoading: false,
tableData: [],
currentPage: 1,
fullyLoaded: false,
showColumnsModal: false,
Expand All @@ -134,20 +136,15 @@ export default {
}
},
computed: {
form: {
get() {
return this.workingFormStore.content
},
set(value) {
this.workingFormStore.set(value)
}
},
exportUrl() {
if (!this.form) {
return ''
}
return this.runtimeConfig.public.apiBase + '/open/forms/' + this.form.id + '/submissions/export'
},
isLoading(){
return this.recordStore.loading
},
filteredData() {
if (!this.tableData) return []

Expand All @@ -169,23 +166,22 @@ export default {
},
watch: {
'form.id'() {
if (this.form === null) {
return
}
this.initFormStructure()
this.getSubmissionsData()
this.onFormChange()
}
},
mounted() {
this.initFormStructure()
this.getSubmissionsData()
this.onFormChange()
},
methods: {
initFormStructure() {
if (!this.form || !this.form.properties || this.formInitDone) {
onFormChange() {
if (this.form === null || this.form.slug !== this.slug) {
return
}

this.fullyLoaded = false
this.initFormStructure()
this.getSubmissionsData()
},
initFormStructure() {
// check if form properties already has a created_at column
this.properties = clonedeep(this.form.properties)
if (!this.properties.find((property) => {
Expand All @@ -201,7 +197,6 @@ export default {
width: 140
})
}
this.formInitDone = true
this.removed_properties = (this.form.removed_properties) ? clonedeep(this.form.removed_properties) : []

// Get display columns from local storage
Expand All @@ -216,24 +211,22 @@ export default {
}
},
getSubmissionsData() {
if (!this.form || this.fullyLoaded) {
if (this.fullyLoaded) {
return
}
this.isLoading = true
this.recordStore.startLoading()
opnFetch('/open/forms/' + this.form.id + '/submissions?page=' + this.currentPage).then((resData) => {
this.tableData = this.tableData.concat(resData.data.map((record) => record.data))
this.recordStore.save(resData.data.map((record) => record.data))
this.dataChanged()

if (this.currentPage < resData.meta.last_page) {
this.currentPage += 1
this.getSubmissionsData()
} else {
this.isLoading = false
this.recordStore.stopLoading()
this.fullyLoaded = true
}
}).catch((error) => {
console.error(error)
this.isLoading = false
this.recordStore.startLoading()
})
},
dataChanged() {
Expand All @@ -252,10 +245,13 @@ export default {
return this.displayColumns[field.id] === true
})
},
onDeleteRecord() {
this.fullyLoaded = false
this.tableData = []
this.getSubmissionsData()
onUpdateRecord(submission){
this.recordStore.save(submission);
this.dataChanged()
},
onDeleteRecord(submission) {
this.recordStore.remove(submission);
this.dataChanged()
},
downloadAsCsv() {
opnFetch(this.exportUrl, {responseType: "blob"})
Expand Down
Loading
Loading