Dc3e4 new matrix field (#484)

* fix password reset bug

* wip: matrix input

* wip: matrix input

* wip: matrix input

* Fixed matric input component logic

* matrix input cleanup

* fix lint errors

* table border and radius

* cleanup, linting

* fix component methos

* wip matrix input

* matrix condition for contains and not contain

* patch matrix input condition logic

* linting

* refactor and cleanup

* fix syntax error

* Polished the matrix input

* Fix linting

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Favour Olayinka
2024-08-23 14:28:21 +01:00
committed by GitHub
parent fedc382594
commit 1adac8e00f
25 changed files with 919 additions and 85 deletions

View File

@@ -4,6 +4,7 @@ namespace App\Http\Requests;
use App\Models\Forms\Form;
use App\Rules\CustomFieldValidationRule;
use App\Rules\MatrixValidationRule;
use App\Rules\StorageFile;
use App\Rules\ValidHCaptcha;
use App\Rules\ValidPhoneInputRule;
@@ -82,9 +83,14 @@ class AnswerFormRequest extends FormRequest
} elseif ($property['type'] == 'rating') {
// For star rating, needs a minimum of 1 star
$rules[] = 'min:1';
} elseif ($property['type'] == 'matrix') {
$rules[] = new MatrixValidationRule($property, true);
}
} else {
$rules[] = 'nullable';
if ($property['type'] == 'matrix') {
$rules[] = new MatrixValidationRule($property, false);
}
}
// Clean id to escape "."
@@ -97,7 +103,7 @@ class AnswerFormRequest extends FormRequest
}
// User custom validation
if(!(Str::of($property['type'])->startsWith('nf-')) && isset($property['validation'])) {
if (!(Str::of($property['type'])->startsWith('nf-')) && isset($property['validation'])) {
$rules[] = (new CustomFieldValidationRule($property['validation'], $data));
}

View File

@@ -73,6 +73,34 @@ class FormPropertyLogicRule implements DataAwareRule, ValidationRule
],
],
],
'matrix' => [
'comparators' => [
'equals' => [
'expected_type' => 'object',
'format' => [
'type' => 'object',
],
],
'does_not_equal' => [
'expected_type' => 'object',
'format' => [
'type' => 'object',
],
],
'contains' => [
'expected_type' => 'object',
'format' => [
'type' => 'object',
],
],
'does_not_contain' => [
'expected_type' => 'object',
'format' => [
'type' => 'object',
],
],
],
],
'url' => [
'comparators' => [
'equals' => [

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class MatrixValidationRule implements ValidationRule
{
protected $field;
protected $isRequired;
public function __construct(array $field, bool $isRequired)
{
$this->field = $field;
$this->isRequired = $isRequired;
}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!$this->isRequired && empty($value)) {
return; // If not required and empty, validation passes
}
if (!is_array($value)) {
$fail('The Matrix field must be an array.');
return;
}
$rows = $this->field['rows'];
$columns = $this->field['columns'];
foreach ($rows as $row) {
if (!array_key_exists($row, $value)) {
if ($this->isRequired) {
$fail("Missing value for row '{$row}'.");
}
continue;
}
$cellValue = $value[$row];
if ($cellValue === null) {
if ($this->isRequired) {
$fail("Value for row '{$row}' is required.");
}
continue;
}
if (!in_array($cellValue, $columns)) {
$fail("Invalid value '{$cellValue}' for row '{$row}'.");
}
}
// Check for extra rows that shouldn't be there
$extraRows = array_diff(array_keys($value), $rows);
foreach ($extraRows as $extraRow) {
$fail("Unexpected row '{$extraRow}' in the matrix.");
}
}
}

View File

@@ -72,6 +72,8 @@ class FormLogicConditionChecker
return $this->multiSelectConditionMet($propertyCondition, $value);
case 'files':
return $this->filesConditionMet($propertyCondition, $value);
case 'matrix':
return $this->matrixConditionMet($propertyCondition, $value);
}
return false;
@@ -90,6 +92,30 @@ class FormLogicConditionChecker
return \Str::contains($fieldValue, $condition['value']);
}
private function checkMatrixContains($condition, $fieldValue): bool
{
foreach($condition['value'] as $key => $value) {
if(!(array_key_exists($key, $condition['value']) && array_key_exists($key, $fieldValue))) {
return false;
}
if($condition['value'][$key] == $fieldValue[$key]) {
return true;
}
}
return false;
}
private function checkMatrixEquals($condition, $fieldValue): bool
{
foreach($condition['value'] as $key => $value) {
if($condition['value'][$key] !== $fieldValue[$key]) {
return false;
}
}
return true;
}
private function checkListContains($condition, $fieldValue): bool
{
if (is_null($fieldValue)) {
@@ -408,4 +434,20 @@ class FormLogicConditionChecker
return false;
}
private function matrixConditionMet(array $propertyCondition, $value): bool
{
switch ($propertyCondition['operator']) {
case 'equals':
return $this->checkMatrixEquals($propertyCondition, $value);
case 'does_not_equal':
return !$this->checkMatrixEquals($propertyCondition, $value);
case 'contains':
return $this->checkMatrixContains($propertyCondition, $value);
case 'does_not_contain':
return !$this->checkMatrixContains($propertyCondition, $value);
}
return false;
}
}

View File

@@ -81,6 +81,17 @@ class FormSubmissionFormatter
return $this;
}
public function getMatrixString(array $val): string
{
$parts = [];
foreach ($val as $key => $value) {
if ($key !== null && $value !== null) {
$parts[] = "$key: $value";
}
}
return implode(' | ', $parts);
}
/**
* Return a nice "FieldName": "Field Response" array
* - If createLink enabled, returns html link for emails and links
@@ -145,7 +156,9 @@ class FormSubmissionFormatter
} else {
$returnArray[$field['name']] = $val;
}
} elseif (in_array($field['type'], ['files', 'signature'])) {
} elseif ($field['type'] == 'matrix' && is_array($data[$field['id']])) {
$returnArray[$field['name']] = $this->getMatrixString($data[$field['id']]);
} elseif ($field['type'] == 'files') {
if ($this->outputStringsOnly) {
$formId = $this->form->id;
$returnArray[$field['name']] = implode(
@@ -219,7 +232,9 @@ class FormSubmissionFormatter
} else {
$field['value'] = $val;
}
} elseif (in_array($field['type'], ['files', 'signature'])) {
} elseif ($field['type'] == 'matrix') {
$field['value'] = str_replace(' | ', "\n", $this->getMatrixString($data[$field['id']]));
} elseif ($field['type'] == 'files') {
if ($this->outputStringsOnly) {
$formId = $this->form->id;
$field['value'] = implode(