added single product route

added aria product theme part
added tooltip to compare add to card
fixed has discount bug
fixed toggle card bugs [quantity]
improved ui different color for secondary color
fixed tag url
master
A1Gard 2 months ago
parent 52c0f48bc1
commit 1d4622f270

@ -33,6 +33,7 @@ class clientAssetGenerator extends Command
$vars['xshop-background'] = $gfxes['background'] ?? '#000000';
$vars['xshop-primary'] = $gfxes['primary'] ?? '#6e0000';
$vars['xshop-diff'] = getGrayscaleTextColor($gfxes['primary']) ?? '#6e0000';
$vars['xshop-diff2'] = getGrayscaleTextColor($gfxes['secondary']) ?? '#6e0000';
$vars['xshop-secondary'] = $gfxes['secondary'] ?? '#ff0000';
$vars['xshop-text'] = $gfxes['text'] ?? '#111111';
$vars['xshop-border-radius'] = $gfxes['border-radius'] ?? '7px';

@ -975,3 +975,39 @@ function isGuestMaxAttemptTry($action, $max = 5, $minutes = 60)
return false;
}
}
/**
* home url to best experience for multi lang shops
* @return string
*/
function homeUrl(){
return \route('client.welcome');
}
/**
* tag url to best experience for multi lang shops
* @return string
*/
function tagUrl($slug){
return route('client.tag',$slug);
}
function usableProp($props)
{
$result = [];
foreach ($props as $prop) {
$tmp = [];
foreach (json_decode($prop->options) as $item) {
$tmp[$item->value] = $item->title;
}
$result[$prop->name]['data'] = $tmp;
$result[$prop->name]['icon'] = $prop->icon;
$result[$prop->name]['unit'] = $prop->unit;
$result[$prop->name]['searchable'] = $prop->searchable;
$result[$prop->name]['priceable'] = $prop->priceable;
$result[$prop->name]['type'] = $prop->type;
$result[$prop->name]['label'] = $prop->label;
}
return $result;
}

@ -13,6 +13,7 @@ use App\Models\Product;
use App\Models\Quantity;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Plank\Metable\Meta;
use Spatie\Tags\Tag;
@ -147,6 +148,14 @@ class ClientController extends Controller
return view('client.group', compact('area', 'posts', 'title', 'subtitle', 'group'));
}
public function product(Product $product)
{
$area = 'product';
$title = $product->name;
$subtitle = $product->excerpt; // WIP SEO
return view('client.default-list', compact('area', 'product', 'title', 'subtitle'));
}
public function category(Category $category, Request $request)
{
$area = 'category';
@ -267,10 +276,22 @@ class ClientController extends Controller
$cards = json_decode(\Cookie::get('card'), true);
$qs = json_decode(\Cookie::get('q'), true);
if (in_array($product->id, $cards)) {
$msg = "Product removed from card";
$i = array_search($product->id, $cards);
unset($cards[$i]);
unset($qs[$i]);
$found = false;
foreach ($cards as $i => $card) {
if ($card == $product->id && $qs[$i] == $quantity) {
$found = true;
break;
}
}
if ($found) {
$msg = "Product removed from card";
unset($cards[$i]);
unset($qs[$i]);
}else{
$cards[] = $product->id;
$qs[] = $quantity;
$msg = "Product added to card";
}
} else {
$cards[] = $product->id;
$qs[] = $quantity;
@ -427,6 +448,8 @@ class ClientController extends Controller
guestLog('sms');
$customer = Customer::where('mobile', $request->input('tel'));
$code = rand(11111, 99999);
Log::info('auth code: '.$code );
if ($customer->count() == 0) {
$customer = new Customer();
$customer->mobile = $request->input('tel');

@ -158,7 +158,11 @@ class Product extends Model implements HasMedia
if (!$this->isAvailable()){
return false;
}
return $this->discounts()->where('expire', '>', date('Y-m-d'))->count() > 0;
return $this->discounts()
->where(function ($query) {
$query->where('expire', '>=', date('Y-m-d'))
->orWhereNull('expire');
})->count() > 0;
}
@ -278,8 +282,7 @@ class Product extends Model implements HasMedia
public function webUrl()
{
return '#';// WIP
return route('');
return route('client.product',$this->slug);
}

@ -47,7 +47,7 @@ class PropSeeder extends Seeder
'label' => __('Internal storage'),
'name'=>'hdd',
'type'=>'select',
'options' => '[{"title":"16 Gig","value":"16"},{"title":"32 Gig","value":"32"},{"title":"64 gig","value":"64"},{"title":"128 Gig","value":"128"},{"title":"256 G","value":"256"}]',
'options' => '[{"title":"16 Gig","value":"16"},{"title":"32 Gig","value":"32"},{"title":"64 gig","value":"64"},{"title":"128 Gig","value":"128"},{"title":"256 Gig","value":"256"}]',
'searchable'=> 1,
'priceable' => 1,
'icon' => 'ri-hard-drive-3-line',

@ -1,4 +1,3 @@
import 'bootstrap';
import { createApp } from 'vue';
import ToastPlugin from 'vue-toast-notification';
import {useToast} from 'vue-toast-notification';
@ -11,9 +10,10 @@ const $toast = useToast({
duration: 10000,
});
import MetaFilter from '../client-vue/MetaFilter.vue';
app.component('meta-filter', MetaFilter);
import QunatotiesAddToCard from "../client-vue/QuantitiesAddToCard.vue";
app.component('quantities-add-to-card', QunatotiesAddToCard);
app.use(ToastPlugin);
app.use(store);

@ -1,3 +1,5 @@
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle.min.js';
window.addEventListener('load', function () {
const API_COOKIE_NAME = 'last_api_call';
const COOKIE_EXPIRY_MINUTES = 59;
@ -46,4 +48,17 @@ window.addEventListener('load', function () {
} else {
console.log('Data was sent recently. Skipping this time.');
}
// bootstrap fix start
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
// add table class autho
document.querySelectorAll('.content table')?.forEach(function (el) {
el.classList.add('table')
});
// bootstrap fix end
});

@ -0,0 +1,187 @@
<template>
<div id="quantities-add-to-card">
<template v-for="(q,i) in qz">
<div :class="`q `+(selected == i?'selected-q':'')" v-if="q.count > 0" @click="select(i)">
<div class="row">
<template v-for="(v,k) in data2object(q.data)">
<div :class="calcClass(props[k])">
<div v-if="props[k].type == 'color'">
<span class="q-color float-start" :style="`background-color:${v}`"></span>
</div>
<div
v-if="props[k].type == 'select' || props[k].type == 'singlemulti' || props[k].type == 'multi'">
<span>
{{ props[k].label }}:
</span>
<b>
{{ props[k].data[v] }}
</b>
</div>
</div>
</template>
<div class="col-md" v-if="discount != null">
<strong>
{{ calcDiscount(q.price) }}
</strong>
&nbsp;
<del class="text-muted">
{{ commafy(q.price.toString()) }} {{ currency }}
</del>
</div>
<div class="col-md" v-else>
{{ commafy(q.price.toString()) }} {{ currency }}
</div>
</div>
</div>
</template>
<a
class="btn btn-outline-primary btn-lg" @click="add2card">
<i class="ri-shopping-bag-3-line"></i>
Add to card
<!-- WIP translate-->
</a>
</div>
</template>
<script>
function commafy(num) {
if (typeof num !== 'string') {
return '';
}
let str = uncommafy(num.toString()).split('.');
if (str[0].length >= 4) {
str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
}
if (str[1] && str[1].length >= 4) {
str[1] = str[1].replace(/(\d{3})/g, '$1 ');
}
return str.join('.');
}
function uncommafy(txt) {
return txt.split(',').join('');
}
export default {
name: "quantities-add-to-card",
components: {},
data: () => {
return {
selected: null,
}
},
props: {
qz: {
default: []
},
props: {
default: {},
},
currency: {
default: '$',
},
cardLink: {
default: '',
},
discount: {
default: null,
},
},
mounted() {
},
computed: {},
methods: {
select(i) {
document.querySelector('#price').innerText = commafy(this.qz[i].price.toString()) + ' ' + this.currency;
this.selected = i;
},
async add2card() {
if (this.selected == null) {
window.$toast.warning('You need to select one quantity');
return;
}
let resp = await axios.get(this.cardLink + '?quantity=' + this.qz[this.selected].id);
if (resp.data.success) {
window.$toast.success(resp.data.message);
document.querySelectorAll('.card-count')?.forEach(function (el2) {
el2.innerText = resp.data.data.count;
});
} else {
window.$toast.error("Error!");
}
},
calcDiscount(price) {
if (this.discount == null) {
return '-';
}
if (this.discount.type == 'PERCENT') {
return commafy(
parseInt(((100 - this.discount.amount) * price) / 100).toString()
) + ' ' + this.currency;
} else {
return commafy((price - this.discount.amount).toString()) + ' ' + this.currency;
}
},
calcClass(prop) {
let cls = '';
if (prop.type == 'color') {
cls = 'col-md-1';
} else {
cls = 'col-md';
}
cls += ' ' + prop.type;
return cls;
},
data2object(data) {
try {
return JSON.parse(data);
} catch {
return '';
}
},
commafy: commafy,
}
}
</script>
<style scoped>
#quantities-add-to-card {
}
.q {
border: 1px solid var(--xshop-primary);
border-radius: var(--xshop-border-radius);
margin-bottom: .5rem;
align-items: center;
cursor: pointer;
transition: 300ms;
padding: 7px;
&:hover {
background: var(--xshop-primary);
color: var(--xshop-diff);
}
}
.q-color {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
}
.selected-q {
background: var(--xshop-secondary);
color: var(--xshop-diff2);
}
</style>

@ -0,0 +1,44 @@
<template>
<div id="quantity">
</div>
</template>
<script>
export default {
name: "quantity",
components: {},
data: () => {
return {
modelValue: 1,
}
},
props: {
min:{
default: 1,
type: Number,
},
max: {
default: 99,
type: Number,
},
xname:{
default: null,
type: null|String,
},
},
mounted() {
},
computed: {},
methods: {}
}
</script>
<style scoped>
#quantity {
direction: ltr;
border: 1px solid gray;
}
</style>

@ -63,3 +63,11 @@ body{
font-size: 25px;
}
}
[id^="hidden-img"]{
display: none;
}
#hidden-images{
display: none;
}

@ -44,7 +44,7 @@ a, a:visited {
&:hover {
border-color: var(--xshop-secondary);
color: var(--xshop-diff) !important;
color: var(--xshop-diff2) !important;
background: var(--xshop-secondary);;
}
}
@ -52,7 +52,7 @@ a, a:visited {
.btn-secondary {
background: var(--xshop-secondary);
border-color: var(--xshop-secondary);
color: var(--xshop-diff) !important;
color: var(--xshop-diff2) !important;
&:hover {
border-color: var(--xshop-primary);
@ -99,7 +99,7 @@ ul.pagination {
&:hover{
background: var(--xshop-secondary);
color: var(--xshop-diff);
color: var(--xshop-diff2);
}
}
}
@ -117,3 +117,23 @@ ul.pagination {
background-color: var(--xshop-primary);
border-right-color: var(--xshop-secondary);
}
.modal.lightbox{
backdrop-filter: blur(7px);
.ratio{
background: #33333355 !important;
border-radius: var(--xshop-border-radius);
overflow: hidden;
}
}
.custom-tooltip {
--bs-tooltip-bg: var(--xshop-primary);
--bs-tooltip-color: var(--xshop-diff);
}
.accordion-button:not(.collapsed){
background: transparent;
}

@ -102,7 +102,7 @@
{{$dis->code}}
</td>
<td>
{{$dis->expire->ldate('Y-m-d H:i:s')}}
{{$dis->expire?->ldate('Y-m-d H:i:s')??'-'}}
</td>
<td>
<a href="{{ route('admin.discount.destroy',$dis->id) }}" class="btn btn-danger" data-id="{{$dis->id}}">

@ -208,7 +208,7 @@
@case('expire')
@case('created_at')
@case('updated_at')
{{$item->$col->ldate("Y-m-d H:i")}}
{{$item->$col?->ldate("Y-m-d H:i")??'-'}}
@break
@case('icon')
<i class="{{$item->$col}}"></i>

@ -35,7 +35,7 @@
<div class="col-md-6">
{{__("Tags")}}:
@foreach($post->tags as $tag)
<a href="{{route('client.tag',$tag->slug)}}" class="tag me-2">
<a href="{{tagUrl($tag->slug)}}" class="tag me-2">
<i class="ri-price-tag-line"></i>
{{$tag->name}}
</a>

@ -28,7 +28,7 @@
<div class="col-md-6">
{{__("Tags")}}:
@foreach($post->tags as $tag)
<a href="{{route('client.tag',$tag->slug)}}" class="tag me-2">
<a href="{{tagUrl($tag->slug)}}" class="tag me-2">
<i class="ri-price-tag-line"></i>
{{$tag->name}}
</a>

@ -0,0 +1,223 @@
<section id='ProductAria' class="content">
<div class="{{gfx()['container']}}">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{{homeUrl()}}">
{{config('app.name')}}
</a>
</li>
<li class="breadcrumb-item">
<a href="{{$product->category->webUrl()}}">
{{$product->category->name}}
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">
{{$product->name}}
</li>
</ol>
</nav>
<div class="row">
<div class="col-lg-5">
<a href="{{$product->originalImageUrl()}}" id="aria-main-img" class="light-box"
data-gallery="aria-products">
<img src="{{$product->originalImageUrl()}}" alt="{{$product->name}}">
</a>
<div id="aria-img-slider">
@foreach($product->getMedia() as $media)
<div class="item">
<a href="{{$media->getUrl('product-image')}}">
<img src="{{$media->getUrl('product-image')}}" alt="{{$product->name}}">
</a>
</div>
@endforeach
</div>
</div>
<div class="col-lg-7" id="aria-product-detail">
<a class="fav-btn" data-slug="{{$product->slug}}" data-is-fav="{{$product->isFav()}}"
data-bs-custom-class="custom-tooltip"
data-bs-toggle="tooltip" data-bs-placement="auto" title="{{__("Add to / Remove from favorites")}}"
>
<i class="ri-heart-line"></i>
<i class="ri-heart-fill"></i>
</a>
<a class="compare-btn" data-slug="{{$product->slug}}"
data-bs-custom-class="custom-tooltip"
data-bs-toggle="tooltip" data-bs-placement="auto" title="{{__("Add to/ Remove from compare list")}}">
<i class="ri-scales-3-line"></i>
</a>
<h1>
{{$product->name}}
</h1>
<div class="row">
<div id="price" class="col">
{{$product->getPrice()}}
</div>
@if($product->hasDiscount())
<div id="price-old" class="col">
{{$product->oldPrice()}}
</div>
@endif
</div>
<div class="description border-end-0 ps-3">
<p>
{{$product->excerpt}}
</p>
</div>
<div class="mt-4">&nbsp;</div>
@if($product->quantities()->count()>0)
<quantities-add-to-card
:qz='@json($product->quantities)'
:props='@json(usableProp($product->category->props))'
currency="{{config('app.currency.symbol')}}"
card-link="{{ route('client.product-card-toggle',$product->slug) }}"
@if($product->hasDiscount())
:discount='@json($product->activeDiscounts()->first())'
@endif
></quantities-add-to-card>
@else
<a href="{{ route('client.product-card-toggle',$product->slug) }}"
class="btn btn-outline-primary add-to-card btn-lg">
<i class="ri-shopping-bag-3-line"></i>
{{__("Add to card")}}
</a>
@endif
<div class="mt-4">&nbsp;</div>
@if($product->sku != null && $product->sku != '')
<div class="aria-product-data">
<span>
{{__("SKU")}}:
</span>
<b>
{{$product->sku}}
</b>
</div>
@endif
@if($product->categories()->count() > 0)
<div class="aria-product-data">
<span>
{{__("Categories")}}:
</span>
@foreach($product->categories()->where('id','<>',$product->category->id)->get() as $cat)
<a href="{{$cat->webUrl()}}">
{{$cat->name}},
</a>
@endforeach
<a href="{{$product->category->webUrl()}}">
{{$product->category->name}}
</a>
</div>
@endif
@if($product->tags()->count() > 0)
<div class="aria-product-data">
<span>
{{__("Tags")}}:
</span>
@foreach($product->tags as $tag)
<a href="{{tagUrl($tag->slug)}}" class="tag me-2">
<i class="ri-price-tag-line"></i>
{{$tag->name}}
</a>
@endforeach
</div>
@endif
</div>
</div>
<div class="accordion mt-4" id="product-detail">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#desc" aria-expanded="true"
aria-controls="desc">
{{__("Description")}}
</button>
</h2>
<div id="desc" class="accordion-collapse collapse show" data-bs-parent="#product-detail">
<div class="accordion-body">
{!! $product->description !!}
</div>
</div>
</div>
@if($product->table != null || trim($product->table) != '')
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#table"
aria-expanded="false" aria-controls="table">
{{__("Product table")}}
</button>
</h2>
<div id="table" class="accordion-collapse collapse" data-bs-parent="#product-detail">
<div class="accordion-body">
{!! $product->table !!}
</div>
</div>
</div>
@endif
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#info" aria-expanded="false" aria-controls="info">
{{__("Information")}}
</button>
</h2>
<div id="info" class="accordion-collapse collapse" data-bs-parent="#product-detail">
<div class="accordion-body">
<table class="table table-striped table-bordered table-striped">
<tr>
<th class="w-50">
{{__("Item")}}
</th>
<th>
{{__("Value")}}
</th>
</tr>
@foreach($product->fullMeta() as $meta)
<tr>
<td>
<i class="{{$meta['data']->icon}}"></i>
&nbsp;
{{$meta['data']->label}}
</td>
<td class="text-center">
{!! $meta['human_value'] !!}
</td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
<h3 class="mt-4">
{{__("Related products")}}
</h3>
<div id="rel-products">
@foreach($product->category->products()->where('status',1)->limit(10)->get() as $p)
<div class="item">
<div class="aria-product-list">
<a href="{{$p->imgUrl()}}">
<img src="{{$p->imgUrl()}}" alt="{{$p->name}}">
<h5>
{{$p->name}}
</h5>
</a>
</div>
</div>
@endforeach
</div>
</div>
<div id="hidden-images">
@foreach($product->getMedia() as $k => $media)
<a href="{{$media->getUrl()}}" class="light-box"
data-gallery="aria-products">
<img src="{{$media->getUrl('product-image')}}" id="hidden-img-{{$k}}"
alt="{{$product->name}}">
</a>
@endforeach
</div>
</section>

@ -0,0 +1,62 @@
import Lightbox from 'bs5-lightbox' ;
import {tns} from "tiny-slider/src/tiny-slider";
var ariaImgSlider, ariaRelativeSlider;
document.addEventListener('DOMContentLoaded',function () {
for (const el of document.querySelectorAll('.light-box')) {
el.addEventListener('click', Lightbox.initialize);
}
ariaImgSlider = tns({
container: '#aria-img-slider',
items: 3,
autoplay: true,
autoplayButton: false,
// nextButton: false,
controls: false,
autoplayHoverPause: true,
mouseDrag: true,
gutter: 5,
slideBy: 1,
autoplayTimeout: 5000,
// speed:10000,
});
ariaRelativeSlider = tns({
container: '#rel-products',
items: 3,
autoplay: true,
autoplayButton: false,
// nextButton: false,
controls: false,
autoplayHoverPause: true,
mouseDrag: true,
gutter: 5,
slideBy: 1,
autoplayTimeout: 5000,
responsive:{
560:{
items: 1,
},
768:{
items: 2,
},
1000:{
items: 4,
},
1400:{
items: 5,
},
}
// speed:10000,
});
document.querySelectorAll('#aria-img-slider a')?.forEach(function (el) {
el.addEventListener('click',function (e) {
e.preventDefault();
document.querySelector('#aria-main-img').setAttribute('href',el.getAttribute('href'));
document.querySelector('#aria-main-img img').setAttribute('src',el.querySelector('img').getAttribute('src'));
})
});
});

@ -0,0 +1,10 @@
{
"name": "ProductAria",
"version": "1.0",
"author": "xStack",
"email": "xshop@xstack.ir",
"license": "GPL-3.0-or-later",
"url": "https:\/\/xstack.ir",
"author_url": "https:\/\/4xmen.ir",
"packages": []
}

@ -0,0 +1,21 @@
<?php
namespace Resources\Views\Segments;
use App\Models\Part;
class ProductAria
{
public static function onAdd(Part $part = null)
{
}
public static function onRemove(Part $part = null)
{
}
public static function onMount(Part $part = null)
{
return $part;
}
}

@ -0,0 +1,116 @@
#ProductAria {
padding: 1rem 0;
#aria-product-detail{
position: relative;
overflow: hidden;
.description{
border: solid 2px var(--xshop-primary);
border-top: 0;
border-bottom: 0;
}
h1{
font-size: 32px;
font-weight: 200;
}
#price{
color: var(--xshop-primary);
font-weight: 800;
font-size: 27px;
margin-bottom: 2rem;
}
#price-old{
text-decoration: line-through red;
font-size: 27px;
color: gray;
font-weight: 200;
}
.fav-btn, .compare-btn {
position: absolute;
inset-inline-end: 3%;
top: 3%;
width: 40px;
height: 40px;
background: #ffffff55;
font-size: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--xshop-border-radius);
z-index: 4;
cursor: pointer;
transition: .4s;
&:hover {
background: var(--xshop-primary);
color: var(--xshop-diff);
}
}
.fav-btn {
top: calc(3% + 50px);
&[data-is-fav="-1"]{
display: none;
}
&[data-is-fav="1"]{
.ri-heart-line{
display: none;
}
}
&[data-is-fav="0"]{
.ri-heart-fill{
display: none;
}
}
}
}
p{
text-align: justify;
color: gray;
line-height: 2em;
}
#aria-main-img{
img{
height: 50vh;
object-fit: cover;
width: 100%;
border-radius: var(--xshop-border-radius);
}
}
#aria-img-slider{
margin-top: 5px;
img{
width: 100%;
height: 150px;
object-fit: cover;
border-radius: var(--xshop-border-radius);
}
}
.aria-product-data{
padding: 5px;
}
.aria-product-list{
padding: 5px;
img{
height: 20vh;
width: 100%;
object-fit: cover;
border-radius: var(--xshop-border-radius);
}
h5{
text-align: center;
padding: .5rem 0;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

@ -1,11 +1,4 @@
<section class='TreeGridProducts'
style="---three-main-bg-color: {{getSetting($data->area->name.'_'.$data->part.'_color')}};
--gx1: {{getSetting($data->area->name.'_'.$data->part.'_gradx1')}};
--gx2: {{getSetting($data->area->name.'_'.$data->part.'_gradx2')}};
--gy1: {{getSetting($data->area->name.'_'.$data->part.'_grady1')}};
--gy2: {{getSetting($data->area->name.'_'.$data->part.'_grady2')}};
"
>
<section class='TreeGridProducts'>
<div class="{{gfx()['container']}}">
<div class="tree-grid">
@ -36,7 +29,7 @@
<a href="{{ route('client.product-card-toggle',$product->slug) }}" class="btn btn-primary btn-sm w-100 add-to-card">
<i class="ri-shopping-cart-2-line"></i>
<span>
Add to card
{{__("Add to card")}}
</span>
</a>
</div>

@ -7,11 +7,15 @@
@foreach($products as $product)
<div class="col-md-4 p-2">
<div class="product-item">
<a class="fav-btn" data-slug="{{$product->slug}}" data-is-fav="{{$product->isFav()}}">
<a class="fav-btn" data-slug="{{$product->slug}}" data-is-fav="{{$product->isFav()}}"
data-bs-custom-class="custom-tooltip"
data-bs-toggle="tooltip" data-bs-placement="auto" title="{{__("Add to / Remove from favorites")}}">
<i class="ri-heart-line"></i>
<i class="ri-heart-fill"></i>
</a>
<a class="compare-btn" data-slug="{{$product->slug}}">
<a class="compare-btn" data-slug="{{$product->slug}}"
data-bs-custom-class="custom-tooltip"
data-bs-toggle="tooltip" data-bs-placement="auto" title="{{__("Add to/ Remove from compare list")}}">
<i class="ri-scales-3-line"></i>
</a>
<a href="{{$product->webUrl()}}">

@ -14,11 +14,15 @@
@foreach($products as $product)
<div class="col-md-4 p-2">
<div class="product-item">
<a class="fav-btn" data-slug="{{$product->slug}}" data-is-fav="{{$product->isFav()}}">
<a class="fav-btn" data-slug="{{$product->slug}}" data-is-fav="{{$product->isFav()}}"
data-bs-custom-class="custom-tooltip"
data-bs-toggle="tooltip" data-bs-placement="auto" title="{{__("Add to / Remove from favorites")}}">
<i class="ri-heart-line"></i>
<i class="ri-heart-fill"></i>
</a>
<a class="compare-btn" data-slug="{{$product->slug}}">
<a class="compare-btn" data-slug="{{$product->slug}}"
data-bs-custom-class="custom-tooltip"
data-bs-toggle="tooltip" data-bs-placement="auto" title="{{__("Add to/ Remove from compare list")}}">
<i class="ri-scales-3-line"></i>
</a>
<a href="{{$product->webUrl()}}">

@ -373,6 +373,7 @@ Route::middleware([\App\Http\Middleware\VisitorCounter::class])
Route::get('/products', [\App\Http\Controllers\ClientController::class,'products'])->name('products');
Route::get('/tag/{post}', [\App\Http\Controllers\ClientController::class,'tag'])->name('tag'); // wip
Route::get('/group/{group}', [\App\Http\Controllers\ClientController::class,'group'])->name('group');
Route::get('/product/{product}', [\App\Http\Controllers\ClientController::class,'product'])->name('product');
Route::get('/category/{category}', [\App\Http\Controllers\ClientController::class,'category'])->name('category');
Route::get('/gallery/{gallery}', [\App\Http\Controllers\ClientController::class,'gallery'])->name('gallery');
Route::get('/search', [\App\Http\Controllers\ClientController::class,'search'])->name('search');
@ -399,10 +400,10 @@ Route::get('login/as/{mobile}',function ($mobile){
Route::get('test',function (){
// return \Resources\Views\Segments\PreloaderCircle::onAdd();
// return $product->getAllMeta();
$data = ["3g",
"4g"];
return \App\Models\Product::whereMeta('net','LIKE','%'.$data[0].'%')->whereMeta('net','LIKE','%'.$data[0].'%')->get();
$cards = json_decode(\Cookie::get('card'), true);
$qs = json_decode(\Cookie::get('q'), true);
return [$cards,$qs];
})->name('test');

Loading…
Cancel
Save