diff --git a/README.md b/README.md index a9b9009..c6bb127 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,26 @@ src\Http\Traits\FormValidator.php ## Development To build the assets, run this command ```bash -npm run prod - +npm run build ``` +### Tailwind CSS + +The project uses **Tailwind CSS v3** with **v4-compatible syntax**. This means: + +- All CSS utilities and Vue components are compatible with both Tailwind v3 and v4 +- Opacity syntax uses the modern slash notation: `ring-sky-200/50` instead of `ring-opacity-50` +- When upgrading to Tailwind v4 in the future, no CSS or component changes will be needed + +**Custom Theme Extensions:** +- `brand` color palette (25, 50, 100, ..., 950) for brand styling +- `success`, `error`, `warning` color utilities +- Custom `fill` width utility for responsive layouts + +**Note:** The `/example` directory remains on Tailwind v3 for backwards compatibility. + ## Example Check example folder to see how to use the package. ```bash /example - ``` diff --git a/src/Http/Traits/FormValidator.php b/src/Http/Traits/FormValidator.php index eef4cc2..79f5b28 100644 --- a/src/Http/Traits/FormValidator.php +++ b/src/Http/Traits/FormValidator.php @@ -2,37 +2,55 @@ namespace Dcodegroup\FormBuilder\Http\Traits; -use Dcodegroup\FormBuilder\Models\Form; - trait FormValidator { - public function getRules(array $formsIds, $list = [], $isMessage = false): array + public function getRules($list = [], $isMessage = false): array { - $forms = Form::query()->find($formsIds); + $fields = null; + + /** @phpstan-ignore-next-line */ + if (method_exists($this, 'route')) { + $fields = $this->route('form')?->fields; + } + + if (empty($fields)) { + $dataFields = data_get(json_decode(request()->input('data', []), true), 'fields'); + $fields = ! empty($dataFields) ? $dataFields : null; + } + $list = collect($list); - $forms->each(function (Form $form) use (&$list, $isMessage) { - if (! empty($form->fields)) { - foreach ($form->fields as $index => $field) { - if (isset($field['required']) && $field['required']) { - [$key, $value] = $this->getValue($isMessage, $index, $field); - $list->put($key, $value); + if (! empty($fields)) { + foreach ($fields as $index => $field) { + if (isset($field['required']) && $field['required']) { + [$key, $value] = $this->getValue($isMessage, sprintf('fields.%s.value', $index), $field); + $list->put($key, $value); + } elseif (data_get($field, 'type') === 'grid') { + foreach (data_get($field, 'grid') as $row => $grid) { + foreach ($grid as $col => $gridItem) { + if (data_get($gridItem, '0.required')) { + [$key, $value] = $this->getValue($isMessage, sprintf('fields.%s.grid.%s.%s.%s.value', $index, $row, $col, 0), $gridItem[0]); + $list->put($key, $value); + } + } } } } - }); + } return $list->toArray(); } - private function getValue(bool $isMessage, int $index, array $field): array + private function getValue(bool $isMessage, string $key, array $field): array { - $key = sprintf('fields.%s.value', $index); $value = match ($field['type']) { 'checkbox' => ['required', 'accepted'], 'file-upload' => [function ($attribute, $value, $fail) use ($field) { - if (strlen($value) < 3 || empty(json_decode($value))) { - return $fail('The '.($field['label']).' must be required'); + if ( + (is_string($value) && (strlen($value) < 3 || empty(json_decode($value)))) + || (is_array($value) && empty($value)) + ) { + return $fail('The '.($field['label']).' is required'); } return true; @@ -42,7 +60,7 @@ private function getValue(bool $isMessage, int $index, array $field): array if ($isMessage) { $key .= '.required'; - $value = sprintf('%s must be required.', strval($field['label'])); + $value = sprintf('%s is required.', $field['label']); } return [$key, $value]; diff --git a/src/Models/Form.php b/src/Models/Form.php index 3e0bb39..434d021 100644 --- a/src/Models/Form.php +++ b/src/Models/Form.php @@ -57,32 +57,6 @@ public function data(): HasMany return $this->hasMany(FormData::class); } - /** - * @return false|string - */ - public function fieldsJson() - { - return json_encode($this->fields); - } - - public function setFields(array $fields): void - { - $this->update(['fields' => $fields]); - } - - public function clearFields(): void - { - $this->update(['fields' => []]); - } - - /** - * @return mixed - */ - public static function get(?string $name = null) - { - return self::latest()->first(); - } - /** * @return Form */ @@ -99,63 +73,7 @@ public static function saveModel( } $form->fill($data)->save(); - $form->setFields(data_get($data, 'fields', [])); return $form; } - - /** - * @return mixed - */ - public function prefill(?array $values = null) - { - /** - * This is done because of the pass by reference in &$field - * This updates the value, and you can not do that on the $this object. - */ - $fields = $this->fields; - if (empty($values)) { - return $fields; - } - - foreach ($fields as &$field) { - $value = $this->getFieldValue($values, $field['name']); - if ($field['type'] === 'checkbox') { - $field['value'] = (bool) $value; - - continue; - } - - if ($field['type'] === 'file-upload' && is_string($value)) { - $field['value'] = json_decode($value, true); - - continue; - } - - if ($value) { - $field['value'] = $value; - } - } - - return $fields; - } - - private function getFieldValue($values, $field): mixed - { - $values = collect($values); - - if ($values->has($field)) { - return $values->pull($field); - } - - $formDataValue = $values->filter(function ($item) use ($field) { - if (! isset($item['name'])) { - return false; - } - - return $item['name'] === $field; - })->first(); - - return $formDataValue['value'] ?? null; - } } diff --git a/src/Models/FormData.php b/src/Models/FormData.php index 104c9e7..7c29e95 100644 --- a/src/Models/FormData.php +++ b/src/Models/FormData.php @@ -6,8 +6,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Collection; -use Illuminate\Support\Str; /** * @property array $values @@ -51,74 +49,4 @@ public function scopeCompleted(Builder $query) { return $query->whereNotNull('completed_at'); } - - /** - * @return mixed - */ - public function prefillForm() - { - return $this->form->prefill($this->values); - } - - public function isComplete(): bool - { - $fields = $this->prefillForm(); - - foreach ($fields as $field) { - if (array_key_exists('value', $field) && $field['value'] === null) { - return false; - } - } - - return true; - } - - public function removeUploadedFile(int $id) - { - $this->values = collect($this->values)->map(function ($field) use ($id) { - if ($field['type'] === 'file-upload') { - $fieldValue = collect($field['value'])->filter(function ($value) use ($id) { - return $value['id'] !== $id; - })->toArray(); - - $field['value'] = $fieldValue; - } - - return $field; - })->toArray(); - - $this->save(); - } - - public function fillUploadedMedia(string $field, $data = []) - { - if (Str::contains($field, 'form_fields')) { - preg_match('/(?<=\[).+?(?=\])/', $field, $matches); - if (count($matches) > 0) { - $values = collect($this->refresh()->values); - $this->values = $this->getFormValue($values, head($matches), $data); - $this->save(); - } - } - } - - private function getFormValue(Collection $values, $field, $data) - { - if ($values->isEmpty()) { - return $this->form->prefill([ - $field => [$data], - ]); - } - - return $values->map(function ($value) use ($field, $data) { - if ($value['name'] === $field) { - if (! isset($value['value']) || ! is_array($value['value'])) { - $value['value'] = []; - } - array_push($value['value'], $data); - } - - return $value; - })->toArray(); - } } diff --git a/src/Models/Traits/HasFilledForms.php b/src/Models/Traits/HasFilledForms.php index 52f9d36..7b82720 100644 --- a/src/Models/Traits/HasFilledForms.php +++ b/src/Models/Traits/HasFilledForms.php @@ -13,12 +13,12 @@ public function filledForms(): MorphMany return $this->morphMany(FormData::class, 'formable'); } - public function getFormData(Form $form): FormData + public function getFormData(Form $form, bool $createNew = false): FormData { /** @var FormData $formData */ $formData = $this->filledForms()->where('form_id', $form->id)->latest()->first(); - if (! $formData) { + if (! $formData && $createNew) { $formData = FormData::query()->create([ 'formable_id' => $this->id, 'formable_type' => get_class($this), @@ -37,7 +37,7 @@ public function saveFormData(Form $form, ?array $values = null) 'formable_type' => get_class($this), 'form_id' => $form->id, ], [ - 'values' => $form->prefill($values), + 'values' => $values, ]); } }