Rework products with components

This commit is contained in:
2024-08-14 22:16:09 +10:00
parent 1b581e9feb
commit f1031beff6
11 changed files with 132 additions and 302 deletions

View File

@@ -1,14 +1,14 @@
@extends('adminlte::layouts.app')
@section('htmlheader_title')
{{ $o->name ?: 'New Product' }}
{{ $pdo->name ?: 'New Product' }}
@endsection
@section('page_title')
{{ $o->name ?: 'New Product' }}
{{ $pdo->name ?: 'New Product' }}
@endsection
@section('contentheader_title')
{{ $o->name ?: 'New Product' }}
{{ $pdo->name ?: 'New Product' }}
@endsection
@section('contentheader_description')
@endsection
@@ -23,8 +23,8 @@
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header bg-dark d-flex p-0">
<ul class="nav nav-pills w-100 p-2">
<div class="card-header bg-dark p-2">
<ul class="nav nav-pills">
<li class="nav-item"><a class="nav-link active" href="#details" data-toggle="tab">Detail</a></li>
<li class="nav-item"><a class="nav-link" href="#services" data-toggle="tab">Services</a></li>
</ul>

View File

@@ -1,54 +1,32 @@
<!-- $o = Product::class -->
<!-- $pdo=Product::class -->
@use(App\Models\Group)
@use(App\Models\Invoice)
@use(App\Models\Product)
@use(App\Models\ProviderOauth)
<div class="row">
<div class="col-12">
<h3>Product Details @include('adminlte::widget.success_button')</h3>
<h3>Product Details <x-leenooks::button.success class="float-right"/></h3>
<hr>
<form class="g-0 needs-validation" method="POST" enctype="multipart/form-data" role="form">
<form method="POST">
@csrf
<div class="row">
<!-- Product ID -->
<div class="col-12 col-sm-9 col-md-4 col-xl-3">
@include('adminlte::widget.form_text',[
'label'=>'Product ID',
'icon'=>'fas fa-atom',
'id'=>'translate.name_short',
'old'=>'translate.name_short',
'name'=>'translate[name_short]',
'value'=>$o->pid,
])
<x-leenooks::form.text id="translate.name_short" name="translate[name_short]" icon="fa-atom" label="Product ID" old="translate.name_short" :value="$pdo->pid"/>
</div>
<!-- Product Name -->
<div class="col-12 col-sm-9 col-md-8 col-xl-9">
@include('adminlte::widget.form_text',[
'label'=>'Product Name',
'icon'=>'fas fa-atom',
'id'=>'translate.name_detail',
'old'=>'translate.name_detail',
'name'=>'translate[name_detail]',
'value'=>$o->name,
])
<x-leenooks::form.text id="translate.name_detail" name="translate[name_detail]" icon="fa-atom" label="Product Name" old="translate.name_detail" :value="$pdo->name"/>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-group @error('description') is-invalid @enderror">
<!-- Product Description -->
<label for="description_full" class="col-form-label">Product Description</label>
<div class="input-group">
<div class="w-100">
<textarea class="textarea" id="description" name="translate[description]" placeholder="Full Description">{!! old('description',$o->description) ?? '' !!}</textarea>
<span class="invalid-feedback" role="alert">
@error('description')
{{ $message }}
@enderror
</span>
</div>
</div>
</div>
<x-leenooks::form.textarea id="translate.description" name="translate[description]" label="Product Description" placeholder="Full Description..." old="translate.description" :value="$pdo->description"/>
</div>
</div>
@@ -57,48 +35,21 @@
<div class="row">
<!-- Active -->
<div class="col-6">
@include('adminlte::widget.form_toggle',[
'label'=>'Active',
'id'=>'active',
'old'=>'active',
'name'=>'active',
'value'=>$o->active ?? '',
])
<x-leenooks::form.toggle name="active" label="Active" :value="$pdo->active"/>
</div>
</div>
<div class="row">
<!-- Product Type -->
<div class="col-12">
@include('adminlte::widget.form_select',[
'label'=>'Product Type',
'icon'=>'fas fa-list',
'id'=>'model',
'old'=>'model',
'name'=>'model',
'options'=>\App\Models\Product::availableTypes()->transform(function($item) { return ['id'=>$item,'value'=>$item]; }),
'value'=>get_class($o->type),
])
<x-leenooks::form.select name="model" icon="fa-list" label="Product" choose="true" groupby="active" :value="get_class($pdo->type)" :options="Product::availableTypes()->transform(fn($item)=>['id'=>$item,'value'=>$item])"/>
</div>
</div>
<div class="row">
<!-- Supplied Product -->
<div class="col-12" id="supplier_product">
<div class="form-group">
<label for="model_id">Supplied Product</label>
<div class="input-group has-validation">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa-fw fas fa-shopping-cart"></i></span>
</div>
<select class="form-control @error('model_id') is-invalid @enderror" id="model_id" name="model_id"></select>
<span class="invalid-feedback" role="alert">
@error('model_id')
{{ $message }}
@enderror
</span>
</div>
</div>
<div class="col-12" id="supplied_product">
<x-leenooks::form.select name="model_id" icon="fa-shopping-cart" label="Supplied Product"/>
</div>
</div>
@@ -107,26 +58,10 @@
<div class="col-12">
<span class="h5">Accounting</span>
<hr>
@foreach (\App\Models\ProviderOauth::providers() as $apo)
<div class="form-group">
<label for="{{ $x=sprintf('acc_%d',$apo->id) }}">{{ ucfirst($apo->name) }}</label>
<div class="input-group has-validation">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa-fw fas fa-calculator"></i></span>
</div>
<select class="form-control @error($x) is-invalid @enderror select" id="{{ $x }}" name="{{ sprintf('accounting[%d]',$apo->id) }}">
<option></option>
@foreach ($o->accounting($apo->name) as $v)
<option value="{{ $v['id'] }}" @if($o->provider_ref($apo) === (string)$v['id'])selected @endif>{{ $v['value'] }}</option>
@endforeach
</select>
<span class="invalid-feedback" role="alert">
@error($x)
{{ $message }}
@enderror
</span>
</div>
</div>
<!-- @todo When returning with a bad value old() is not selecting the previous value, may need to have the full html here instead of a component -->
@foreach (ProviderOauth::providers() as $apo)
<x-leenooks::form.select :id="sprintf('acc_%d',$apo->id)" :name="sprintf('accounting[%d]',$apo->id)" icon="fa-calculator" :label="ucfirst($apo->name)" :choose="true" :value="$pdo->provider_ref($apo)" old="accounting" :options="$pdo->accounting($apo->name)"/>
@endforeach
</div>
</div>
@@ -136,20 +71,20 @@
<span class="h5">Pricing</span><small> Ex Taxes</small>
<hr>
@error('pricing')
@include('adminlte::widget.errors')
<x-leenooks::errors/>
@enderror
<ul class="nav nav-pills w-100 pl-0 pt-2 pb-2">
@foreach(\App\Models\Group::pricing()->active()->get() as $go)
@foreach($g=Group::pricing()->active()->get() as $go)
<li class="nav-item"><a class="nav-link @if(! $loop->index)active @endif" href="#pg_{{ $go->id }}" data-toggle="tab">{{ $go->name }}</a></li>
@endforeach
</ul>
<div class="tab-content">
@foreach(\App\Models\Group::pricing()->active()->get() as $go)
<div class="tab-pane fade @if(! $loop->index)show active @endif" id="pg_{{ $go->id }}" role="tabpanel">
@foreach($g as $go)
<div class="tab-pane fade @if(! $loop->index)show active @endif" id="pg_{{ $go->id }}">
@foreach(\App\Models\Invoice::billing_periods as $bp=>$detail)
@foreach(Invoice::billing_periods as $bp=>$detail)
<div class="row">
<div class="col-6">
<div class="form-group">
@@ -158,13 +93,13 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa-fw fas fa-calculator"></i></span>
</div>
<input type="text" class="form-control text-right @error($x) is-invalid @enderror" id="{{ $x }}" name="{{ sprintf('pricing[%d][%d][base]',$bp,$go->id) }}" {{ ($ca=$o->charge_available($bp,$go)) ? 'value' : 'placeholder' }}="{{ $c=$o->charge($bp,$go,'base') }}" @if(is_null($c) || ! $ca) disabled @endif>
<input type="text" class="form-control text-right @error($x) is-invalid @enderror" id="{{ $x }}" name="{{ sprintf('pricing[%d][%d][base]',$bp,$go->id) }}" {{ ($ca=$pdo->charge_available($bp,$go)) ? 'value' : 'placeholder' }}="{{ $c=$pdo->charge($bp,$go,'base') }}" @if(is_null($c) || ! $ca) disabled @endif>
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="{{ sprintf('pricing[%d][show]',$bp) }}" @if($c && $ca)checked @endif>
</div>
</div>
<span class="invalid-feedback" role="alert">
<span class="invalid-feedback">
@error($x)
{{ $message }}
@enderror
@@ -180,8 +115,8 @@
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa-fw fas fa-cog"></i></span>
</div>
<input type="text" class="form-control text-right @error($x) is-invalid @enderror" id="{{ $x }}" name="{{ sprintf('pricing[%d][%d][setup]',$bp,$go->id) }}" {{ $ca ? 'value' : 'placeholder' }}="{{ $c=$o->charge($bp,$go,'setup') }}" @if(is_null($c) || ! $ca) disabled @endif>
<span class="invalid-feedback" role="alert">
<input type="text" class="form-control text-right @error($x) is-invalid @enderror" id="{{ $x }}" name="{{ sprintf('pricing[%d][%d][setup]',$bp,$go->id) }}" {{ $ca ? 'value' : 'placeholder' }}="{{ $c=$pdo->charge($bp,$go,'setup') }}" @if(is_null($c) || ! $ca) disabled @endif>
<span class="invalid-feedback">
@error($x)
{{ $message }}
@enderror
@@ -198,12 +133,9 @@
</div>
<div class="row">
<!-- Buttons -->
<div class="col-12">
<a href="{{ url('/home') }}" class="btn btn-danger">Cancel</a>
@can('wholesaler')
<button type="submit" name="submit" class="btn btn-success mr-0 float-right">@if ($o->exists)Save @else Add @endif</button>
@endcan
<div class="col">
<x-leenooks::button.reset/>
<x-leenooks::button.submit class="float-right">Save</x-leenooks::button.submit>
</div>
</div>
</form>
@@ -211,14 +143,9 @@
</div>
@section('page-scripts')
@css(select2)
@css(simplemde)
@js(select2,autofocus)
@js(simplemde)
<script type="text/javascript">
// Get a list of supplier items matching this type to populate model_id
function supplier_products(type,destination,selected) {
function supplied_products(type,destination,selected) {
destination.prop('disabled',true);
$.ajax({
@@ -226,11 +153,10 @@
dataType: 'json',
data: {type: type},
cache: false,
url: '{{ url('api/a/supplier_products') }}',
url: '{{ url('a/supplied_products') }}',
timeout: 2000,
error: function(x) {
// @todo add a spinner
//spinner.toggleClass('d-none').toggleClass('fa-spin');
alert('Failed to submit');
},
success: function(data) {
@@ -246,28 +172,24 @@
}
$(document).ready(function() {
new SimpleMDE({ element: $('.textarea')[0], forceSync: true });
$('#model').on('change',function(item) {
if ($(this).val()) {
$('#supplier_product').show();
supplier_products($(this).val(),$('#model_id'));
$('#supplied_product').show();
supplied_products($(this).val(),$('#model_id'));
} else {
$('#supplier_product').hide();
$('#supplied_product').hide();
}
});
$('#model_id').select2();
$('.select').select2();
// After we render the page, hide the supplier_product if this product has no model.
// After we render the page, hide the supplied_product if this product has no model.
// We do this here, because adding d-none to the div results in the select2 input not presenting correctly
if (! $('#model').val())
$('#supplier_product').hide();
$('#supplied_product').hide();
else
supplier_products($('#model').val(),$('#model_id'),{{ old('model_id',$o->model_id) }});
supplied_products($('#model').val(),$('#model_id'),{{ old('model_id',$pdo->model_id) }});
// @todo when there is a value in 1 of the two boxes in the row, the toggle incorrectly disables one and enables the other
$('input[type=checkbox]').on('click',function(item) {
var input = $(this).parent().parent().parent().find('input[type="text"]');
input.prop('disabled',(i,v)=>!v);

View File

@@ -1,25 +1,18 @@
<!-- $o = Product::class -->
<!-- $o=Product::class -->
@use(App\Models\Product)
<div class="card card-dark">
<div class="card-header">
<h1 class="card-title">Product Configuration</h1>
</div>
<div class="card-body">
<form class="g-0 needs-validation" method="POST" enctype="multipart/form-data" role="form">
<form method="POST">
@csrf
<div class="row">
<div class="col-12 col-sm-9 col-md-6 col-xl-5">
@include('adminlte::widget.form_select',[
'label'=>'Product',
'icon'=>'fas fa-list',
'id'=>'product_id',
'old'=>'product_id',
'name'=>'product_id',
'groupby'=>'active',
'options'=>\App\Models\Product::get()->sortBy(function($item) { return ($item->active ? '0' : '1').$item->name; })->transform(function($item) { return ['id'=>$item->id,'value'=>$item->name,'active'=>$item->active]; }),
'value'=>isset($o) ? $o->id : NULL,
])
<x-leenooks::form.select name="product_id" icon="fa-list" label="Product" choose="true" groupby="active" :value="$po?->id ?? ''" :options="Product::get()->sortBy(fn($item)=>($item->active ? '0' : '1').$item->name)->transform(fn($item)=>['id'=>$item->id,'value'=>$item->name,'active'=>$item->active])"/>
</div>
</div>
</form>
@@ -27,13 +20,10 @@
</div>
@section('page-scripts')
@css(select2)
@js(select2,autofocus)
<script type="text/javascript">
$(document).ready(function() {
$('#product_id').on('change',function(item) {
window.location.href = '{{ url('a/product/details') }}/'+item.target.value;
window.location.href = '{{ url('a/product') }}/'+item.target.value;
});
});
</script>

View File

@@ -1,8 +1,8 @@
<!-- $o = Product::class -->
<!-- $pdo=Product::class -->
<div class="row">
@if(count($o->services))
@if(count($pdo->services))
<div class="col-7">
<table class="table table-hover w-100" id="table">
<table class="table table-hover" id="table">
<thead>
<tr>
<th>ID</th>
@@ -17,7 +17,7 @@
</thead>
<tbody>
@foreach ($o->services as $so)
@foreach ($pdo->services as $so)
<tr>
<td><a href="{{ url('u/service',[$so->id]) }}">{{ $so->sid }}</a></td>
<td>{{ $so->start_at ? $so->start_at->format('Y-m-d') : '-' }}</td>
@@ -38,24 +38,12 @@
@endif
</div>
@pa(datatables,select)
@section('page-scripts')
@css(datatables,bootstrap4|rowgroup|select|searchpanes|searchpanes-left)
@js(datatables,bootstrap4|rowgroup|select|searchpanes)
<style>
tr.odd td:first-child,
tr.even td:first-child {
padding-left: 3em;
}
table.dataTable tr.dtrg-group.dtrg-level-1 td {
background-color: #e0e0e0;
color: #4c110f;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
@if(count($o->services))
@if(count($pdo->services))
$('#table').DataTable({
paging: true,
pageLength: 25,
@@ -66,34 +54,12 @@
autoWidth: false,
fixedHeader: true,
order: [[4,'desc'],[0,'asc']],
rowGroup: {
dataSrc: 4,
},
columnDefs: [
{
targets: [4],
visible: false,
}
],
language: {
searchPanes: {
clearMessage: 'Clear',
title: 'Filters: %d',
collapse: 'Filter',
}
},
searchPanes: {
cascadePanes: true,
viewTotal: true,
layout: 'columns-1',
dataLength: 20,
controls: false,
},
dom: '<"dtsp-verticalContainer"<"dtsp-verticalPanes"P><"dtsp-dataTable"Bfrtip>>',
});
$('tbody').on('click','tr', function () {
$(this).toggleClass('selected');
});
@endif
});