added first page ignore middleware [seo]

improved fix css switch
added basic meta filter [WIP]
master
A1Gard 2 months ago
parent 66cc2b1acc
commit 209541fb93

@ -141,15 +141,46 @@ class ClientController extends Controller
$area = 'group';
$title = $group->name;
$subtitle = $group->subtitle;
$posts = $group->posts()->orderByDesc('id')->paginate($this->paginate);
$posts = $group->posts()->where('status', 1)->orderByDesc('id')->paginate($this->paginate);
return view('client.group', compact('area', 'posts', 'title', 'subtitle', 'group'));
}
public function category(Category $category)
public function category(Category $category, Request $request)
{
$area = 'category';
$title = $category->name;
$subtitle = $category->subtitle;
$products = $category->products()->orderByDesc('id')->paginate($this->paginate);
$query = $category->products()->where('status', 1);
if ($request->has('only')) {
$query->where('stock_quantity', '>', 0);
}
if ($request->has('sort') && $request->input('sort') != '') {
switch ($request->input('sort')) {
case 'oldest':
$query = $query->orderBy('id');
break;
case 'cheap':
$query = $query->where('price', '<>', 0)->orderBy('price');
break;
case 'expensive':
$query = $query->orderByDesc('price');
break;
case 'fav':
$query = $query->orderByDesc('view');
break;
case 'sale':
$query = $query->orderByDesc('sell');
break;
default:
$query = $query->orderByDesc('id');
}
} else {
$query = $query->orderByDesc('id');
}
$products = $query->paginate($this->paginate);
return view('client.category', compact('area', 'products', 'title', 'subtitle', 'category'));
}

@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class IgnoreFirstPage
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->has('page') && $request->get('page') == '1'){
$q = $request->all();
unset($q['page']);
return redirect($request->url().'?'.http_build_query($q));
}
return $next($request);
}
}

@ -19,6 +19,9 @@ class Product extends Model implements HasMedia
{
use HasFactory, SoftDeletes, InteractsWithMedia, HasTranslations, HasTags, Metable;
public static $stock_status = ['IN_STOCK', 'OUT_STOCK', 'BACK_ORDER'];
public $translatable = ['name', 'excerpt', 'description', 'table'];
protected $casts = [
'qz' => 'array',
'qidz' => 'array'
@ -50,9 +53,6 @@ class Product extends Model implements HasMedia
return $this->quantities()->pluck('id')->toArray();
}
public static $stock_status = ['IN_STOCK', 'OUT_STOCK', 'BACK_ORDER'];
public $translatable = ['name', 'excerpt', 'description', 'table'];
public function registerMediaConversions(?Media $media = null): void
{
@ -155,6 +155,9 @@ class Product extends Model implements HasMedia
function hasDiscount()
{
if (!$this->isAvailable()){
return false;
}
return $this->discounts()->where('expire', '>', date('Y-m-d'))->count() > 0;
}
@ -281,6 +284,10 @@ class Product extends Model implements HasMedia
$price = $this->quantities()->min('price');
}
if (!$this->isAvailable()){
return __('Unavailable');
}
if ($this->hasDiscount()) {
$d = $this->activeDiscounts()->first();
if ($d->type == 'PRICE') {
@ -322,4 +329,15 @@ class Product extends Model implements HasMedia
return 0;
}
}
public function isAvailable(){
if ($this->stock_quantity == 0){
return false;
}
if ($this->stock_status != 'IN_STOCK'){
return false;
}
return true;
}
}

@ -13,6 +13,10 @@ return Application::configure(basePath: dirname(__DIR__))
)
->withMiddleware(function (Middleware $middleware) {
//
$middleware->use([
\App\Http\Middleware\IgnoreFirstPage::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//

@ -11,6 +11,10 @@ const $toast = useToast({
duration: 10000,
});
import MetaFilter from './../client-vue/meta-filter.vue';
app.component('meta-filter', MetaFilter);
app.use(ToastPlugin);
app.use(store);
app.mount('#app');

@ -0,0 +1,131 @@
<template>
<div id="meta-filter">
<form action="#product-list-view" id="filter-form" ref="frm">
<ul>
<li>
<div class="form-check form-switch">
<input class="form-check-input" value="1" type="checkbox" v-model="only" role="switch" id="flexSwitchCheckDefault"
name="only">
<label class="form-check-label" for="flexSwitchCheckDefault">
<!-- WIP translate -->
Only available
</label>
</div>
</li>
<li>
<label>
Sort by
</label>
<select name="sort" v-model="sort" class="form-control">
<option value="">
Newest
</option>
<option value="oldest">
Oldest
</option>
<option value="cheap">
Cheaper
</option>
<option value="expensive">
More expensive
</option>
<option value="fav">
Favorite
</option>
<option value="sale">
More sale
</option>
</select>
</li>
</ul>
<button type="submit" class="btn btn-outline-primary btn-sm w-100">
Apply filter
</button>
</form>
</div>
</template>
<script>
function getUrlVars()
{
var foo = window.location.href.split('?')[1].split('#')[0].split('&');
var dict = {};
var elem = [];
for (var i = foo.length - 1; i >= 0; i--) {
elem = foo[i].split('=');
dict[elem[0]] = elem[1];
}
return dict;
}
export default {
name: "meta-filter",
components: {},
data: () => {
return {
inited: false,
only: false,
sort: '',
}
},
props: {},
mounted() {
let gets = getUrlVars();
console.log(gets);
for( const get in gets) {
if (typeof(this[get]) == 'boolean'){
if (gets[get] == '1'){
this[get] = true;
}
}else{
this[get] = gets[get];
}
}
console.log(this.only);
setTimeout( () => {
this.inited = true;
},100);
},
computed: {
},
methods: {
apply() {
this.$refs.frm.submit();
},
},
watch:{
only(){
if (this.inited){
this.apply();
}
}
}
}
</script>
<style scoped>
#meta-filter {
ul {
list-style: none;
padding: 0;
li {
padding: .5rem;
background: #ffffff44;
margin-bottom: 4px;
label {
margin-top: 2px;
}
}
}
}
</style>

@ -112,3 +112,8 @@ ul.pagination {
}
}
.form-check-input:checked{
background-color: var(--xshop-primary);
border-right-color: var(--xshop-secondary);
}

@ -1,4 +1,4 @@
<section class='ProductGrid content'>
<section class='ProductGrid content' id="product-list-view">
<div class="{{gfx()['container']}}">
<h1>
{{$title}}
@ -40,6 +40,6 @@
</div>
@endforeach
</div>
{{$products->links()}}
{{$products->withQueryString()->links()}}
</div>
</section>

@ -1,4 +1,4 @@
<section class='ProductGridSidebar content'>
<section class='ProductGridSidebar content' id="product-list-view">
<div class="{{gfx()['container']}}">
<h1>
{{$title}}
@ -48,7 +48,7 @@
</div>
@endforeach
</div>
{{$products->links()}}
{{$products->withQueryString()->links()}}
</div>
@if(getSetting($data->area->name.'_'.$data->part.'_invert'))

@ -0,0 +1,5 @@
window.addEventListener('load',function () {
document.querySelectorAll('#product-list-view nav .pagination .page-link')?.forEach(function (el) {
el.setAttribute('href',el.getAttribute('href')+'#product-list-view');
});
});

@ -22,4 +22,13 @@
</ul>
</div>
</div>
<div class="side-item">
<h4>
{{__("Filter")}}
</h4>
<div class="side-data">
<meta-filter></meta-filter>
</div>
</div>
</aside>

@ -356,7 +356,8 @@ Route::prefix(config('app.panel.prefix'))->name('admin.')->group(
Route::get('theme/variable.css',[\App\Http\Controllers\ThemeController::class,'cssVariables'])->name('theme.variable.css');
Route::name('client.')->group(function (){
Route::middleware([\App\Http\Middleware\VisitorCounter::class])
->name('client.')->group(function (){
// index
Route::get('/', [\App\Http\Controllers\ClientController::class,'welcome'])->name('welcome');
Route::get('/posts', [\App\Http\Controllers\ClientController::class,'posts'])->name('posts');
@ -375,7 +376,7 @@ Route::name('client.')->group(function (){
Route::get('card/toggle/{product}', [\App\Http\Controllers\ClientController::class, 'productCardToggle'])->name('product-card-toggle');
Route::post('/comment/submit', [\App\Http\Controllers\ClientController::class,'submitComment'])->name('comment.submit');
})->middleware([\App\Http\Middleware\VisitorCounter::class]);
});
// to developer test
Route::get('login/as/{mobile}',function ($mobile){

Loading…
Cancel
Save