Compare commits

..

6 Commits

Author SHA1 Message Date
b145856ce9 Update setup with new component based layout
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 37s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2024-07-23 20:25:32 +10:00
45794ff109 Put back site_id middleware 2024-07-23 20:25:32 +10:00
c91a2fa8e5 Added Passkey login, fixed password reset as a result of updating laravel 2024-07-23 20:25:32 +10:00
b486a0eac4 Moving accounting commands into an Intuit/ namespace, updates to intuit module 2024-07-23 20:25:32 +10:00
28aa1f9dc8 Home page performance optimisations 2024-07-23 20:25:32 +10:00
f561139d45 Optimise Invoice 2024-07-23 20:25:32 +10:00
11 changed files with 141 additions and 237 deletions

View File

@ -2,10 +2,11 @@
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use App\Http\Requests\SiteEdit;
use App\Models\{Account,
Charge,
Invoice,
@ -187,63 +188,43 @@ class AdminController extends Controller
* Site setup
*
* @note This method is protected by the routes
* @param Request $request
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
* @param SiteEdit $request
* @return RedirectResponse
*/
public function setup(Request $request)
public function setup(SiteEdit $request)
{
if ($request->post()) {
$validated = $request->validate([
'site_name' => 'required|string|min:2|max:255',
'site_email' => 'required|string|email|max:255',
'site_address1' => 'required|string|min:2|max:255',
'site_address2' => 'nullable|string|min:2|max:255',
'site_city' => 'required|string|min:2|max:64',
'site_state' => 'required|string|min:2|max:32',
'site_postcode' => 'required|string|min:2|max:8',
'site_description' => 'nullable|string|min:5',
'site_phone' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
'site_fax' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
'site_tax' => 'required|regex:/[0-9 ]+/|size:14',
'social' => 'nullable|array',
'top_menu' => 'nullable|array',
'site_logo' => 'nullable|image',
'email_logo' => 'nullable|image',
]);
$site = config('site');
$images = ['site_logo','email_logo'];
$validated = collect($request->validated());
$site = config('site');
// Handle the images
foreach($images as $key)
if ($x=$request->validated($key))
$validated->put($key,$x->storeAs('site/'.$site->site_id,$x->getClientOriginalName(),'public'));
// @todo - not currently rendered in the home page
$validated['social'] = [];
$validated['top_menu'] = [];
foreach ($site->details as $oo)
if ($validated->has($oo->key)) {
// Dont set the following keys to null if they are null
if (in_array($oo->key,$images) && is_null($validated->get($oo->key)))
continue;
// Handle the images
foreach(['site_logo','email_logo'] as $key)
if (array_key_exists($key,$validated))
$validated[$key] = ($x=$validated[$key])->storeAs('site/'.$site->site_id,$x->getClientOriginalName(),'public');
$oo->value = $validated->get($oo->key) ?: '';
$oo->save();
foreach ($site->details as $oo)
if (array_key_exists($oo->key,$validated)) {
$oo->value = Arr::get($validated,$oo->key);
$oo->save();
unset($validated[$oo->key]);
}
// Left over values to be created.
foreach ($validated as $k=>$v) {
$oo = new SiteDetail;
$oo->key = $k;
$oo->value = $v ?: '';
$site->details()->save($oo);
$validated->forget($oo->key);
}
return redirect()
->back()
->with('success','Settings saved');
// Left over values to be created.
foreach ($validated as $k=>$v) {
$oo = new SiteDetail;
$oo->key = $k;
$oo->value = $v ?: '';
$site->details()->save($oo);
}
return view('theme.backend.adminlte.theme.backend.adminlte.a.setup');
return redirect()
->back()
->with('success','Settings saved');
}
}

View File

@ -6,7 +6,6 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\View;
use Closure;
use App\Models\Site;
@ -19,11 +18,13 @@ use App\Models\Site;
class SetSite
{
/**
* @param Request $request
* @param Closure $next
* Handle an incoming request.
*
* @param Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request,Closure $next)
public function handle(Request $request,\Closure $next): mixed
{
$so = new Site;

View File

@ -0,0 +1,43 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
class SiteEdit extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Gate::allows('wholesaler');
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'site_name' => 'required|string|min:2|max:255',
'site_email' => 'required|string|email|max:255',
'site_address1' => 'required|string|min:2|max:255',
'site_address2' => 'nullable|string|min:2|max:255',
'site_city' => 'required|string|min:2|max:64',
'site_state' => 'required|string|min:2|max:32',
'site_postcode' => 'required|string|min:2|max:8',
'site_description' => 'nullable|string|min:5',
'site_phone' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
'site_fax' => 'nullable|regex:/[0-9 ]+/|min:6|max:12',
'site_tax' => 'required|regex:/[0-9 ]+/|size:14',
'social' => 'nullable|array',
'top_menu' => 'nullable|array',
'site_logo' => 'nullable|image',
'email_logo' => 'nullable|image',
];
}
}

View File

@ -6,9 +6,11 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Leenooks\Traits\CompositeKeys;
use App\Traits\SiteID;
class SiteDetail extends Model
{
use CompositeKeys;
use CompositeKeys,SiteID;
protected $casts = [
'social' => 'array',
@ -55,13 +57,6 @@ class SiteDetail extends Model
public $timestamps = FALSE;
/* RELATIONS */
public function site()
{
return $this->belongsTo(Site::class);
}
/* ATTRIBUTES */
/**
@ -82,12 +77,11 @@ class SiteDetail extends Model
/**
* Set our value, casting it if required
*
* @param $key
* @param $value
* @return string
* @return void
* @throws \Exception
*/
public function setValueAttribute($value)
public function setValueAttribute($value): void
{
// Check that the value can be set
if (! $this->key)
@ -111,7 +105,7 @@ class SiteDetail extends Model
* @return mixed
* @throws \Exception
*/
public static function sample($key)
public static function sample($key): mixed
{
return Arr::get(self::sampleData,$key);
}

View File

@ -14,6 +14,7 @@ use Leenooks\Traits\UserSwitch;
use App\Interfaces\IDs;
use App\Notifications\ResetPassword as ResetPasswordNotification;
use App\Traits\SiteID;
/**
* Class User
@ -23,7 +24,7 @@ use App\Notifications\ResetPassword as ResetPasswordNotification;
*/
class User extends Authenticatable implements IDs
{
use HasFactory,HasApiTokens,Notifiable,UserSwitch,ScopeActive;
use HasFactory,HasApiTokens,Notifiable,UserSwitch,ScopeActive,SiteID;
private const CACHE_TIME = 3600;

View File

@ -20,15 +20,13 @@ class ResetPassword extends ResetPasswordNotification implements ShouldQueue
*/
public function toMail($notifiable): MailMessage
{
$site = Site::findOrFail($notifiable->site_id);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $this->token);
}
return (new MailMessage)
->markdown('email.user.passwordreset',[
'site'=>$site,
'site'=>$notifiable->site,
'user'=>$notifiable,
'reset_link'=>route('password.reset',$this->token,true),
]);

22
composer.lock generated
View File

@ -2288,16 +2288,16 @@
},
{
"name": "league/commonmark",
"version": "2.4.3",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "b7a7af3a23a818dcc5836e62e93e4b9ce4704481"
"reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b7a7af3a23a818dcc5836e62e93e4b9ce4704481",
"reference": "b7a7af3a23a818dcc5836e62e93e4b9ce4704481",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
"reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
"shasum": ""
},
"require": {
@ -2310,8 +2310,8 @@
},
"require-dev": {
"cebe/markdown": "^1.0",
"commonmark/cmark": "0.30.3",
"commonmark/commonmark.js": "0.30.0",
"commonmark/cmark": "0.31.0",
"commonmark/commonmark.js": "0.31.0",
"composer/package-versions-deprecated": "^1.8",
"embed/embed": "^4.4",
"erusev/parsedown": "^1.0",
@ -2333,7 +2333,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
"dev-main": "2.6-dev"
}
},
"autoload": {
@ -2390,7 +2390,7 @@
"type": "tidelift"
}
],
"time": "2024-07-22T12:31:21+00:00"
"time": "2024-07-22T18:18:14+00:00"
},
{
"name": "league/config",
@ -3056,11 +3056,11 @@
},
{
"name": "leenooks/laravel",
"version": "11.1.1",
"version": "11.1.2",
"source": {
"type": "git",
"url": "https://gitea.dege.au/laravel/leenooks.git",
"reference": "b9a3cd564759218fd6b3bc1ebf58e798833f014e"
"reference": "f32c29fa8c4b189add48bde26b7b7115be49355f"
},
"type": "library",
"extra": {
@ -3093,7 +3093,7 @@
"laravel",
"leenooks"
],
"time": "2024-07-22T14:11:06+00:00"
"time": "2024-07-23T08:47:36+00:00"
},
{
"name": "leenooks/passkey",

View File

@ -6,8 +6,8 @@ A new invoice has been generated on your account. A summary of that invoice is b
@component('mail::table')
| # | ID | Name | Amount |
| -: | - |:-----| ------:|
@foreach ($invoice->products() as $po)
| {{ $po->count }} | {{ $po->product_id }} | {{ $po->name }} | ${{ number_format($invoice->items->filter(function($item) use ($po) {return $item->product_id == $po->id; })->sum('total'),2) }} |
@foreach ($invoice->summary_products() as $item)
| {{ $item['services']->count() }} | {{ $item['product']->lid }} | {{ $item['product']->name }} | ${{ number_format($item['total'],2) }} |
@endforeach
||| Sub Total | ${{ number_format($invoice->sub_total,2) }} |
||| Tax | ${{ number_format($invoice->tax_total,2) }} |

View File

@ -21,9 +21,7 @@
<div class="card card-dark">
<div class="card-header">
<h1 class="card-title">Setup Configuration</h1>
@if(session()->has('success'))
<span class="ml-3 pt-0 pb-0 pr-1 pl-1 btn btn-outline-success"><small>{{ session()->get('success') }}</small></span>
@endif
<x-leenooks::button.success class="float-right"/>
</div>
<div class="card-body">
@ -31,182 +29,69 @@
@csrf
<div class="row">
<div class="col-4">
<div class="col-12 col-sm-6 col-lg-4">
<div class="row">
<div class="col-12">
<div class="form-group has-validation">
<label for="site_name">Organisation Name</label>
<input type="text" class="form-control form-control-border @error('site_name') is-invalid @enderror" id="site_name" name="site_name" placeholder="Site Name..." value="{{ old('site_name',$site->site_name) }}" required>
<span class="invalid-feedback" role="alert">
@error('site_name')
{{ $message }}
@else
Organisation Name is required.
@enderror
</span>
<span class="input-helper">System Name used everywhere.</span>
</div>
<div class="col">
<x-leenooks::form.text name="site_name" label="Organisation Name" helper="System Name used everywhere." feedback="Organisation Name is required!" :value="$site->site_name" required/>
</div>
</div>
<div class="row">
<div class="col-10">
<div class="form-group has-validation">
<label for="site_logo">Site Logo</label>
<input type="file" class="form-control @error('site_logo') is-invalid @enderror" id="site_logo" name="site_logo"><img class="col-12" src="{{ asset($site->site_logo) }}">
<span class="invalid-feedback" role="alert">
@error('site_logo')
{{ $message }}
@enderror
</span>
</div>
<div class="col">
<x-leenooks::form.text name="site_address1" label="Address" placeholder="Address..." :value="$site->site_address1" required/>
<x-leenooks::form.text name="site_address2" helper="At Least 1 address line required" :value="$site->site_address2"/>
</div>
</div>
<div class="row">
<div class="col-10">
<div class="form-group has-validation">
<label for="email_logo">Email Logo</label>
<input type="file" class="form-control @error('email_logo') is-invalid @enderror" id="email_logo" name="email_logo"><img class="col-12" src="{{ asset($site->email_logo) }}">
<span class="invalid-feedback" role="alert">
@error('email_logo')
{{ $message }}
@enderror
</span>
</div>
<div class="col-12 col-md-8">
<x-leenooks::form.text name="site_city" label="City" feedback="City is required" :value="$site->site_city" required/>
</div>
</div>
<div class="row">
<div class="col-12 col-md-4">
<x-leenooks::form.text name="site_state" label="State" feedback="State is required" :value="$site->site_state" required/>
</div>
<div class="col-12 col-md-4">
<x-leenooks::form.text name="site_postcode" label="Post Code" feedback="Postcode is required" :value="$site->site_postcode" required/>
</div>
</div>
</div>
<div class="col-4">
<div class="row">
<div class="col-12">
<div class="form-group has-validation">
<label for="site_address1">Address Lines</label>
<input type="text" class="form-control form-control-border @error('site_address1') is-invalid @enderror" id="site_address1" name="site_address1" placeholder="Address1" value="{{ old('site_address1',$site->site_address1) }}" required>
<input type="text" class="form-control form-control-border" id="site_address2" name="site_address2" placeholder="Address2" value="{{ old('site_address2',$site->site_address2) }}">
<span class="invalid-feedback" role="alert">
@error('site_address1')
{{ $message }}
@else
Atleast 1 address line required.
@enderror
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-group has-validation">
<label for="site_city">City</label>
<input type="text" class="form-control form-control-border @error('site_city') is-invalid @enderror" id="site_city" name="site_city" placeholder="City" value="{{ old('site_city',$site->site_city) }}">
<span class="invalid-feedback" role="alert">
@error('site_city')
{{ $message }}
@else
City is required.
@enderror
</span>
</div>
</div>
</div>
<div class="row">
<div class="col-12" style="display: inline-flex;">
<div class="form-group has-validation">
<label for="site_state">State</label>
<input type="text" class="form-control form-control-border @error('site_state') is-invalid @enderror col-5" id="site_state" name="site_state" placeholder="State" value="{{ old('site_state',$site->site_state) }}">
<span class="invalid-feedback" role="alert">
@error('site_state')
{{ $message }}
@else
State is required.
@enderror
</span>
</div>
<div class="form-group has-validation">
<label for="site_postcode">Postal Code</label>
<input type="text" class="form-control form-control-border @error('site_postcode') is-invalid @enderror col-5" id="site_postcode" name="site_postcode" placeholder="Postal Code" value="{{ old('site_postcode',$site->site_postcode) }}">
<span class="invalid-feedback" role="alert">
@error('site_postcode')
{{ $message }}
@else
Postcode is required.
@enderror
</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-lg-4">
<x-leenooks::form.text name="site_phone" label="Phone" :value="$site->site_phone"/>
<x-leenooks::form.text name="site_fax" label="Fax" :value="$site->site_fax"/>
<x-leenooks::form.email name="site_email" label="Email" feedback="Email is required" :value="$site->site_email" required/>
<x-leenooks::form.text name="site_tax" label="Tax Number" prepend="<small>ABN</small>" feedback="Tax Number is required" :value="$site->site_tax" required/>
</div>
<div class="col-4">
<div class="form-group has-validation">
<label for="site_phone">Phone</label>
<input type="text" class="form-control form-control-border @error('site_phone') is-invalid @enderror" id="site_phone" name="site_phone" placeholder="Site Phone" value="{{ old('site_phone',$site->site_phone) }}">
<span class="invalid-feedback" role="alert">
@error('site_phone')
{{ $message }}
@enderror
</span>
<div class="col-12 col-sm-6 col-lg-4">
<div class="row">
<div class="col">
<x-leenooks::form.file name="site_logo" label="Site Logo" helper="Choose a file" :value="$site->site_logo"><img class="col-12 p-2 border mb-4" src="{{ asset($site->site_logo) }}"></x-leenooks::form.file>
</div>
</div>
<div class="form-group has-validation">
<label for="site_fax">Fax</label>
<input type="text" class="form-control form-control-border @error('site_fax') is-invalid @enderror" id="site_fax" name="site_fax" placeholder="Site Fax" value="{{ old('site_fax',$site->site_fax) }}">
<span class="invalid-feedback" role="alert">
@error('site_fax')
{{ $message }}
@enderror
</span>
</div>
<div class="form-group has-validation">
<label for="site_email">Email</label>
<input type="email" class="form-control form-control-border @error('site_email') is-invalid @enderror" id="site_email" name="site_email" placeholder="Site Email" value="{{ old('site_email',$site->site_email) }}" required>
<span class="invalid-feedback" role="alert">
@error('site_email')
{{ $message }}
@else
Email required.
@enderror
</span>
</div>
<div class="form-group has-validation">
<label for="site_tax">Tax Number</label>
<span class="input-group-prepend">ABN</span>
<input type="text" class="form-control form-control-border @error('site_tax') is-invalid @enderror" id="site_tax" name="site_tax" placeholder="Site Tax" value="{{ old('site_tax',$site->site_tax) }}" required>
<span class="invalid-feedback" role="alert">
@error('site_tax')
{{ $message }}
@enderror
</span>
<div class="row">
<div class="col">
<x-leenooks::form.file name="email_logo" label="Email Logo" helper="Choose a file" :value="$site->email_logo"><img class="col-12 p-2 border mb-4" src="{{ asset($site->email_logo) }}"></x-leenooks::form.file>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-group has-validation">
<label for="site_description">Organisation Description</label>
<textarea class="form-control @error('site_description') is-invalid @enderror" id="site_description" name="site_description" placeholder="Site Description...">{{ old('site_description',$site->site_description) }}</textarea>
<span class="input-helper">Brief description of site.</span>
<span class="invalid-feedback" role="alert">
@error('site_description')
{{ $message }}
@enderror
</span>
</div>
<x-leenooks::form.textarea name="site_description" label="Organisation Description" placeholder="Site Description..." helper="Brief description of site." :value="$site->site_description"/>
</div>
</div>
<div class="row">
<div class="col-12">
<a href="{{ url('/home') }}" class="btn btn-danger">Cancel</a>
<x-leenooks::button.cancel/>
@can('wholesaler')
<button type="submit" name="submit" class="btn btn-success mr-0 float-right">@if ($site->exists)Save @else Add @endif</button>
<x-leenooks::button.submit class="float-right">Save</x-leenooks::button.submit>
@endcan
</div>
</div>

View File

@ -22,7 +22,7 @@
<div class="card">
<div class="card-header">
<div class="card-title">Update Settings</div>
@session('success')<x-leenooks::success class="float-right">{{ $value }}</x-leenooks::success>@endsession
<x-leenooks::button.success class="float-right"/>
</div>
<form method="POST" action="{{ url(request()->path(),[($o??$user)->id]) }}">
@ -45,7 +45,7 @@
<div class="row">
<div class="col">
<x-leenooks::form.password id="password_confirm" name="password_confirmation" icon="fa-lock" label="Password Confirm"/>
<x-leenooks::form.password id="password_confirm" name="password_confirmation" icon="fa-lock" label="Password Confirm" helper="Verify Password"/>
</div>
</div>
@ -101,8 +101,8 @@
<div class="row pt-3">
<div class="col">
<button type="reset" name="cancel" class="btn btn-danger">Cancel</button>
<button type="submit" name="submit" class="btn btn-success float-right">Save</button>
<x-leenooks::button.cancel/>
<x-leenooks::button.submit class="float-right">Save</x-leenooks::button.submit>
</div>
</div>
</div>

View File

@ -63,7 +63,8 @@ Route::get('admin/switch/stop',[SwitchUserController::class,'switch_stop'])
// Our Admin Routes - for wholesalers
Route::group(['middleware'=>['auth','role:wholesaler'],'prefix'=>'a'],function() {
// Site Setup
Route::match(['get','post'],'setup',[AdminController::class,'setup']);
Route::view('setup','theme.backend.adminlte.a.setup');
Route::post('setup',[AdminController::class,'setup']);
// Checkout Setup (Payments)
Route::get('checkout',[CheckoutController::class,'home']);