Skip to content

Commit

Permalink
Merge pull request #300 from nurmuhammet-ali/master
Browse files Browse the repository at this point in the history
add wizard form like functionality
  • Loading branch information
marcfil authored Sep 9, 2024
2 parents 669a3c6 + 6d1d73d commit bdaed94
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 15 deletions.
2 changes: 1 addition & 1 deletion dist/css/field.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/field.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions resources/css/field.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
@apply tabs-relative tabs-min-w-min tabs-flex-shrink-0 tabs-flex-1 tabs-overflow-hidden tabs-bg-white dark:tabs-bg-gray-800 tabs-py-4 tabs-px-4 tabs-font-semibold tabs-text-center tabs-cursor-pointer hover:tabs-bg-gray-50 hover:dark:tabs-bg-gray-700 focus:tabs-z-10 first:tabs-rounded-tl-lg last:tabs-rounded-tr-lg;
}
.tab-group .tab.fields-tab {
@apply tabs-py-2 tabs-px-6;
@apply tabs-pb-2 tabs-px-6;
}
form .tab-group .tab.fields-tab {
@apply tabs-py-2 tabs-px-0;
@apply tabs-pb-2 tabs-px-0;
}
.tab-group .tab-card {
@apply tabs-shadow tabs-bg-white dark:tabs-bg-gray-800 tabs-rounded-b-lg tabs-rounded-t-lg;
Expand All @@ -42,7 +42,7 @@ form .tab-group .tab.fields-tab {
}

.tabs-border-b-primary-500 {
border-bottom-color: rgba(var(--colors-primary-500));
border-bottom-color: rgba(var(--colors-primary-500)) !important;
}

.tabs-text-primary-500 {
Expand Down
138 changes: 134 additions & 4 deletions resources/js/components/FormTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
:dusk="tab.slug + '-tab'"
:ref="tab.slug + '-tab'"
class="tab-item border-gray-200"
@click.prevent="handleTabClick(tab)"
@click.prevent="tabClicked(tab)"
>
<span class="capitalize">{{ tab.properties.title }}</span>
</a>
Expand Down Expand Up @@ -62,7 +62,7 @@
:is="getComponentName(field)"
ref="fields"
:class="{'remove-bottom-border': index === tab.fields.length - 1}"
:errors="validationErrors"
:errors="isResourceCreatePage() ? ourErrors : validationErrors"
:field="field"
:form-unique-id="formUniqueId"
:related-resource-id="relatedResourceId"
Expand All @@ -85,7 +85,7 @@
<component
v-if="field.from"
:is="getComponentName(field)"
:errors="validationErrors"
:errors="isResourceCreatePage() ? ourErrors : validationErrors"
:resource-id="getResourceId(field)"
:resource-name="field.resourceName"
:field="field"
Expand All @@ -106,16 +106,146 @@
</div>
</div>
</div>

<div
v-if="this.panel.wizardFormEnabled && this.isResourceCreatePage()"
class="flex flex-col md:flex-row md:items-center justify-center md:justify-end space-y-2 md:space-y-0 md:space-x-3">
<button
v-if="currentStep > 0"
@click="prevStep"
type="button"
class="border text-left appearance-none cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring ring-primary-200 dark:ring-gray-600 relative disabled:cursor-not-allowed inline-flex items-center justify-center bg-transparent border-transparent h-9 px-3 text-gray-600 dark:text-gray-400 hover:[&amp;:not(:disabled)]:bg-gray-700/5 dark:hover:[&amp;:not(:disabled)]:bg-gray-950"
dusk="prev-step-button">
<span class="flex items-center gap-1">{{ __('Previus') }}</span>
</button>

<button
v-if="currentStep < steps.length - 1"
@click="nextStep"
type="button"
class="border text-left appearance-none cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring ring-primary-200 dark:ring-gray-600 relative disabled:cursor-not-allowed inline-flex items-center justify-center shadow h-9 px-3 bg-primary-500 border-primary-500 hover:[&amp;:not(:disabled)]:bg-primary-400 hover:[&amp;:not(:disabled)]:border-primary-400 text-white dark:text-gray-900"
dusk="next-step-button">
<span class="flex items-center gap-1">{{ __('Next') }}</span>
</button>

<button
v-if="currentStep === steps.length - 1"
type="submit"
class="border text-left appearance-none cursor-pointer rounded text-sm font-bold focus:outline-none focus:ring ring-primary-200 dark:ring-gray-600 relative disabled:cursor-not-allowed inline-flex items-center justify-center shadow h-9 px-3 bg-primary-500 border-primary-500 hover:[&amp;:not(:disabled)]:bg-primary-400 hover:[&amp;:not(:disabled)]:border-primary-400 text-white dark:text-gray-900"
dusk="next-step-button">
<span class="flex items-center gap-1">{{ __('Save') }}</span>
</button>
</div>
</template>

<script>
import each from 'lodash/each';
import isNil from 'lodash/isNil';
import tap from 'lodash/tap';
import { HandlesFormRequest, Localization } from 'laravel-nova';
import { Errors } from 'form-backend-validation'
import BehavesAsPanel from '../mixins/BehavesAsPanel';
import HasTabs from "../mixins/HasTabs";
export default {
mixins: [BehavesAsPanel, HasTabs],
mixins: [BehavesAsPanel, HasTabs, HandlesFormRequest, Localization],
data: () => ({
tabMode: 'form',
ourErrors: new Errors(),
novaFormButtons: {
createAndAddOther: {},
cancel: {},
submit: {},
},
steps: [],
currentStep: 0,
}),
methods: {
tabClicked(tab) {
this.handleTabClick(tab)
this.currentStep = this.steps.findIndex(item => item.slug === this.selectedTab.slug);
},
nextStep() {
if (this.currentStep < this.steps.length - 1) {
let validations = [];
Array.from(this.selectedTab.fields).forEach(field => {
validations.push(field.validationRules)
})
Nova.request().post(`/nova-vendor/eminiarts/nova-tabs/validate`, this.createResourceFormData(validations))
.then(response => {
if (response.data.message === 'Yeah') {
this.currentStep++;
this.handleTabClick(this.steps[this.currentStep])
}
}).catch(error => {
this.ourErrors = new Errors(error.response.data.errors)
})
}
},
/**
* Create the form data for creating the resource.
*/
createResourceFormData(validations) {
return tap(new FormData(), formData => {
// append validations
formData.append('validations', JSON.stringify(validations))
each(this.selectedTab.fields, field => {
field.fill(formData)
})
if (!isNil(this.fromResourceId)) {
formData.append('fromResourceId', this.fromResourceId)
}
formData.append('viaResource', this.viaResource)
formData.append('viaResourceId', this.viaResourceId)
formData.append('viaRelationship', this.viaRelationship)
})
},
prevStep() {
if (this.currentStep > 0) {
this.currentStep--;
this.handleTabClick(this.steps[this.currentStep])
}
},
hideNovaFormButtons() {
this.novaFormButtons.cancel = document.querySelector('button[dusk="cancel-create-button"]');
this.novaFormButtons.submit = document.querySelector('button[dusk="create-button"]');
this.novaFormButtons.createAndAddOther = document.querySelector('button[dusk="create-and-add-another-button"]');
this.novaFormButtons.cancel.style.display = 'none';
this.novaFormButtons.submit.style.display = 'none';
this.novaFormButtons.createAndAddOther.style.display = 'none';
},
isResourceCreatePage() {
return Nova.$router.page.component === 'Nova.Create';
}
},
mounted() {
if (this.panel.wizardFormEnabled && this.isResourceCreatePage()) {
this.hideNovaFormButtons();
this.steps = this.getSortedTabs(this.tabs);
this.currentStep = this.steps.findIndex(item => item.slug === this.selectedTab.slug);
}
}
};
</script>
6 changes: 6 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

use Eminiarts\Tabs\Http\Controllers\ValidatorController;
use Illuminate\Support\Facades\Route;

Route::post('validate', [ValidatorController::class, 'index']);
13 changes: 13 additions & 0 deletions src/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Eminiarts\Tabs\Http\Controllers;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
46 changes: 46 additions & 0 deletions src/Http/Controllers/ValidatorController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Eminiarts\Tabs\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class ValidatorController extends Controller
{
/**
* Work
* @param Request $request
*/
public function index(Request $request)
{
$request->validate([
'validations' => ['required']
]);

$validations = $this->castArray(json_decode($request->input('validations')));

$request->validate($validations);

return response()->json([
'message' => 'Yeah'
]);
}

/**
* Cast array
*/
public function castArray(array $validations): array
{
$castedArray = [];

foreach ($validations as $validation) {
$really = (array) $validation;

foreach ($really as $key => $value) {
$cs[$key] = $value;
}
}

return $cs;
}
}
24 changes: 18 additions & 6 deletions src/Tabs.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
use Laravel\Nova\Contracts\ListableField;
use Laravel\Nova\Panel;
use Laravel\Nova\ResourceToolElement;
use Laravel\Nova\Http\Requests\NovaRequest;
use function is_array;
use function is_callable;

class Tabs extends Panel
{
/**
* NovaRequest
*/
protected NovaRequest $request;

/**
* @var mixed
*/
Expand Down Expand Up @@ -51,15 +57,23 @@ class Tabs extends Panel
* @param (\Closure():array|iterable)|array $fields
* @return void
*/
public function __construct($name, $fields = [])
public function __construct($name, $fields = [], NovaRequest $request = null)
{
$this->name = $name;
$this->preservedName = $name;
$this->request = $request;
$this->withComponent('tabs');

parent::__construct($name, $fields);
}

/**
* Wizard form enabled
*/
public function asWizard()
{
return $this->withMeta(['wizardFormEnabled' => true]);
}

/**
* Set the tabs slug.
Expand All @@ -69,7 +83,6 @@ public function __construct($name, $fields = [])
*/
public function withSlug($slug): Tabs
{

$this->slug = is_bool($slug) ? ($slug ? Str::slug($this->preservedName, '_') : null) : $slug;

return $this;
Expand All @@ -83,7 +96,6 @@ public function withSlug($slug): Tabs
*/
public function withCurrentColor(string $color): Tabs
{

$this->currentColor = $color;

return $this;
Expand All @@ -97,7 +109,6 @@ public function withCurrentColor(string $color): Tabs
*/
public function withErrorColor(string $color): Tabs
{

$this->errorColor = $color;

return $this;
Expand All @@ -112,6 +123,7 @@ public function withErrorColor(string $color): Tabs
public function rememberTabs($retain): Tabs
{
$this->retainTabPosition = $retain;

return $this;
}

Expand All @@ -131,7 +143,6 @@ protected function prepareFields($fields): array
$this->addFields($tab);
});


return $this->data ?? [];
}

Expand Down Expand Up @@ -223,7 +234,8 @@ public function addFields(TabContract $tab): self
'tab' => $tab->getName(),
'tabSlug' => $tab->getSlug(),
'tabPosition' => $tab->getPosition(),
'tabInfo' => Arr::except($tab->toArray(), ['fields', 'slug'])
'tabInfo' => Arr::except($tab->toArray(), ['fields', 'slug']),
'validationRules' => $field->getRules($this->request),
];

if ($field instanceof ListableField) {
Expand Down
19 changes: 19 additions & 0 deletions src/TabsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Eminiarts\Tabs;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
Expand All @@ -17,12 +18,30 @@ class TabsServiceProvider extends ServiceProvider
*/
public function boot(): void
{
$this->app->booted(function () {
$this->routes();
});

Nova::serving(function (ServingNova $event): void {
Nova::script('tabs', __DIR__.'/../dist/js/field.js');
Nova::style('tabs', __DIR__.'/../dist/css/field.css');
});
}

/**
* Routes
*/
protected function routes()
{
// if ($this->app->routesAreCached()) {
// return;
// }

Route::middleware(['nova'])
->prefix('nova-vendor/eminiarts/nova-tabs')
->group(__DIR__.'/../routes/api.php');
}

/**
* Register any application services.
*
Expand Down

0 comments on commit bdaed94

Please sign in to comment.