3a703 admin edit submission (#305)
* wip: admin submission edit feature * wip: refresh form submission after update * wip: connect submissions page data to store * Fixed the submission loading issue * test: admin edit submission feature test * Fix pending submission, editabe submission & more (#306) --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
a426f091c1
commit
ef83ffcf77
|
@ -6,7 +6,11 @@ use App\Http\Controllers\Controller;
|
|||
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;
|
||||
|
@ -27,6 +31,20 @@ class FormSubmissionController extends Controller
|
|||
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -56,6 +56,12 @@ class StoreFormSubmissionJob implements ShouldQueue
|
|||
return $this->submissionId;
|
||||
}
|
||||
|
||||
public function setSubmissionId(int $id)
|
||||
{
|
||||
$this->submissionId = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function storeSubmission(array $formData)
|
||||
{
|
||||
// Create or update record
|
||||
|
@ -76,6 +82,9 @@ class StoreFormSubmissionJob implements ShouldQueue
|
|||
*/
|
||||
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;
|
||||
|
|
|
@ -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>
|
|
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -28,8 +39,8 @@ export default {
|
|||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
rowid: {
|
||||
type: Number,
|
||||
submission: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
|
@ -40,6 +51,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
showEditSubmissionModal:false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -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!')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -93,6 +93,7 @@ export default {
|
|||
type: Array,
|
||||
required: true
|
||||
},
|
||||
defaultDataForm:{},
|
||||
adminPreview: { type: Boolean, default: false } // If used in FormEditorPreview
|
||||
},
|
||||
|
||||
|
@ -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')) {
|
||||
|
|
|
@ -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">
|
||||
|
@ -89,7 +89,8 @@
|
|||
:data="filteredData"
|
||||
:loading="isLoading"
|
||||
@resize="dataChanged()"
|
||||
@deleted="onDeleteRecord()"
|
||||
@deleted="onDeleteRecord"
|
||||
@updated="(submission)=>onUpdateRecord(submission)"
|
||||
@update-columns="onColumnUpdated"
|
||||
/>
|
||||
</scroll-shadow>
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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 []
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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"})
|
||||
|
|
|
@ -53,7 +53,9 @@
|
|||
<td v-if="hasActions" class="n-table-cell border-gray-100 dark:border-gray-900 text-sm p-2 border-b"
|
||||
style="width: 100px"
|
||||
>
|
||||
<record-operations :form="form" :structure="columns" :rowid="row.id" @deleted="$emit('deleted')"/>
|
||||
<record-operations :form="form" :structure="columns" :submission="row"
|
||||
@deleted="(submission)=>$emit('deleted',submission)"
|
||||
@updated="(submission)=>$emit('updated', submission)"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="loading" class="n-table-row border-t bg-gray-50 dark:bg-gray-900">
|
||||
|
@ -89,6 +91,7 @@ import {hash} from "~/lib/utils.js";
|
|||
|
||||
export default {
|
||||
components: {ResizableTh, RecordOperations},
|
||||
emits: ["updated", "deleted"],
|
||||
props: {
|
||||
columns: {
|
||||
type: Array,
|
||||
|
|
|
@ -13,7 +13,7 @@ export const pendingSubmission = (form) => {
|
|||
|
||||
const set = (value) => {
|
||||
if (process.server || !enabled.value) return
|
||||
useStorage(formPendingSubmissionKey.value).value = JSON.stringify(value)
|
||||
useStorage(formPendingSubmissionKey.value).value = value === null ? value : JSON.stringify(value)
|
||||
}
|
||||
|
||||
const remove = () => {
|
||||
|
@ -29,6 +29,7 @@ export const pendingSubmission = (form) => {
|
|||
return {
|
||||
enabled,
|
||||
set,
|
||||
get
|
||||
get,
|
||||
remove
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@ const workspacesStore = useWorkspacesStore()
|
|||
|
||||
const slug = useRoute().params.slug
|
||||
|
||||
formsStore.startLoading()
|
||||
const user = computed(() => authStore.user)
|
||||
const form = computed(() => formsStore.getByKey(slug))
|
||||
const workspace = computed(() => workspacesStore.getByKey(form?.value?.workspace_id))
|
||||
|
|
|
@ -18,4 +18,9 @@ useOpnSeoMeta({
|
|||
title: (props.form) ? 'Form Submissions - ' + props.form.title : 'Form Submissions'
|
||||
})
|
||||
|
||||
onBeforeRouteLeave(()=>{
|
||||
console.log('Clearing store state')
|
||||
useRecordsStore().resetState()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,49 +1,21 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export const namespaced = true
|
||||
import { useContentStore } from '~/composables/stores/useContentStore'
|
||||
|
||||
/**
|
||||
* Loads records from database
|
||||
*/
|
||||
export const useRecordsStore = defineStore('records', {
|
||||
state: () => ({
|
||||
content: [],
|
||||
loading: false
|
||||
}),
|
||||
getters: {
|
||||
getById: (state) => (id) => {
|
||||
if (state.content.length === 0) return null
|
||||
return state.content.find(item => item.submission_id === id)
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
set (items) {
|
||||
this.content = items
|
||||
},
|
||||
addOrUpdate (item) {
|
||||
this.content = this.content.filter((val) => val.id !== item.id)
|
||||
this.content.push(item)
|
||||
},
|
||||
remove (itemId) {
|
||||
this.content = this.content.filter((val) => val.id !== itemId)
|
||||
},
|
||||
startLoading () {
|
||||
this.loading = true
|
||||
},
|
||||
stopLoading () {
|
||||
this.loading = false
|
||||
},
|
||||
resetState () {
|
||||
this.set([])
|
||||
this.stopLoading()
|
||||
},
|
||||
loadRecord (request) {
|
||||
this.set([])
|
||||
this.startLoading()
|
||||
return request.then((data) => {
|
||||
this.addOrUpdate(data)
|
||||
this.stopLoading()
|
||||
})
|
||||
}
|
||||
export const useRecordsStore = defineStore('records', ()=>{
|
||||
|
||||
const contentStore = useContentStore()
|
||||
|
||||
const loadRecord = (request)=> {
|
||||
contentStore.resetState()
|
||||
contentStore.startLoading()
|
||||
return request.then((data) => {
|
||||
contentStore.save(data)
|
||||
contentStore.stopLoading()
|
||||
})
|
||||
}
|
||||
})
|
||||
return {...contentStore, loadRecord}
|
||||
|
||||
})
|
||||
|
|
|
@ -18,6 +18,7 @@ use App\Http\Controllers\Forms\RecordController;
|
|||
use App\Http\Controllers\WorkspaceController;
|
||||
use App\Http\Controllers\TemplateController;
|
||||
use App\Http\Controllers\Forms\Integration\FormZapierWebhookController;
|
||||
use App\Http\Middleware\Form\ResolveFormMiddleware;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
@ -85,6 +86,7 @@ Route::group(['middleware' => 'auth:api'], function () {
|
|||
Route::delete('/{id}', [FormController::class, 'destroy'])->name('destroy');
|
||||
|
||||
Route::get('/{id}/submissions', [FormSubmissionController::class, 'submissions'])->name('submissions');
|
||||
Route::put('/{id}/submissions/{submission_id}', [FormSubmissionController::class, 'update'])->name('submissions.update')->middleware([ResolveFormMiddleware::class]);
|
||||
Route::get('/{id}/submissions/export', [FormSubmissionController::class, 'export'])->name('submissions.export');
|
||||
Route::get('/{id}/submissions/file/{filename}', [FormSubmissionController::class, 'submissionFile'])
|
||||
->middleware('signed')
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
use Tests\Helpers\FormSubmissionDataFactory;
|
||||
|
||||
it('can update form submission', function () {
|
||||
$user = $this->actingAsUser();
|
||||
$workspace = $this->createUserWorkspace($user);
|
||||
$form = $this->makeForm($user, $workspace);
|
||||
$form = $this->createForm($user, $workspace, [
|
||||
'closes_at' => \Carbon\Carbon::now()->addDays(1)->toDateTimeString(),
|
||||
]);
|
||||
$formData = FormSubmissionDataFactory::generateSubmissionData($form, ['text' => 'John']);
|
||||
$textFieldId = array_keys($formData)[0];
|
||||
$updatedFormData = $formData;
|
||||
$updatedFormTextValue = "Updated text";
|
||||
$updatedFormData[$textFieldId] = $updatedFormTextValue;
|
||||
$this->postJson(route('forms.answer', $form->slug), $formData)
|
||||
->assertSuccessful()
|
||||
->assertJson([
|
||||
'type' => 'success',
|
||||
'message' => 'Form submission saved.'
|
||||
]);
|
||||
$submission = $form->submissions()->first();
|
||||
$updateResponse = $this->putJson(route('open.forms.submissions.update', ['id'=>$form->id, 'submission_id' => $submission->id]), $updatedFormData)
|
||||
->assertSuccessful()
|
||||
->assertJson([
|
||||
'type' => 'success',
|
||||
'message' => 'Record successfully updated.'
|
||||
]);
|
||||
$expectedTextString = $updateResponse->json('data')['data'][$textFieldId];
|
||||
expect($expectedTextString)->toBe($updatedFormTextValue);
|
||||
$updatedSubmission = $form->submissions()->first();
|
||||
expect($updatedSubmission->data[$textFieldId])->toBe($updatedFormTextValue);
|
||||
});
|
||||
|
||||
it('cannot update form submission as non admin', function () {
|
||||
$secondUser =$this->createUser();
|
||||
$user = $this->actingAsUser();
|
||||
$workspace = $this->createUserWorkspace($user);
|
||||
$form = $this->makeForm($user, $workspace);
|
||||
$form = $this->createForm($user, $workspace, [
|
||||
'closes_at' => \Carbon\Carbon::now()->addDays(1)->toDateTimeString(),
|
||||
]);
|
||||
$formData = FormSubmissionDataFactory::generateSubmissionData($form, ['text' => 'John']);
|
||||
$textFieldId = array_keys($formData)[0];
|
||||
$updatedFormData = $formData;
|
||||
$updatedFormTextValue = "Updated text";
|
||||
$updatedFormData[$textFieldId] = $updatedFormTextValue;
|
||||
$this->postJson(route('forms.answer', $form->slug), $formData)
|
||||
->assertSuccessful()
|
||||
->assertJson([
|
||||
'type' => 'success',
|
||||
'message' => 'Form submission saved.'
|
||||
]);
|
||||
$submission = $form->submissions()->first();
|
||||
$this->actingAs($secondUser);
|
||||
$updateResponse = $this->putJson(route('open.forms.submissions.update', ['id'=>$form->id, 'submission_id' => $submission->id]), $updatedFormData)
|
||||
->assertStatus(403);
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue