diff --git a/app/Http/Controllers/Forms/FormSubmissionController.php b/app/Http/Controllers/Forms/FormSubmissionController.php
index 2eec417..1c4e007 100644
--- a/app/Http/Controllers/Forms/FormSubmissionController.php
+++ b/app/Http/Controllers/Forms/FormSubmissionController.php
@@ -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);
diff --git a/app/Http/Middleware/Form/ResolveFormMiddleware.php b/app/Http/Middleware/Form/ResolveFormMiddleware.php
new file mode 100644
index 0000000..b2a6875
--- /dev/null
+++ b/app/Http/Middleware/Form/ResolveFormMiddleware.php
@@ -0,0 +1,26 @@
+route($routeParamName))->firstOrFail();
+ $request->merge([
+ 'form' => $form,
+ ]);
+ return $next($request);
+ }
+}
diff --git a/app/Jobs/Form/StoreFormSubmissionJob.php b/app/Jobs/Form/StoreFormSubmissionJob.php
index 198bb2c..871ba9a 100644
--- a/app/Jobs/Form/StoreFormSubmissionJob.php
+++ b/app/Jobs/Form/StoreFormSubmissionJob.php
@@ -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;
diff --git a/client/components/open/components/EditSubmissionModal.vue b/client/components/open/components/EditSubmissionModal.vue
new file mode 100644
index 0000000..746ec2d
--- /dev/null
+++ b/client/components/open/components/EditSubmissionModal.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+ Update Submission
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/components/open/components/RecordOperations.vue b/client/components/open/components/RecordOperations.vue
index 19c79d9..75d52bc 100644
--- a/client/components/open/components/RecordOperations.vue
+++ b/client/components/open/components/RecordOperations.vue
@@ -1,5 +1,13 @@
+ $emit('updated', submission)"/>
diff --git a/client/stores/records.js b/client/stores/records.js
index b5d7c1f..7dde38f 100644
--- a/client/stores/records.js
+++ b/client/stores/records.js
@@ -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()
+ })
}
-})
\ No newline at end of file
+ return {...contentStore, loadRecord}
+
+})
diff --git a/routes/api.php b/routes/api.php
index eaa87d6..9ae138f 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -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')
diff --git a/tests/Feature/Submissions/EditSubmissionTest.php b/tests/Feature/Submissions/EditSubmissionTest.php
new file mode 100644
index 0000000..5bf2b0f
--- /dev/null
+++ b/tests/Feature/Submissions/EditSubmissionTest.php
@@ -0,0 +1,61 @@
+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);
+});
+
+