Add ability to edit forms and add new fields
This commit adds the ability to edit and modify existing forms. On the technical side, it also adds a new reusable validation Facade which helps reduce duplicated code.
This commit is contained in:
parent
1f50faaea7
commit
bca6020ab0
|
@ -4,6 +4,7 @@
|
||||||
/public/storage
|
/public/storage
|
||||||
/storage/*.key
|
/storage/*.key
|
||||||
/vendor
|
/vendor
|
||||||
|
/tools
|
||||||
.env
|
.env
|
||||||
.env.backup
|
.env.backup
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phive xmlns="https://phar.io/phive">
|
||||||
|
<phar name="phpunit" version="^9.2.5" installed="9.2.5" location="./tools/phpunit" copy="false"/>
|
||||||
|
<phar name="php-cs-fixer" version="^2.16.4" installed="2.16.4" location="./tools/php-cs-fixer" copy="false"/>
|
||||||
|
</phive>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Facades;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
class ContextAwareValidation extends Facade
|
||||||
|
{
|
||||||
|
|
||||||
|
protected static function getFacadeAccessor()
|
||||||
|
{
|
||||||
|
return 'contextAwareValidator';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
class ContextAwareValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The getValidator() method will take an array of fields from the request body, iterates through them,
|
||||||
|
* and dynamically adds validation rules for them. Depending on parameters, it may or may not generate
|
||||||
|
* a form structure for rendering purposes.
|
||||||
|
*
|
||||||
|
* This method is mostly meant by internal use by means of static proxies (Facades), in order to reduce code repetition;
|
||||||
|
* Using it outside it's directed scope may cause unexpected results; For instance, the method expects inputs to be in array format, e.g. myFieldNameID1[],
|
||||||
|
* myFieldNameID2[], and so on and so forth.
|
||||||
|
*
|
||||||
|
* This isn't checked by the code yet, but if you're implementing it this way in the HTML markup, make sure it's consistent (e.g. use a loop).
|
||||||
|
*
|
||||||
|
* P.S This method automatically ignores the CSRF token for validation.
|
||||||
|
*
|
||||||
|
* @param array $fields The request form fields
|
||||||
|
* @param bool $generateStructure Whether to incldue a JSON-ready form structure for rendering
|
||||||
|
* @param bool $includeFormName Whether to include formName in the list of validation rules
|
||||||
|
* @return Validator|Collection A validator instance you can use to check for validity, or a Collection with a validator and structure (validator, structure)
|
||||||
|
*/
|
||||||
|
public function getValidator(array $fields, bool $generateStructure = false, bool $includeFormName = false)
|
||||||
|
{
|
||||||
|
$formStructure = [];
|
||||||
|
$validator = [];
|
||||||
|
|
||||||
|
$excludedNames = [
|
||||||
|
'_token',
|
||||||
|
'_method',
|
||||||
|
'formName'
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($includeFormName)
|
||||||
|
{
|
||||||
|
$validator['formName'] = 'required|string|max:100';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($fields as $fieldName => $field)
|
||||||
|
{
|
||||||
|
if(!in_array($fieldName, $excludedNames))
|
||||||
|
{
|
||||||
|
$validator[$fieldName . ".0"] = 'required|string';
|
||||||
|
$validator[$fieldName . ".1"] = 'required|string';
|
||||||
|
|
||||||
|
if ($generateStructure)
|
||||||
|
{
|
||||||
|
$formStructure['fields'][$fieldName]['title'] = $field[0];
|
||||||
|
$formStructure['fields'][$fieldName]['type'] = $field[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$validatorInstance = Validator::make($fields, $validator);
|
||||||
|
|
||||||
|
return ($generateStructure) ?
|
||||||
|
collect([
|
||||||
|
'validator' => $validatorInstance,
|
||||||
|
'structure' => json_encode($formStructure)
|
||||||
|
])
|
||||||
|
: $validatorInstance;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
use ContextAwareValidator;
|
||||||
|
|
||||||
class FormController extends Controller
|
class FormController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -29,39 +31,17 @@ class FormController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->authorize('create', Form::class);
|
$this->authorize('create', Form::class);
|
||||||
|
$fields = $request->all();
|
||||||
|
|
||||||
$formFields = $request->all();
|
$contextValidation = ContextAwareValidator::getValidator($fields, true, true);
|
||||||
|
|
||||||
$formStructure = [];
|
if (!$contextValidation->get('validator')->fails())
|
||||||
$excludedNames = [
|
|
||||||
'_token',
|
|
||||||
'formName' // It's added outside the loop. Not excluding causes unwanted duplication.
|
|
||||||
];
|
|
||||||
$validator = [
|
|
||||||
'formName' => 'required|string|max:100'
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($formFields as $fieldName => $field)
|
|
||||||
{
|
{
|
||||||
if(!in_array($fieldName, $excludedNames))
|
$storableFormStructure = $contextValidation->get('structure');
|
||||||
{
|
|
||||||
$validator[$fieldName . ".0"] = 'required|string';
|
|
||||||
$validator[$fieldName . ".1"] = 'required|string';
|
|
||||||
|
|
||||||
$formStructure['fields'][$fieldName]['title'] = $field[0];
|
|
||||||
$formStructure['fields'][$fieldName]['type'] = $field[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$validation = Validator::make($formFields, $validator);
|
|
||||||
|
|
||||||
if (!$validation->fails())
|
|
||||||
{
|
|
||||||
$storableFormStructure = json_encode($formStructure);
|
|
||||||
|
|
||||||
Form::create(
|
Form::create(
|
||||||
[
|
[
|
||||||
'formName' => $formFields['formName'],
|
'formName' => $fields['formName'],
|
||||||
'formStructure' => $storableFormStructure,
|
'formStructure' => $storableFormStructure,
|
||||||
'formStatus' => 'ACTIVE'
|
'formStatus' => 'ACTIVE'
|
||||||
]
|
]
|
||||||
|
@ -71,7 +51,7 @@ class FormController extends Controller
|
||||||
return redirect()->to(route('showForms'));
|
return redirect()->to(route('showForms'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$request->session()->flash('errors', $validation->errors()->getMessages());
|
$request->session()->flash('errors', $contextValidation->get('validator')->errors()->getMessages());
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +87,41 @@ class FormController extends Controller
|
||||||
{
|
{
|
||||||
return view('dashboard.administration.formpreview')
|
return view('dashboard.administration.formpreview')
|
||||||
->with('form', json_decode($form->formStructure, true))
|
->with('form', json_decode($form->formStructure, true))
|
||||||
->with('title', $form->formName);
|
->with('title', $form->formName)
|
||||||
|
->with('formID', $form->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Request $request, Form $form)
|
||||||
|
{
|
||||||
|
return view('dashboard.administration.editform')
|
||||||
|
->with('formStructure', json_decode($form->formStructure, true))
|
||||||
|
->with('title', $form->formName)
|
||||||
|
->with('formID', $form->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, Form $form)
|
||||||
|
{
|
||||||
|
$contextValidation = ContextAwareValidator::getValidator($request->all(), true);
|
||||||
|
$this->authorize('update', $form);
|
||||||
|
|
||||||
|
|
||||||
|
if (!$contextValidation->get('validator')->fails())
|
||||||
|
{
|
||||||
|
// Add the new structure into the form. New, subsquent fields will be identified by the "new" prefix
|
||||||
|
// This prefix doesn't actually change the app's behavior when it receives applications.
|
||||||
|
// Additionally, old applications won't of course display new and updated fields, because we can't travel into the past and get data for them
|
||||||
|
$form->formStructure = $contextValidation->get('structure');
|
||||||
|
$form->save();
|
||||||
|
|
||||||
|
$request->session()->flash('success', 'Hooray! Your form was updated. New applications for it\'s vacancy will use it.');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$request->session()->flash('errors', $contextValidation->get('validator')->errors()->getMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to(route('previewForm', ['form' => $form->id]));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class FormPolicy
|
||||||
*/
|
*/
|
||||||
public function update(User $user, Form $form)
|
public function update(User $user, Form $form)
|
||||||
{
|
{
|
||||||
// unused
|
return $user->can('admin.hiring.forms');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
use App;
|
||||||
|
|
||||||
|
class ContextAwareValidatorProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Register services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
App::bind('contextAwareValidator', function(){
|
||||||
|
|
||||||
|
return new App\Helpers\ContextAwareValidator();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -175,6 +175,7 @@ return [
|
||||||
App\Providers\AuthServiceProvider::class,
|
App\Providers\AuthServiceProvider::class,
|
||||||
App\Providers\UUIDConversionProvider::class,
|
App\Providers\UUIDConversionProvider::class,
|
||||||
App\Providers\IPInfoProvider::class,
|
App\Providers\IPInfoProvider::class,
|
||||||
|
App\Providers\ContextAwareValidatorProvider::class,
|
||||||
// App\Providers\BroadcastServiceProvider::class,
|
// App\Providers\BroadcastServiceProvider::class,
|
||||||
App\Providers\EventServiceProvider::class,
|
App\Providers\EventServiceProvider::class,
|
||||||
App\Providers\RouteServiceProvider::class,
|
App\Providers\RouteServiceProvider::class,
|
||||||
|
@ -234,6 +235,7 @@ return [
|
||||||
'UUID' => App\Facades\UUID::class,
|
'UUID' => App\Facades\UUID::class,
|
||||||
'IP' => App\Facades\IP::class,
|
'IP' => App\Facades\IP::class,
|
||||||
'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class,
|
'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class,
|
||||||
|
'ContextAwareValidator' => App\Facades\ContextAwareValidation::class
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg id="a5860ba0-ef67-4914-ac96-07e2ebcccb4f" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1095.74023" height="664.03433" viewBox="0 0 1095.74023 664.03433"><title>abstract</title><rect x="701" y="661" width="394.74023" height="2" fill="#3f3d56"/><polygon points="474 429 474 216 718 216 718 0 0 0 0 643 718 643 718 429 474 429" fill="#3f3d56"/><polygon points="718 216 718 0 469 0 469 213 249 213 249 0 0 0 0 216 225 216 225 429 0 429 0 643 476 643 476 429 474 429 474 216 718 216" opacity="0.1"/><path d="M224.12988,117.98284a172.00382,172.00382,0,0,1-172,172v-172Z" transform="translate(-52.12988 -117.98284)" fill="#6c63ff"/><path d="M132.12988,117.98284a80.00357,80.00357,0,0,1-80,80v44a124.00426,124.00426,0,0,0,124-124Z" transform="translate(-52.12988 -117.98284)" opacity="0.1"/><polygon points="718 60 718 29 553 29 553 0 517 0 517 29 517 60 517 84 517 115 553 115 718 115 718 84 553 84 553 60 718 60" fill="#d0cde1"/><path d="M481.12988,448.98284a81,81,0,1,0-92,80.23639V642.98284H256.36627a81,81,0,1,0,0,22H389.12988v48h22v-48h45v-22h-45V529.21923A81.00034,81.00034,0,0,0,481.12988,448.98284Z" transform="translate(-52.12988 -117.98284)" fill="#d0cde1"/><polygon points="626.5 445.263 662.508 507.631 698.517 570 626.5 570 554.483 570 590.492 507.631 626.5 445.263" fill="#d0cde1"/><polygon points="610.5 578 538.483 578 574.492 515.631 574.742 515.198 561.5 492.263 525.492 554.631 489.483 617 561.5 617 633.517 617 611 578 610.5 578" fill="#6c63ff"/><polygon points="159 345 159 291 186 291 186 259 52 259 52 393 186 393 186 345 159 345" fill="#d0cde1"/><rect x="89" y="234" width="50" height="50" fill="#6c63ff"/><path d="M989.162,357.83165,972.618,401.5551l-59.664,20.935s-36-4-36,16c0,14.18058,43,2,43,2l78.66169-23.57271,24.816-61.44917Z" transform="translate(-52.12988 -117.98284)" fill="#ffb8b8"/><rect x="635" y="228" width="234" height="202" fill="#6c63ff"/><polygon points="747.5 289.263 783.508 351.631 819.517 414 747.5 414 675.483 414 711.492 351.631 747.5 289.263" fill="#3f3d56"/><polygon points="747.331 334.415 763.665 362.708 780 391 747.331 391 714.661 391 730.996 362.708 747.331 334.415" fill="#d0cde1"/><polygon points="747.331 242.415 763.665 270.708 780 299 747.331 299 714.661 299 730.996 270.708 747.331 242.415" fill="#d0cde1"/><path d="M1024.15387,329.42035s-16.544-2.36343-24.816,28.36116-10.63544,33.088-10.63544,33.088,48.45031,7.09029,49.632,4.72686S1048.96988,336.51064,1024.15387,329.42035Z" transform="translate(-52.12988 -117.98284)" fill="#2f2e41"/><circle cx="962.57027" cy="167.71406" r="31.9063" fill="#ffb8b8"/><path d="M1000.51957,311.69463s10.63544,27.17944,7.09029,30.72459,43.72345,8.272,43.72345,8.272,3.54515-17.72572-10.63543-28.36116c0,0-5.90858-17.72572-4.72686-21.27087S1000.51957,311.69463,1000.51957,311.69463Z" transform="translate(-52.12988 -117.98284)" fill="#ffb8b8"/><path d="M1028.88073,323.51178a85.216,85.216,0,0,0-31.9063,21.27087c-14.18058,15.36229-21.27087,20.08915-21.27087,20.08915s-16.544,5.90857-10.63544,34.26973c2.95429,14.18058.88629,30.13373-1.92028,42.54174a265.8223,265.8223,0,0,0-6.35172,58.65559v12.2473s-108.71777,228.071-72.08461,243.43328,69.72118,20.08915,72.08461,16.544,44.90517-187.89267,44.90517-187.89267,11.81715,197.34639,41.36,197.34639,77.99318-8.272,77.99318-8.272l-60.26746-236.343s14.18058-40.17831-21.27087-68.53947l-4.72686-12.99886c19.60447.01,28.56614-124.06968,8.272-134.7155C1043.06131,321.14835,1038.33445,317.6032,1028.88073,323.51178Z" transform="translate(-52.12988 -117.98284)" fill="#2f2e41"/><path d="M999.33786,387.32438l-16.544,43.72345-59.664,20.935s-36-4-36,16c0,14.18058,43,2,43,2l78.6617-23.57271,24.816-61.44918Z" transform="translate(-52.12988 -117.98284)" fill="#ffb8b8"/><ellipse cx="868.62393" cy="652.21718" rx="20.68001" ry="11.81715" fill="#2f2e41"/><ellipse cx="1026.97373" cy="652.21718" rx="20.68001" ry="11.81715" fill="#2f2e41"/><path d="M1027.01734,240.95422h-.00012a25.34145,25.34145,0,0,0-4.89258.48651,21.44172,21.44172,0,0,0-9.34021-2.15271h-.84777c-16.34473,0-29.59461,14.7901-29.59461,33.03461v.00006h5.47669l.88452-6.73224L990,272.32269h4.56226a64.57589,64.57589,0,0,0-2.119,16.55036v33.62384h8.28027l4.79395-12.45795-1.19849,12.45795h53.22314l4.35816-11.32538-1.0896,11.32538h5.99243V296.09613C1066.80311,265.64215,1048.99037,240.95422,1027.01734,240.95422Z" transform="translate(-52.12988 -117.98284)" fill="#2f2e41"/></svg>
|
After Width: | Height: | Size: 4.3 KiB |
|
@ -1,3 +1,4 @@
|
||||||
|
// TODO: Add cleaner and less verbose solution found in formeditor.js
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$("#add").click(function() {
|
$("#add").click(function() {
|
||||||
var lastField = $("#buildyourform div:last");
|
var lastField = $("#buildyourform div:last");
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// reminder: use vuejs instead, this is still an ugly and cheap solution
|
||||||
|
$(document).ready(function(){
|
||||||
|
|
||||||
|
var fieldID = 0;
|
||||||
|
var wrapper = $('.field-container');
|
||||||
|
var newBtn = $('#add');
|
||||||
|
|
||||||
|
$(newBtn).click(function(e){
|
||||||
|
e.preventDefault()
|
||||||
|
fieldID++;
|
||||||
|
|
||||||
|
$(wrapper).append('<div id=group' + fieldID + '><input type="text" name="newFieldID' + fieldID + '[]" class="form-control" />');
|
||||||
|
$(wrapper).append('<select name="newFieldID' + fieldID + '[]" class="custom-select"> <option value="nil" disabled>Choose a type</option> <option value="textbox">Textbox</option> <option value="textarea">Multi line answer</option> <option value="checkbox">Checkbox</option> </select>');
|
||||||
|
//$(wrapper).append('<button type="button" class="btn btn-danger btn-sm float-right delete"><i class="fas fa-minus"></i></button></div>');
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,103 @@
|
||||||
|
@extends('adminlte::page')
|
||||||
|
|
||||||
|
@section('title', 'Raspberry Network | Edit From')
|
||||||
|
|
||||||
|
@section('content_header')
|
||||||
|
|
||||||
|
<h4>Administration / Forms / Editor</h4>
|
||||||
|
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('js')
|
||||||
|
|
||||||
|
<script src="/js/formeditor.js"></script>
|
||||||
|
<x-global-errors></x-global-errors>
|
||||||
|
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<div class="row mb-5">
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col text-center">
|
||||||
|
|
||||||
|
<img src="/img/editor.svg" width="250px" alt="Editor illustration" class="img-responsive" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
<form id="editForm" method="POST" action="{{ route('updateForm', ['form' => $formID]) }}">
|
||||||
|
@csrf
|
||||||
|
@method('PATCH')
|
||||||
|
<div class="card">
|
||||||
|
|
||||||
|
<div class="card-header">
|
||||||
|
|
||||||
|
<h4>Editing {{ $title }}...</h4>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
@foreach($formStructure['fields'] as $fieldName => $field)
|
||||||
|
|
||||||
|
<div class="form-group mt-4 mb-4">
|
||||||
|
|
||||||
|
<input autocomplete="false" type="text" id="{{ $fieldName }}" class="form-control" name="{{ $fieldName }}[]" value="{{ $field['title'] }}" />
|
||||||
|
|
||||||
|
<select class="custom-select" id="{{ $fieldName }}-type" name="{{ $fieldName }}[]">
|
||||||
|
|
||||||
|
<option value="nil" disabled>Choose a type</option>
|
||||||
|
<option value="textbox" {{ ($field['type'] == 'textbox' ? 'selected' : '') }}>Textbox</option>
|
||||||
|
<option value="textarea" {{ ($field['type'] == 'textarea' ? 'selected' : '') }}>Multi line answer</option>
|
||||||
|
<option value="checkbox" {{ ($field['type'] == 'checkbox' ? 'selected' : '') }}>Checkbox</option>
|
||||||
|
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
<div class="field-container mt-4 mb-4">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer text-center">
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-warning ml-2" onclick="$('#editForm').submit()"><i class="fas fa-save"></i> Save & Quit</button>
|
||||||
|
<button type="button" class="btn btn-primary ml-2" id="add"><i class="fas fa-plus"></i> New field</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@stop
|
|
@ -66,8 +66,8 @@
|
||||||
|
|
||||||
<div class="card-footer text-center">
|
<div class="card-footer text-center">
|
||||||
|
|
||||||
<button type="button" class="btn btn-success ml-2" onlick="window.location.href='{{ route('showForms') }}'"><i class="fas fa-chevron-left"></i> Go back</button>
|
<button type="button" class="btn btn-success ml-2" onclick="window.location.href='{{ route('showForms') }}'"><i class="fas fa-chevron-left"></i> Go back</button>
|
||||||
<button type="button" class="btn btn-warning ml-2"><i class="far fa-edit"></i> Edit</button>
|
<button type="button" class="btn btn-warning ml-2" onclick="window.location.href='{{ route('editForm', ['form' => $formID]) }}'"><i class="far fa-edit"></i> Edit</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Form Title</th>
|
<th>Form Title</th>
|
||||||
<th>Created On</th>
|
<th>Created On</th>
|
||||||
|
<th>Updated On</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
<td>{{$form->id}}</td>
|
<td>{{$form->id}}</td>
|
||||||
<td>{{$form->formName}}</td>
|
<td>{{$form->formName}}</td>
|
||||||
<td>{{$form->created_at}}</td>
|
<td>{{$form->created_at}}</td>
|
||||||
|
<td>{{ $form->updated_at }}</td>
|
||||||
<td>
|
<td>
|
||||||
<form style="display: inline-block; white-space: nowrap" action="{{route('destroyForm', ['id' => $form->id])}}" method="POST">
|
<form style="display: inline-block; white-space: nowrap" action="{{route('destroyForm', ['id' => $form->id])}}" method="POST">
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,18 @@
|
||||||
|
|
||||||
@endhasrole
|
@endhasrole
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
<div class="alert alert-warning alert-dismissible">
|
||||||
|
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
|
||||||
|
<strong>Reminder:</strong> If this form has been updated, new fields and updated questions will not show up here!
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
|
@ -357,9 +369,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
|
||||||
@if ($comments->isEmpty())
|
@if ($comments->isEmpty())
|
||||||
|
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
|
@ -378,11 +390,11 @@
|
||||||
|
|
||||||
|
|
||||||
@if (!$comments->isEmpty())
|
@if (!$comments->isEmpty())
|
||||||
|
|
||||||
@foreach($comments as $comment)
|
@foreach($comments as $comment)
|
||||||
<div class="row mt-3 mb-3">
|
<div class="row mt-3 mb-3">
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@if($application->user->avatarPreference == 'gravatar')
|
@if($application->user->avatarPreference == 'gravatar')
|
||||||
<img class="profile-user-img img-fluid img-circle" src="https://gravatar.com/avatar/{{md5($comment->user->email)}}" alt="User profile picture">
|
<img class="profile-user-img img-fluid img-circle" src="https://gravatar.com/avatar/{{md5($comment->user->email)}}" alt="User profile picture">
|
||||||
|
@ -394,24 +406,24 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card comment">
|
<div class="card comment">
|
||||||
|
|
||||||
<div class="card-header comment-header">
|
<div class="card-header comment-header">
|
||||||
|
|
||||||
<h1 class="commenter">{{$comment->user->name}} ● {{Carbon\Carbon::parse($comment->created_at)->diffForHumans()}}</h3>
|
<h1 class="commenter">{{$comment->user->name}} ● {{Carbon\Carbon::parse($comment->created_at)->diffForHumans()}}</h3>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
{{$comment->text}}
|
{{$comment->text}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if(Auth::user()->is($comment->user) || Auth::user()->hasRole('admin'))
|
@if(Auth::user()->is($comment->user) || Auth::user()->hasRole('admin'))
|
||||||
|
|
||||||
<div class="card-footer comment-footer">
|
<div class="card-footer comment-footer">
|
||||||
|
|
||||||
<form method="POST" id="deleteComment" action="{{route('deleteApplicationComment', ['comment' => $comment->id])}}">
|
<form method="POST" id="deleteComment" action="{{route('deleteApplicationComment', ['comment' => $comment->id])}}">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
|
@ -434,7 +446,7 @@
|
||||||
<div class="row mt-5">
|
<div class="row mt-5">
|
||||||
|
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
@if($application->user->avatarPreference == 'gravatar')
|
@if($application->user->avatarPreference == 'gravatar')
|
||||||
<img class="profile-user-img img-fluid img-circle" src="https://gravatar.com/avatar/{{md5(Auth::user()->email)}}" alt="User profile picture">
|
<img class="profile-user-img img-fluid img-circle" src="https://gravatar.com/avatar/{{md5(Auth::user()->email)}}" alt="User profile picture">
|
||||||
|
@ -443,15 +455,15 @@
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card border-top border-bottom">
|
<div class="card border-top border-bottom">
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<form id="newComment" method="POST" action="{{route('addApplicationComment', ['application' => $application->id])}}">
|
<form id="newComment" method="POST" action="{{route('addApplicationComment', ['application' => $application->id])}}">
|
||||||
|
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<textarea id="comment" name="comment" class="form-control" id="commentText"></textarea>
|
<textarea id="comment" name="comment" class="form-control" id="commentText"></textarea>
|
||||||
|
@ -459,14 +471,14 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col text-left">
|
<div class="col text-left">
|
||||||
<p class="text-sm text-muted">Commenting as {{Auth::user()->name}}</p>
|
<p class="text-sm text-muted">Commenting as {{Auth::user()->name}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="col text-right">
|
<div class="col text-right">
|
||||||
|
|
||||||
<p class="text-sm text-muted"><span id="charcount">0</span>/600 max characters</p>
|
<p class="text-sm text-muted"><span id="charcount">0</span>/600 max characters</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -476,7 +488,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-footer text-right">
|
<div class="card-footer text-right">
|
||||||
|
|
||||||
<button type="button" id="submitComment" class="btn btn-sm btn-secondary">Post</button>
|
<button type="button" id="submitComment" class="btn btn-sm btn-secondary">Post</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -196,7 +196,13 @@ Route::group(['middleware' => ['auth', 'forcelogout']], function(){
|
||||||
|
|
||||||
Route::get('forms/preview/{form}', 'FormController@preview')
|
Route::get('forms/preview/{form}', 'FormController@preview')
|
||||||
->name('previewForm');
|
->name('previewForm');
|
||||||
|
|
||||||
|
Route::get('forms/edit/{form}', 'FormController@edit')
|
||||||
|
->name('editForm');
|
||||||
|
|
||||||
|
Route::patch('forms/update/{form}', 'FormController@update')
|
||||||
|
->name('updateForm');
|
||||||
|
|
||||||
|
|
||||||
Route::get('devtools', 'DevToolsController@index')
|
Route::get('devtools', 'DevToolsController@index')
|
||||||
->name('devTools');
|
->name('devTools');
|
||||||
|
|
Loading…
Reference in New Issue