Skip to content

Commit

Permalink
✨ Add form livewire component, add user entries manager
Browse files Browse the repository at this point in the history
  • Loading branch information
tgeorgel committed Oct 19, 2024
1 parent 7c72f97 commit 3b309c5
Show file tree
Hide file tree
Showing 13 changed files with 398 additions and 15 deletions.
4 changes: 4 additions & 0 deletions database/migrations/2024_10_12_154450_create_forms_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public function up(): void
Schema::create('forms', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('notify_subject')->nullable();
$table->string('notify_emails')->nullable();
$table->string('submit_button_label')->nullable();
$table->text('submit_success_message')->nullable();
$table->timestamps();
});
}
Expand Down
2 changes: 2 additions & 0 deletions resources/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
"Value": "Valeur",
"Email": "Email",
"Telephone": "Téléphone",
"Submit": "Envoyer",
"New form submission": "Nouvelle entrée de formulaire",
"Select option...": "Sélectionner une option...",
"Manage form fields": "Gérer les champs du formulaire",
"Field size": "Largeur du champ",
Expand Down
22 changes: 22 additions & 0 deletions resources/views/components/forms/checkbox-confirm.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@props(['label', 'name', 'helperText' => null])

<div class="mb-4">
<div class="flex">
<input
type="checkbox" name="{{ $name }}" id="{{ $name }}" {{
$attributes->class([
'shrink-0 mt-0.5 rounded disabled:opacity-50 disabled:pointer-events-none border-gray-200 dark:bg-neutral-800 dark:border-neutral-700 dark:focus:ring-offset-gray-800',
'border-gray-200 text-blue-600 focus:ring-blue-500 dark:checked:bg-blue-500 dark:checked:border-blue-500' => !$errors->has($name),
'text-red-600 focus:ring-red-500 dark:checked:bg-red-500 dark:checked:border-red-500"' => $errors->has($name),
])
}} />

<label for="{{ $name }}" value="1" class="!text-sm ms-3 dark:text-neutral-400 prose {{ $errors->has($name) ? 'text-red-500' : 'text-gray-500' }}">
{!! strip_tags($label, '<br><b><a><i>') !!}
</label>
</div>

@if ($helperText)
<p class="mt-2 text-xs text-gray-500 dark:text-neutral-500">{{ $helperText }}</p>
@endif
</div>
37 changes: 37 additions & 0 deletions resources/views/components/forms/input.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@props(['label', 'name', 'helperText' => null])

<div class="mb-4">
<label for="{{ $name }}" class="block mb-2 text-sm font-medium dark:text-white">
{{ $label }}
</label>

<div class="relative">
<input name="{{ $name }}" id="{{ $name }}" {{
$attributes->class([
'py-3 px-4 block w-full rounded-lg text-sm disabled:opacity-50 disabled:pointer-events-none',
'border-gray-200 focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder-neutral-500 dark:focus:ring-neutral-600' => !$errors->has($name),
'border-red-500 focus:border-red-500 focus:ring-red-500 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-400' => $errors->has($name),
])
}} />

@if ($errors->has($name))
<div class="absolute inset-y-0 flex items-center pointer-events-none end-0 pe-3">
<svg class="text-red-500 shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" x2="12" y1="8" y2="12"></line>
<line x1="12" x2="12.01" y1="16" y2="16"></line>
</svg>
</div>
@endif
</div>

@if ($errors->has($name))
<p class="mt-2 !text-sm text-red-600">
@foreach ($errors->get($name) as $error)
{{ $error }}<br>
@endforeach
</p>
@elseif ($helperText)
<p class="mt-2 text-xs text-gray-500 dark:text-neutral-500">{{ $helperText }}</p>
@endif
</div>
29 changes: 29 additions & 0 deletions resources/views/components/forms/select.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@props(['label', 'name', 'placeholder' => __('Select option...'), 'helperText' => null])

<div class="mb-4">
<label for="{{ $name }}" class="block mb-2 text-sm font-medium dark:text-white">
{{ $label }}
</label>

<select data-hs-select='{
"placeholder": "{{ $placeholder }}",
"toggleTag": "<button type=\"button\" aria-expanded=\"false\"></button>",
"toggleClasses": "hs-select-disabled:pointer-events-none hs-select-disabled:opacity-50 relative py-3 ps-4 pe-9 flex gap-x-2 text-nowrap w-full cursor-pointer bg-white border border-gray-200 rounded-lg text-start text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:focus:outline-none dark:focus:ring-1 dark:focus:ring-neutral-600",
"dropdownClasses": "mt-2 z-50 w-full max-h-72 p-1 space-y-0.5 bg-white border border-gray-200 rounded-lg overflow-hidden overflow-y-auto [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-track]:bg-gray-100 [&::-webkit-scrollbar-thumb]:bg-gray-300 dark:[&::-webkit-scrollbar-track]:bg-neutral-700 dark:[&::-webkit-scrollbar-thumb]:bg-neutral-500 dark:bg-neutral-900 dark:border-neutral-700",
"optionClasses": "py-2 px-4 w-full text-sm text-gray-800 cursor-pointer hover:bg-gray-100 rounded-lg focus:outline-none focus:bg-gray-100 hs-select-disabled:pointer-events-none hs-select-disabled:opacity-50 dark:bg-neutral-900 dark:hover:bg-neutral-800 dark:text-neutral-200 dark:focus:bg-neutral-800",
"optionTemplate": "<div class=\"flex justify-between items-center w-full\"><span data-title></span><span class=\"hidden hs-selected:block\"><svg class=\"shrink-0 size-3.5 text-blue-600 dark:text-blue-500 \" xmlns=\"http:.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg></span></div>",
"extraMarkup": "<div class=\"absolute top-1/2 end-3 -translate-y-1/2\"><svg class=\"shrink-0 size-3.5 text-gray-500 dark:text-neutral-500 \" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m7 15 5 5 5-5\"/><path d=\"m7 9 5-5 5 5\"/></svg></div>"
}' {{ $attributes->class('hidden') }}>
{{ $slot }}
</select>

@if ($errors->has($name))
<p class="mt-2 !text-sm text-red-600">
@foreach ($errors->get($name) as $error)
{{ $error }}<br>
@endforeach
</p>
@elseif ($helperText)
<p class="mt-2 text-xs text-gray-500 dark:text-neutral-500">{{ $helperText }}</p>
@endif
</div>
37 changes: 37 additions & 0 deletions resources/views/components/forms/textarea.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@props(['label', 'name', 'helperText' => null])

<div class="mb-4">
<label for="{{ $name }}" class="block mb-2 text-sm font-medium dark:text-white">
{{ $label }}
</label>

<div class="relative">
<textarea name="{{ $name }}" id="{{ $name }}" {{
$attributes->class([
'py-3 px-4 block w-full rounded-lg text-sm disabled:opacity-50 disabled:pointer-events-none',
'border-gray-200 focus:border-blue-500 focus:ring-blue-500 dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder-neutral-500 dark:focus:ring-neutral-600' => !$errors->has($name),
'border-red-500 focus:border-red-500 focus:ring-red-500 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-400' => $errors->has($name),
])
}}></textarea>

@if ($errors->has($name))
<div class="absolute inset-y-0 flex items-center pointer-events-none end-0 pe-3">
<svg class="text-red-500 shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" x2="12" y1="8" y2="12"></line>
<line x1="12" x2="12.01" y1="16" y2="16"></line>
</svg>
</div>
@endif
</div>

@if ($errors->has($name))
<p class="mt-2 !text-sm text-red-600">
@foreach ($errors->get($name) as $error)
{{ $error }}<br>
@endforeach
</p>
@elseif ($helperText)
<p class="mt-2 text-xs text-gray-500 dark:text-neutral-500">{{ $helperText }}</p>
@endif
</div>
95 changes: 95 additions & 0 deletions resources/views/livewire/contact-form.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
@php
use Hydrat\GroguCMS\Enums\FormFieldType;
@endphp

<div>
<form wire:submit.prevent="submit">
<div class="grid grid-cols-12 gap-4 p-2">
@foreach ($form->fields as $field)
<div class="col-span-{{ $field->column_span ?: 'full' }}">
@switch ($field->type)
@case (FormFieldType::Text)
<x-grogu::forms.input
type="text"
:label="$field->name"
name="data.{{ $field->key }}"
wire:model="data.{{ $field->key }}"
:helperText="$field->helper_text"
:placeholder="$field->placeholder"
:required="$field->required"
/>
@break
@case (FormFieldType::Email)
<x-grogu::forms.input
type="email"
:label="$field->name"
name="data.{{ $field->key }}"
wire:model="data.{{ $field->key }}"
:helperText="$field->helper_text"
:placeholder="$field->placeholder"
:required="$field->required"
/>
@break
@case (FormFieldType::Telephone)
<x-grogu::forms.input
type="tel"
:label="$field->name"
name="data.{{ $field->key }}"
wire:model="data.{{ $field->key }}"
:helperText="$field->helper_text"
:placeholder="$field->placeholder"
:required="$field->required"
/>
@break
@case (FormFieldType::Textarea)
<x-grogu::forms.textarea
type="email"
:label="$field->name"
name="data.{{ $field->key }}"
wire:model="data.{{ $field->key }}"
:helperText="$field->helper_text"
:placeholder="$field->placeholder"
:required="$field->required"
rows="{{ $field->rows ?: 3 }}"
/>
@break
@case (FormFieldType::Select)
<x-grogu::forms.select
:label="$field->name"
name="data.{{ $field->key }}"
wire:model="data.{{ $field->key }}"
:helperText="$field->helper_text"
:placeholder="$field->placeholder"
:required="$field->required"
:multiple="$field->multiple"
>
@foreach ($field->options as $option)
<option value="{{ $option['value'] }}">{{ $option['label'] }}</option>
@endforeach
</x-forms.select>
@break
@case (FormFieldType::Confirm)
<x-grogu::forms.checkbox-confirm
:label="$field->content"
name="data.{{ $field->key }}"
wire:model="data.{{ $field->key }}"
:helperText="$field->helper_text"
:required="$field->required"
/>
@break
@default
{{-- Nothing. --}}
@endswitch
</div>
@endforeach
</div>

<button
type="submit"
class="inline-flex items-center px-4 py-3 text-sm font-medium text-gray-800 bg-white border border-gray-200 rounded-lg shadow-sm gap-x-2 hover:bg-gray-50 focus:outline-none focus:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700"
>
{{-- Todo: custom submit label --}}
Envoyer
</button>
</form>
</div>
18 changes: 18 additions & 0 deletions src/Filament/Resources/FormResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ public static function form(Form $form): Form
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),

Forms\Components\TextInput::make('submit_button_label')
->required()
->default(__('Submit'))
->maxLength(255),

Forms\Components\Textarea::make('submit_success_message')
->rows(3)
->columnSpanFull()
->maxLength(5000),

Forms\Components\TextInput::make('notify_subject')
->default(__('New form submission'))
->maxLength(255),

Forms\Components\TextInput::make('notify_email')
->maxLength(255),
]);
}

Expand Down Expand Up @@ -86,6 +103,7 @@ public static function getRecordSubNavigation(Page $page): array
return $page->generateNavigationItems([
Pages\EditForm::class,
Pages\ManageFormFields::class,
Pages\ManageFormEntries::class,
]);
}
}
81 changes: 81 additions & 0 deletions src/Filament/Resources/FormResource/Pages/ManageFormEntries.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Hydrat\GroguCMS\Filament\Resources\FormResource\Pages;

use Filament\Forms;
use Filament\Tables;
use Filament\Actions;
use Filament\Forms\Form;
use Filament\Tables\Table;
use Filament\Resources\Pages\ManageRelatedRecords;
use Illuminate\Contracts\Support\Htmlable;
use Hydrat\GroguCMS\Filament\Resources\FormResource;

class ManageFormEntries extends ManageRelatedRecords
{
protected static string $relationship = 'entries';

protected static ?string $navigationIcon = 'radix-enter';

/**
* @return class-string
*/
public static function getResource(): string
{
return config('grogu-cms.resources.form_resource') ?: FormResource::class;
}

public static function getNavigationLabel(): string
{
return __('Entries');
}

public function getTitle(): string|Htmlable
{
return __('Manage form entries');
}

public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('submitted_at')
->required()
->maxLength(255),

Forms\Components\TextInput::make('user_id')
->relationship('user', 'name')
->required()
->maxLength(255),

Forms\Components\KeyValue::make('values')
->columnSpanFull(),
]);
}

public function table(Table $table): Table
{
return $table
->recordTitleAttribute('submitted_at')
->columns([
Tables\Columns\TextColumn::make('submitted_at'),
Tables\Columns\TextColumn::make('user.name'),
])
->filters([
//
])
->headerActions([
// Tables\Actions\CreateAction::make(),
])
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make()->iconSoftButton('heroicon-o-pencil-square'),
Tables\Actions\DeleteAction::make()->iconSoftButton('heroicon-o-trash'),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
}
11 changes: 3 additions & 8 deletions src/Filament/Resources/FormResource/Pages/ManageFormFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ManageFormFields extends ManageRelatedRecords
{
protected static string $relationship = 'fields';

protected static ?string $navigationIcon = 'heroicon-o-queue-list';
protected static ?string $navigationIcon = 'radix-section'; // heroicon-o-queue-list

/**
* @return class-string
Expand Down Expand Up @@ -220,18 +220,13 @@ public function table(Table $table): Table
])
->headerActions([
Tables\Actions\CreateAction::make(),
// Tables\Actions\AssociateAction::make(),
])
->actions([
// Tables\Actions\DissociateAction::make(),
Tables\Actions\EditAction::make()
->iconSoftButton('heroicon-o-pencil-square'),
Tables\Actions\DeleteAction::make()
->iconSoftButton('heroicon-o-trash'),
Tables\Actions\EditAction::make()->iconSoftButton('heroicon-o-pencil-square'),
Tables\Actions\DeleteAction::make()->iconSoftButton('heroicon-o-trash'),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DissociateBulkAction::make(),
Tables\Actions\DeleteBulkAction::make(),
]),
])
Expand Down
Loading

0 comments on commit 3b309c5

Please sign in to comment.