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

Dc3e4 new matrix field #484

Merged
merged 36 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a4ac0d1
fix password reset bug
madassdev May 27, 2024
9345436
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev May 28, 2024
c4e069f
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev May 28, 2024
ad07808
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev May 30, 2024
ce99260
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 3, 2024
840e9c1
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 5, 2024
d443551
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 7, 2024
267e631
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 10, 2024
34ff509
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 10, 2024
8697ede
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 11, 2024
9bf816c
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 18, 2024
bdc64bc
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 19, 2024
4a5b72c
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jun 25, 2024
49a8a04
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jul 1, 2024
7079bd4
Merge branch 'main' of https://github.com/JhumanJ/OpnForm
madassdev Jul 3, 2024
a9c5371
wip: matrix input
madassdev Jul 11, 2024
d633500
wip: matrix input
madassdev Jul 18, 2024
88c3a67
wip: matrix input
madassdev Jul 19, 2024
0c37a89
Fixed matric input component logic
JhumanJ Jul 19, 2024
a853c82
matrix input cleanup
madassdev Jul 23, 2024
a307379
fix lint errors
madassdev Jul 23, 2024
4e87cf8
Merge branch 'dc3e4-new-matrix-field' of https://github.com/JhumanJ/O…
madassdev Jul 23, 2024
ca723b6
table border and radius
madassdev Jul 23, 2024
046038b
cleanup, linting
madassdev Jul 23, 2024
82fe4b0
Merge remote-tracking branch 'origin/main' into dc3e4-new-matrix-field
madassdev Jul 23, 2024
a1dbd4a
fix component methos
madassdev Jul 23, 2024
c1cfcac
wip matrix input
madassdev Jul 30, 2024
c7f7e5d
matrix condition for contains and not contain
madassdev Aug 1, 2024
e19c461
patch matrix input condition logic
madassdev Aug 6, 2024
54b7a7b
linting
madassdev Aug 6, 2024
104eabe
refactor and cleanup
madassdev Aug 6, 2024
4d4b747
fix syntax error
madassdev Aug 13, 2024
1b6ec44
Merge remote-tracking branch 'origin/main' into dc3e4-new-matrix-field
madassdev Aug 13, 2024
88ec9a3
Merge branch 'main' into dc3e4-new-matrix-field
JhumanJ Aug 23, 2024
8afbdb1
Polished the matrix input
JhumanJ Aug 23, 2024
24ac419
Fix linting
JhumanJ Aug 23, 2024
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
5 changes: 4 additions & 1 deletion app/Http/Requests/AnswerFormRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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;
Expand Down Expand Up @@ -82,6 +83,8 @@ public function rules()
} elseif ($property['type'] == 'rating') {
// For star rating, needs a minimum of 1 star
$rules[] = 'min:1';
} elseif ($property['type'] == 'matrix') {
$rules[] = new MatrixValidationRule();
}
} else {
$rules[] = 'nullable';
Expand All @@ -97,7 +100,7 @@ public function rules()
}

// 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));
}

Expand Down
28 changes: 28 additions & 0 deletions app/Rules/FormPropertyLogicRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand Down
28 changes: 28 additions & 0 deletions app/Rules/MatrixValidationRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class MatrixValidationRule implements ValidationRule
{
/**
* Run the validation rule.
*
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!is_array($value)) {
$fail('The Matrix field must be an array.');
return;
}
$nullValues = array_filter($value, function ($val) {
return $val === null;
});
if (sizeof($nullValues)) {
$fail('The Matrix field is required.');
}
}
}
42 changes: 42 additions & 0 deletions app/Service/Forms/FormLogicConditionChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ private function propertyConditionMet(array $propertyCondition, $value): bool
return $this->multiSelectConditionMet($propertyCondition, $value);
case 'files':
return $this->filesConditionMet($propertyCondition, $value);
case 'matrix':
return $this->matrixConditionMet($propertyCondition, $value);
}

return false;
Expand All @@ -90,6 +92,30 @@ private function checkContains($condition, $fieldValue): bool
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;
}
Comment on lines +95 to +107
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor for efficiency in checkMatrixContains.

The current implementation checks for key existence twice. This can be streamlined by checking once and then comparing values.

-        foreach($condition['value'] as $key => $value) {
-            if(!(array_key_exists($key, $condition['value']) && array_key_exists($key, $fieldValue))) {
+        foreach($condition['value'] as $key => $value) {
+            if(array_key_exists($key, $fieldValue) && $value == $fieldValue[$key]) {
                 return true;
             }
         }
         return false;
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. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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 checkMatrixContains($condition, $fieldValue): bool
{
foreach($condition['value'] as $key => $value) {
if(array_key_exists($key, $fieldValue) && $value == $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)) {
Expand Down Expand Up @@ -408,4 +434,20 @@ private function filesConditionMet(array $propertyCondition, $value): bool

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;
}
}
15 changes: 15 additions & 0 deletions app/Service/Forms/FormSubmissionFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ public function useSignedUrlForFiles()
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
Expand Down Expand Up @@ -145,6 +156,8 @@ public function getCleanKeyValue()
} else {
$returnArray[$field['name']] = $val;
}
} 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;
Expand Down Expand Up @@ -219,6 +232,8 @@ public function getFieldsWithValue()
} else {
$field['value'] = $val;
}
} 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;
Expand Down
1 change: 0 additions & 1 deletion client/components/forms/FlatSelectInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
theme.default.input,
theme.default.borderRadius,
{
'mb-2': index !== options.length,
'!ring-red-500 !ring-2 !border-transparent': hasError,
'!cursor-not-allowed !bg-gray-200': disabled,
},
Expand Down
117 changes: 117 additions & 0 deletions client/components/forms/MatrixInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<template>
<input-wrapper v-bind="inputWrapperProps">
<template #label>
<slot name="label"/>
</template>
<div class='border' :class="[
theme.default.borderRadius,
{
'!ring-red-500 !ring-2 !border-transparent': hasError,
'!cursor-not-allowed !bg-gray-200': disabled,
},
]">
<table
class="w-full table-fixed overflow-hidden border border-collapse border-transparent"
:class="[
theme.default.borderRadius]"
>
<thead class="">
<tr class="bg-gray-200/60">
<th>

</th>
<td v-for="column in columns" :key="column" class="border border-gray-300">
<div class="p-2 w-full flex items-center justify-center capitalize text-sm truncate">
{{ column }}
</div>
</td>
</tr>
</thead>

<tbody>
<tr v-for="row, rowIndex in rows" :key="rowIndex" class="">
<td class="border border-gray-300">
<div class="w-full flex-grow p-2 text-sm truncate">
{{ row }}
</div>
</td>
<td v-for="column in columns" :key="row + column" class="border border-gray-300">
<div
class="w-full flex items-center justify-center hover:bg-gray-200/40"
v-if="compVal"
role="radio"
:aria-checked="compVal[row] === column"
:class="[
theme.FlatSelectInput.spacing.vertical,
theme.FlatSelectInput.fontSize,
theme.FlatSelectInput.option,
]"
@click="onSelect(row, column)"
>
<Icon
v-if="compVal[row] === column"
:key="row+column"
name="material-symbols:radio-button-checked-outline"
class="text-inherit"
:color="color"
:class="[theme.FlatSelectInput.icon]"
/>
<Icon
v-else
:key="row+column"
name="material-symbols:radio-button-unchecked"
:class="[theme.FlatSelectInput.icon,theme.FlatSelectInput.unselectedIcon]"
/>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<template #help>
<slot name="help"/>
</template>
<template #error>
<slot name="error"/>
</template>
</input-wrapper>
</template>
<script>
import {inputProps, useFormInput} from "./useFormInput.js"
import InputWrapper from "./components/InputWrapper.vue"

export default {
name: "MatrixInput",
components: {InputWrapper},

props: {
...inputProps,
rows: {type: Array, required: true},
columns: {type: Array, required: true},
},
data() {
return {
}
},
setup(props, context) {
return {
...useFormInput(props, context),
}
},
computed: {},
methods: {
onSelect(row, column) {
if (this.compVal[row] === column) {
this.compVal[row] = null
} else {
this.compVal[row] = column
}
},
},
beforeMount() {
if (!this.compVal || typeof this.compVal !== 'object') {
this.compVal = {}
}
},
}
</script>
42 changes: 42 additions & 0 deletions client/components/forms/components/MatrixRadioInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<template>
<div
role="radio"
:aria-checked="selected"
:class="[
theme.FlatSelectInput.spacing.vertical,
theme.FlatSelectInput.fontSize,
theme.FlatSelectInput.option,
]"
@click="onSelect"
>
<Icon
v-if="selected"
name="material-symbols:radio-button-checked-outline"
class="text-inherit"
:color="color"
:class="[theme.FlatSelectInput.icon]"
/>
<Icon
v-else
name="material-symbols:radio-button-unchecked"
:class="[theme.FlatSelectInput.icon,theme.FlatSelectInput.unselectedIcon]"
/>
</div>
</template>
<script>
export default {
emits: ['toggle-select'],
props: {
theme: { type: Object },
color: {type: String, default: "#3B82F6"},
selected: { type: Boolean, default: false }
},
methods: {
onSelect(){
this.$emit("toggle-select")
}
}

}

</script>
2 changes: 2 additions & 0 deletions client/components/open/forms/OpenForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ export default {
formData[field.id] = urlPrefill.getAll(field.id + '[]')
} else if (field.type === 'date' && field.prefill_today === true) { // For Prefill with 'today'
formData[field.id] = new Date().toISOString()
} else if (field.type === 'matrix') {
formData[field.id] = {...field.prefill}
} else { // Default prefill if any
formData[field.id] = field.prefill
}
Expand Down
Loading