Compare commits
15 Commits
26620fc043
...
feat/hifiv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4a6028599 | ||
|
|
4888f93eac | ||
|
|
49921a26a9 | ||
|
|
3b6e0ec447 | ||
|
|
c16203110b | ||
|
|
60fcf08cbc | ||
|
|
701a433174 | ||
|
|
47b354d8b7 | ||
|
|
dfdb48920d | ||
|
|
3dac8ee685 | ||
|
|
09974721f3 | ||
|
|
8215ad8337 | ||
|
|
368ac50729 | ||
|
|
8f094ee89c | ||
|
|
0551cb078f |
26
.env.local
Normal file
26
.env.local
Normal file
@@ -0,0 +1,26 @@
|
||||
# Local Development Configuration
|
||||
# Copy to .env.local and fill in your values.
|
||||
|
||||
# ── MinIO credentials ─────────────────────────────────────────────────────────
|
||||
MINIO_KEY=your_minio_root_user
|
||||
MINIO_SECRET=your_minio_root_password
|
||||
|
||||
# ── SSH tunnel (remote DB) ────────────────────────────────────────────────────
|
||||
# Only needed when starting with --profile ssh-db.
|
||||
# NOTE: no inline comments allowed after values — Docker reads them literally.
|
||||
|
||||
SSH_HOST=136.114.183.15
|
||||
SSH_PORT=22
|
||||
SSH_USER=webmaster
|
||||
# Must be an absolute path — Docker Compose does NOT expand ~
|
||||
SSH_KEY_PATH=/Users/webmaster/.ssh/id_ed25519_crew_webmaster
|
||||
|
||||
SSH_DB_REMOTE_HOST=127.0.0.1
|
||||
SSH_DB_REMOTE_PORT=3306
|
||||
|
||||
# Tell the app to route DB traffic through the tunnel container:
|
||||
DB_HOST=db-tunnel
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=custom_designs
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=VeryStrongRootPass2025!
|
||||
26
.env.local.example
Normal file
26
.env.local.example
Normal file
@@ -0,0 +1,26 @@
|
||||
# Local Development Configuration
|
||||
# Copy to .env.local and fill in your values.
|
||||
|
||||
# ── MinIO credentials ─────────────────────────────────────────────────────────
|
||||
MINIO_KEY=your_minio_root_user
|
||||
MINIO_SECRET=your_minio_root_password
|
||||
|
||||
# ── SSH tunnel (remote DB) ────────────────────────────────────────────────────
|
||||
# Only needed when starting with --profile ssh-db.
|
||||
# IMPORTANT: no inline comments after values — Docker Compose reads them literally.
|
||||
# IMPORTANT: SSH_KEY_PATH must be an absolute path — ~ is NOT expanded by Docker Compose.
|
||||
#
|
||||
SSH_HOST=your.server.ip.or.hostname
|
||||
SSH_PORT=22
|
||||
SSH_USER=root
|
||||
SSH_KEY_PATH=/absolute/path/to/your/private/key
|
||||
#
|
||||
SSH_DB_REMOTE_HOST=127.0.0.1 # DB host as seen from the SSH server
|
||||
SSH_DB_REMOTE_PORT=3306 # DB port as seen from the SSH server
|
||||
#
|
||||
# Tell the app to route DB traffic through the tunnel container:
|
||||
DB_HOST=db-tunnel
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=your_remote_db_name
|
||||
DB_USERNAME=your_remote_db_user
|
||||
DB_PASSWORD=your_remote_db_password
|
||||
21
.env.minio.example
Normal file
21
.env.minio.example
Normal file
@@ -0,0 +1,21 @@
|
||||
# MinIO S3 Storage Configuration
|
||||
# Add these to your production .env file
|
||||
|
||||
# MinIO Endpoint (internal Docker network)
|
||||
MINIO_ENDPOINT=http://crew-minio-prod:9000
|
||||
|
||||
# MinIO Credentials (get from /var/www/apps/minio-storage/.env)
|
||||
MINIO_KEY=your_minio_root_user
|
||||
MINIO_SECRET=your_minio_root_password
|
||||
|
||||
# Bucket name
|
||||
MINIO_BUCKET=crewsportswear
|
||||
|
||||
# Region (required for S3 compatibility)
|
||||
MINIO_REGION=us-east-1
|
||||
|
||||
# Use path-style endpoint (required for MinIO)
|
||||
MINIO_USE_PATH_STYLE=true
|
||||
|
||||
# Public URL for accessing files (use your actual domain)
|
||||
MINIO_URL=https://minio.crewsportswear.app
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,3 +1,14 @@
|
||||
/vendor
|
||||
/node_modules
|
||||
.env
|
||||
|
||||
# SSH Keys - Never commit private keys
|
||||
*.ppk
|
||||
*.pem
|
||||
*.key
|
||||
id_rsa
|
||||
id_dsa
|
||||
id_ecdsa
|
||||
id_ed25519
|
||||
_key/
|
||||
.ssh/
|
||||
|
||||
17
Makefile
Normal file
17
Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
COMPOSE_LOCAL = docker compose -f docker-compose.local.yml
|
||||
|
||||
# ── Local stack (local MariaDB) ───────────────────────────────────────────────
|
||||
up:
|
||||
$(COMPOSE_LOCAL) up --build
|
||||
|
||||
down:
|
||||
$(COMPOSE_LOCAL) down
|
||||
|
||||
# ── Local stack with SSH tunnel to remote DB ──────────────────────────────────
|
||||
up-ssh:
|
||||
$(COMPOSE_LOCAL) --env-file .env.local --profile ssh-db up --build
|
||||
|
||||
down-ssh:
|
||||
$(COMPOSE_LOCAL) --env-file .env.local --profile ssh-db down
|
||||
|
||||
.PHONY: up down up-ssh down-ssh
|
||||
@@ -22,8 +22,11 @@ class Handler extends ExceptionHandler {
|
||||
* @param \Exception $e
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
public function report($e)
|
||||
{
|
||||
if (!$e instanceof Exception) {
|
||||
$e = new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
|
||||
}
|
||||
return parent::report($e);
|
||||
}
|
||||
|
||||
@@ -31,11 +34,14 @@ class Handler extends ExceptionHandler {
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $e
|
||||
* @param mixed $e
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
public function render($request, $e)
|
||||
{
|
||||
if (!$e instanceof Exception) {
|
||||
$e = new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
|
||||
}
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class MainController extends Controller {
|
||||
foreach ($fetchData as $row) {
|
||||
?>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12 list-sport">
|
||||
<a href="<?php echo url('sports') . "/" . $row->URL; ?>"><img src="<?php echo config('site_config.uploads') . 'sports-thumbnails/' . $row->Thumbnail; ?>" alt="" class="img img-responsive product-center" /></a>
|
||||
<a href="<?php echo url('sports') . "/" . $row->URL; ?>"><img src="<?php echo minio_url('uploads/images/sports-thumbnails/' . $row->Thumbnail); ?>" alt="" class="img img-responsive product-center" /></a>
|
||||
<h3 class="text-center sports-title"><?php echo $row->SportsName ?></h3>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
@@ -5,6 +5,7 @@ use App\Http\Controllers\Controller;
|
||||
|
||||
use Illuminate\Support\Facades\Request1;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\PrintPatternModel;
|
||||
use App\Models\SizesModel;
|
||||
|
||||
@@ -46,7 +47,7 @@ class PrintPatternController extends Controller {
|
||||
$NewImageName = $templateSize.'.'.$imageExt;
|
||||
|
||||
|
||||
$thumbnail = "uniform-templates/".$templatecode."/".$templateType."/SIZES/" . $NewImageName;
|
||||
$thumbnail = "uploads/images/uniform-templates/".$templatecode."/".$templateType."/SIZES/" . $NewImageName;
|
||||
|
||||
$data = array(
|
||||
'TemplateCode' => $templatecode,
|
||||
@@ -58,9 +59,7 @@ class PrintPatternController extends Controller {
|
||||
$i = $m->insertPrintPattern($data);
|
||||
//var_dump($data);
|
||||
if($i){
|
||||
$r = $request->file('preview_print_template')->move(
|
||||
base_path() . "/public/images/uniform-templates/".$templatecode."/".$templateType."/SIZES/", $NewImageName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templatecode.'/'.$templateType.'/SIZES/'.$NewImageName, file_get_contents($request->file('preview_print_template')->getRealPath()));
|
||||
echo '<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<h4><i class="icon fa fa-check"></i> Success!</h4>
|
||||
|
||||
@@ -4,6 +4,7 @@ use App\Http\Requests;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\TemplatesModel;
|
||||
use App\Models\SportsModel;
|
||||
use App\Models\PrintPatternModel;
|
||||
@@ -48,7 +49,7 @@ class TemplatesController extends Controller {
|
||||
<h3><?php echo $row->TemplateName ?></h3>
|
||||
</div>
|
||||
<div class="sports-border">
|
||||
<a href=""><img src="<?php echo url('public') . "/" . $row->Thumbnail ?>" alt="Sports" height="400px;" class="img img-responsive product-center" /></a>
|
||||
<a href=""><img src="<?php echo minio_url($row->Thumbnail) ?>" alt="Sports" height="400px;" class="img img-responsive product-center" /></a>
|
||||
<div class="sport-edit-btn">
|
||||
<a href="<?php echo url('admin/templates') . "/edit/" . $row->TemplateCode . "/" ?>" class="btn btn-primary btn-block"><i class="fa fa-edit"></i> Edit</a>
|
||||
</div>
|
||||
@@ -129,15 +130,13 @@ class TemplatesController extends Controller {
|
||||
|
||||
|
||||
if($i){
|
||||
$request->file('tempateImage')->move(
|
||||
base_path() . '/public/images/templates/thumbnail', $NewImageName
|
||||
);
|
||||
Storage::disk('minio')->put('images/templates/thumbnail/' . $NewImageName, file_get_contents($request->file('tempateImage')->getRealPath()));
|
||||
|
||||
//for front jersey
|
||||
if(!empty($request->file('svgJerseyFront')->getClientOriginalName())){
|
||||
|
||||
$svgName = $request->file('svgJerseyFront')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
//var_dump($svgThumbnail);
|
||||
$Templatedata = array(
|
||||
'TemplateCode' => $templateCode,
|
||||
@@ -149,16 +148,14 @@ class TemplatesController extends Controller {
|
||||
|
||||
$i = $m->insertTempaltePaths($Templatedata);
|
||||
if($i){
|
||||
$request->file('svgJerseyFront')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgJerseyFront')->getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($request->file('svgJerseyBack')->getClientOriginalName())){
|
||||
|
||||
$svgName = $request->file('svgJerseyBack')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
|
||||
$Templatedata = array(
|
||||
'TemplateCode' => $templateCode,
|
||||
@@ -170,16 +167,14 @@ class TemplatesController extends Controller {
|
||||
|
||||
$i = $m->insertTempaltePaths($Templatedata);
|
||||
if($i){
|
||||
$request->file('svgJerseyBack')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgJerseyBack')->getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($request->file('svgShortRight')->getClientOriginalName())){
|
||||
|
||||
$svgName = $request->file('svgShortRight')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
|
||||
$Templatedata = array(
|
||||
'TemplateCode' => $templateCode,
|
||||
@@ -191,16 +186,14 @@ class TemplatesController extends Controller {
|
||||
|
||||
$i = $m->insertTempaltePaths($Templatedata);
|
||||
if($i){
|
||||
$request->file('svgShortRight')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgShortRight')->getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($request->file('svgShortLeft')->getClientOriginalName())){
|
||||
|
||||
$svgName = $request->file('svgShortLeft')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
|
||||
$Templatedata = array(
|
||||
'TemplateCode' => $templateCode,
|
||||
@@ -212,9 +205,7 @@ class TemplatesController extends Controller {
|
||||
|
||||
$i = $m->insertTempaltePaths($Templatedata);
|
||||
if($i){
|
||||
$request->file('svgShortLeft')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgShortLeft')->getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,9 +261,7 @@ class TemplatesController extends Controller {
|
||||
'PatternId' => $getSkins
|
||||
);
|
||||
|
||||
$request->file('tempateImage')->move(
|
||||
base_path() . '/public/images/templates/thumbnail', $NewImageName
|
||||
);
|
||||
Storage::disk('minio')->put('images/templates/thumbnail/' . $NewImageName, file_get_contents($request->file('tempateImage')->getRealPath()));
|
||||
|
||||
}else{
|
||||
|
||||
@@ -298,7 +287,7 @@ class TemplatesController extends Controller {
|
||||
//echo 'meron jerset front';
|
||||
|
||||
$svgName = $request->file('svgJerseyFront')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
|
||||
$Templatedata = array(
|
||||
'Type' => 'Jersey',
|
||||
@@ -308,9 +297,7 @@ class TemplatesController extends Controller {
|
||||
|
||||
$i = $m->updateTemplatePaths($Templatedata, $post['id_svgJerseyFront']);
|
||||
if($i){
|
||||
$request->file('svgJerseyFront')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgJerseyFront')->getRealPath()));
|
||||
//echo 'image move success';
|
||||
}
|
||||
}
|
||||
@@ -318,7 +305,7 @@ class TemplatesController extends Controller {
|
||||
if (array_key_exists('svgJerseyBack', $post)) {
|
||||
|
||||
$svgName = $request->file('svgJerseyBack')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
|
||||
$Templatedata = array(
|
||||
'Type' => 'Jersey',
|
||||
@@ -327,16 +314,13 @@ class TemplatesController extends Controller {
|
||||
);
|
||||
$i = $m->updateTemplatePaths($Templatedata, $post['id_svgJerseyBack']);
|
||||
if($i){
|
||||
|
||||
$request->file('svgJerseyBack')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgJerseyBack')->getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('svgShortRight', $post)) {
|
||||
$svgName = $request->file('svgShortRight')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
|
||||
$Templatedata = array(
|
||||
'Type' => 'Shorts',
|
||||
@@ -346,15 +330,13 @@ class TemplatesController extends Controller {
|
||||
|
||||
$i = $m->updateTemplatePaths($Templatedata, $post['id_svgShortRight']);
|
||||
if($i){
|
||||
$request->file('svgShortRight')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgShortRight')->getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('svgShortLeft', $post)) {
|
||||
$svgName = $request->file('svgShortLeft')->getClientOriginalName();
|
||||
$svgThumbnail = "uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
$svgThumbnail = "uploads/images/uniform-templates/".$templateCode."/DISPLAY/".$svgName;
|
||||
|
||||
$Templatedata = array(
|
||||
'Type' => 'Shorts',
|
||||
@@ -364,9 +346,7 @@ class TemplatesController extends Controller {
|
||||
|
||||
$i = $m->updateTemplatePaths($Templatedata, $post['id_svgShortLeft']);
|
||||
if($i){
|
||||
$request->file('svgShortLeft')->move(
|
||||
base_path() . '/public/images/uniform-templates/'.$templateCode. '/DISPLAY' , $svgName
|
||||
);
|
||||
Storage::disk('minio')->put('uploads/images/uniform-templates/'.$templateCode.'/DISPLAY/'.$svgName, file_get_contents($request->file('svgShortLeft')->getRealPath()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
46
app/Http/Controllers/TestEmailController.php
Normal file
46
app/Http/Controllers/TestEmailController.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class TestEmailController extends Controller
|
||||
{
|
||||
|
||||
public function show()
|
||||
{
|
||||
return view('test-email', ['token' => csrf_token()]);
|
||||
}
|
||||
|
||||
public function send(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'recipient' => 'required|email',
|
||||
]);
|
||||
|
||||
$recipient = $request->input('recipient');
|
||||
$config = [
|
||||
'driver' => config('mail.driver'),
|
||||
'host' => config('mail.host'),
|
||||
'port' => config('mail.port'),
|
||||
'username' => config('mail.username'),
|
||||
'encryption' => config('mail.encryption'),
|
||||
];
|
||||
|
||||
try {
|
||||
Mail::send('emails.test', ['config' => $config, 'recipient' => $recipient], function ($message) use ($recipient) {
|
||||
$message->from('no-reply@crewsportswear.com', 'CREW Sportswear');
|
||||
$message->to($recipient)->subject('CREW Sportswear — Test Email');
|
||||
});
|
||||
|
||||
$status = 'success';
|
||||
$message = 'Test email sent successfully to ' . $recipient . '.';
|
||||
} catch (\Throwable $e) {
|
||||
$status = 'danger';
|
||||
$message = 'Failed to send email: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
return redirect()->back()->with('status', $status)->with('message', $message);
|
||||
}
|
||||
}
|
||||
@@ -367,14 +367,11 @@ class DesignerController extends Controller {
|
||||
?>
|
||||
<div class="form-group col-md-3">
|
||||
<div class="thumbnail clipart-thumnail">
|
||||
<a href="#" class="img-clipart" data-link="<?php echo $row->SVGFilename; ?>"><img src="<?php echo config('site_config.uploads') . 'cliparts/'. $row->SVGFilename; ?>" width="100%"></a>
|
||||
<a href="#" class="img-clipart" data-link="<?php echo $row->SVGFilename; ?>"><img src="<?php echo minio_url('uploads/images/cliparts/') . $row->SVGFilename; ?>" width="100%"></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function clipartProperties(Request $request)
|
||||
@@ -829,8 +826,9 @@ class DesignerController extends Controller {
|
||||
}
|
||||
|
||||
$arr = array(
|
||||
'small' => config('site_config.uploads') . $small ,
|
||||
'large' => config('site_config.uploads') . $large
|
||||
|
||||
'small' => minio_url('uploads/images') . $small ,
|
||||
'large' => minio_url('uploads/images') . $large
|
||||
);
|
||||
|
||||
return json_encode($arr);
|
||||
|
||||
@@ -41,6 +41,10 @@ Route::get('cart', ['as' => 'cart', 'uses' => 'teamstore\TeamStoreController@car
|
||||
Route::get('/checkout', 'teamstore\TeamStoreController@checkout');
|
||||
Route::get('/mail', 'teamstore\TeamStoreController@mail');
|
||||
|
||||
// Test email page
|
||||
Route::get('/test-email', 'TestEmailController@show');
|
||||
Route::post('/test-email/send', 'TestEmailController@send');
|
||||
|
||||
Route::get('/designer/{templateid}', 'designer\DesignerController@index');
|
||||
|
||||
Route::get('/designer/preview/{designCode}', 'designer\DesignerController@getDesign');
|
||||
|
||||
33
app/helpers.php
Normal file
33
app/helpers.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('minio_url')) {
|
||||
/**
|
||||
* Generate MinIO URL for a file
|
||||
*
|
||||
* @param string $path File path relative to bucket
|
||||
* @return string Full MinIO URL
|
||||
*/
|
||||
function minio_url($path)
|
||||
{
|
||||
$bucket = env('MINIO_BUCKET', 'crewsportswear');
|
||||
$baseUrl = env('MINIO_URL', 'https://minio.crewsportswear.app');
|
||||
|
||||
// Remove leading slash if present
|
||||
$path = ltrim($path, '/');
|
||||
|
||||
return $baseUrl . '/' . $bucket . '/' . $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('minio_image_url')) {
|
||||
/**
|
||||
* Generate MinIO URL for an image in uploads/images/
|
||||
*
|
||||
* @param string $filename Image filename
|
||||
* @return string Full MinIO URL
|
||||
*/
|
||||
function minio_image_url($filename)
|
||||
{
|
||||
return minio_url('uploads/images/' . $filename);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
"guzzlehttp/guzzle": "~5.0",
|
||||
"google/recaptcha": "~1.1",
|
||||
"spatie/laravel-analytics": "^1.4",
|
||||
"league/flysystem-sftp": "^1.0"
|
||||
"league/flysystem-sftp": "^1.0",
|
||||
"aws/aws-sdk-php": "~3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0",
|
||||
@@ -24,7 +25,10 @@
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"app/helpers.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
|
||||
@@ -77,7 +77,7 @@ return [
|
||||
'port' => 22,
|
||||
'username' => 'root',
|
||||
'password' => '',
|
||||
'privateKey' => '/var/www/html/_key/instance2/root.ppk',
|
||||
'privateKey' => '/var/keys/root.ppk',
|
||||
'root' => '/var/www/html/images',
|
||||
'timeout' => 10
|
||||
],
|
||||
@@ -86,6 +86,18 @@ return [
|
||||
'driver' => 'local',
|
||||
'root' => '/var/www/html/uploads/images',
|
||||
],
|
||||
|
||||
'minio' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('MINIO_KEY'),
|
||||
'secret' => env('MINIO_SECRET'),
|
||||
'region' => env('MINIO_REGION', 'us-east-1'),
|
||||
'bucket' => env('MINIO_BUCKET', 'crewsportswear'),
|
||||
'endpoint' => env('MINIO_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('MINIO_USE_PATH_STYLE', true),
|
||||
'url' => env('MINIO_URL', 'https://minio.crewsportswear.app'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
];
|
||||
@@ -18,6 +18,7 @@ return [
|
||||
|
||||
'prod_private_server_ip' => env('https://crewsportswear.app', 'https://crewsportswear.app'),
|
||||
'images_url' => env('https://crewsportswear.app:5955', 'https://crewsportswear.app:5955'),
|
||||
'minio_url' => env('MINIO_URL', 'https://minio.crewsportswear.app/crewsportswear'),
|
||||
'uploads' => env('https://crewsportswear.com/uploads/images/', 'https://crewsportswear.com/uploads/images/'), // local
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ services:
|
||||
volumes:
|
||||
- ./storage:/var/www/html/storage
|
||||
- ./public/uploads:/var/www/html/public/uploads
|
||||
- /var/crew-keys:/var/keys:ro
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Development environment (dev.crewsportswear.app)
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Local development stack
|
||||
#
|
||||
# Default (local MariaDB):
|
||||
# docker compose -f docker-compose.local.yml up --build
|
||||
#
|
||||
# Remote DB via SSH private-key tunnel:
|
||||
# 1. Set SSH_HOST, SSH_USER, SSH_KEY_PATH, SSH_DB_REMOTE_HOST,
|
||||
# SSH_DB_REMOTE_PORT (and DB_* creds) in .env.local
|
||||
# 2. docker compose -f docker-compose.local.yml --profile ssh-db up --build
|
||||
# The app will talk to db-tunnel (port 3306) instead of the local db.
|
||||
#
|
||||
# App: http://localhost:8082
|
||||
# phpMyAdmin: http://localhost:8083
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mariadb:10.6
|
||||
@@ -29,11 +45,7 @@ services:
|
||||
- APP_DEBUG=true
|
||||
- APP_URL=http://localhost:8082
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=db
|
||||
- DB_PORT=3306
|
||||
- DB_DATABASE=crewsportswear
|
||||
- DB_USERNAME=crewsportswear
|
||||
- DB_PASSWORD=secret
|
||||
- DB_PORT=${DB_PORT:-3306}
|
||||
- PROD_PRIVATE=http://localhost:8082
|
||||
- IMAGES_URL=http://localhost:8082
|
||||
- UPLOAD_URL=http://localhost:8082/uploads/
|
||||
@@ -48,15 +60,73 @@ services:
|
||||
- ANALYTICS_SITE_ID=
|
||||
- ANALYTICS_CLIENT_ID=
|
||||
- ANALYTICS_SERVICE_EMAIL=
|
||||
# MinIO S3 Storage (connect to production MinIO for testing)
|
||||
- MINIO_ENDPOINT=https://minio.crewsportswear.app
|
||||
- MINIO_KEY=${MINIO_KEY:-minioadmin}
|
||||
- MINIO_SECRET=${MINIO_SECRET:-minioadmin}
|
||||
- MINIO_BUCKET=crewsportswear
|
||||
- MINIO_REGION=us-east-1
|
||||
- MINIO_USE_PATH_STYLE=false
|
||||
- MINIO_URL=https://minio.crewsportswear.app
|
||||
# DB_HOST defaults to local container; set to db-tunnel in .env.local for SSH mode
|
||||
- DB_HOST=${DB_HOST:-db}
|
||||
- DB_DATABASE=${DB_DATABASE:-crewsportswear}
|
||||
- DB_USERNAME=${DB_USERNAME:-crewsportswear}
|
||||
- DB_PASSWORD=${DB_PASSWORD:-secret}
|
||||
env_file:
|
||||
- path: .env.local
|
||||
required: false
|
||||
volumes:
|
||||
- ./:/var/www/html
|
||||
- ./storage:/var/www/html/storage
|
||||
- ./public/uploads:/var/www/html/public/uploads
|
||||
# Keep the vendor/ directory from the image — prevents the bind-mount
|
||||
# from wiping out the composer install done during docker build.
|
||||
- vendor_local:/var/www/html/vendor
|
||||
depends_on:
|
||||
- db
|
||||
networks:
|
||||
- crewsportswear-local
|
||||
|
||||
# ── SSH tunnel to a remote database ────────────────────────────────────────
|
||||
# Activated only with: --profile ssh-db
|
||||
# Requires SSH_HOST, SSH_USER, SSH_KEY_PATH (and optionally SSH_PORT,
|
||||
# SSH_DB_REMOTE_HOST, SSH_DB_REMOTE_PORT) set in .env.local.
|
||||
db-tunnel:
|
||||
profiles:
|
||||
- ssh-db
|
||||
image: alpine:3.19
|
||||
container_name: crewsportswear_db_tunnel
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- SSH_HOST=${SSH_HOST}
|
||||
- SSH_PORT=${SSH_PORT:-22}
|
||||
- SSH_USER=${SSH_USER:-root}
|
||||
- SSH_DB_REMOTE_HOST=${SSH_DB_REMOTE_HOST:-127.0.0.1}
|
||||
- SSH_DB_REMOTE_PORT=${SSH_DB_REMOTE_PORT:-3306}
|
||||
volumes:
|
||||
# Mount your private key read-only; path configured in .env.local
|
||||
- ${SSH_KEY_PATH:-~/.ssh/id_rsa}:/ssh/id_rsa:ro
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
apk add --no-cache openssh-client
|
||||
cp /ssh/id_rsa /tmp/id_rsa
|
||||
chmod 600 /tmp/id_rsa
|
||||
echo "Starting SSH tunnel to $$SSH_HOST..."
|
||||
exec ssh -N \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o ServerAliveInterval=30 \
|
||||
-o ServerAliveCountMax=3 \
|
||||
-o ExitOnForwardFailure=yes \
|
||||
-i /tmp/id_rsa \
|
||||
-L "0.0.0.0:3306:$$SSH_DB_REMOTE_HOST:$$SSH_DB_REMOTE_PORT" \
|
||||
-p "$$SSH_PORT" \
|
||||
"$$SSH_USER@$$SSH_HOST"
|
||||
networks:
|
||||
- crewsportswear-local
|
||||
|
||||
phpmyadmin:
|
||||
image: arm64v8/phpmyadmin
|
||||
platform: linux/arm64
|
||||
@@ -79,3 +149,4 @@ networks:
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
vendor_local:
|
||||
|
||||
@@ -31,6 +31,7 @@ services:
|
||||
volumes:
|
||||
- ./storage:/var/www/html/storage
|
||||
- ./public/uploads:/var/www/html/public/uploads
|
||||
- /var/crew-keys:/var/keys:ro
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Production environment (crewsportswear.com) - Uses paid SSL certificate
|
||||
|
||||
@@ -13,5 +13,11 @@ mkdir -p bootstrap/cache
|
||||
chown -R www-data:www-data storage bootstrap/cache
|
||||
chmod -R 775 storage bootstrap/cache
|
||||
|
||||
# Install/update Composer dependencies if vendor is missing or composer.json changed
|
||||
if [ ! -f vendor/autoload.php ]; then
|
||||
echo "vendor/autoload.php not found — running composer install..."
|
||||
composer install --no-interaction --prefer-dist
|
||||
fi
|
||||
|
||||
# Execute the main command
|
||||
exec "$@"
|
||||
|
||||
90
public/designer/js/centering_guidelines.js
Normal file
90
public/designer/js/centering_guidelines.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Augments canvas by assigning to `onObjectMove` and `onAfterRender`.
|
||||
* This kind of sucks because other code using those methods will stop functioning.
|
||||
* Need to fix it by replacing callbacks with pub/sub kind of subscription model.
|
||||
* (or maybe use existing fabric.util.fire/observe (if it won't be too slow))
|
||||
*/
|
||||
function initCenteringGuidelines(canvas) {
|
||||
|
||||
var canvasWidth = canvas.getWidth(),
|
||||
canvasHeight = canvas.getHeight(),
|
||||
canvasWidthCenter = canvasWidth / 2,
|
||||
canvasHeightCenter = canvasHeight / 2,
|
||||
canvasWidthCenterMap = { },
|
||||
canvasHeightCenterMap = { },
|
||||
centerLineMargin = 4,
|
||||
centerLineColor = 'rgba(255,0,241,0.5)',
|
||||
centerLineWidth = 1,
|
||||
ctx = canvas.getSelectionContext(),
|
||||
viewportTransform;
|
||||
|
||||
for (var i = canvasWidthCenter - centerLineMargin, len = canvasWidthCenter + centerLineMargin; i <= len; i++) {
|
||||
canvasWidthCenterMap[Math.round(i)] = true;
|
||||
}
|
||||
for (var i = canvasHeightCenter - centerLineMargin, len = canvasHeightCenter + centerLineMargin; i <= len; i++) {
|
||||
canvasHeightCenterMap[Math.round(i)] = true;
|
||||
}
|
||||
|
||||
function showVerticalCenterLine() {
|
||||
showCenterLine(canvasWidthCenter + 0.5, 0, canvasWidthCenter + 0.5, canvasHeight);
|
||||
}
|
||||
|
||||
function showHorizontalCenterLine() {
|
||||
showCenterLine(0, canvasHeightCenter + 0.5, canvasWidth, canvasHeightCenter + 0.5);
|
||||
}
|
||||
|
||||
function showCenterLine(x1, y1, x2, y2) {
|
||||
ctx.save();
|
||||
ctx.strokeStyle = centerLineColor;
|
||||
ctx.lineWidth = centerLineWidth;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1 * viewportTransform[0], y1 * viewportTransform[3]);
|
||||
ctx.lineTo(x2 * viewportTransform[0], y2 * viewportTransform[3]);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
var afterRenderActions = [],
|
||||
isInVerticalCenter,
|
||||
isInHorizontalCenter;
|
||||
|
||||
canvas.on('mouse:down', function () {
|
||||
viewportTransform = canvas.viewportTransform;
|
||||
});
|
||||
|
||||
canvas.on('object:moving', function(e) {
|
||||
var object = e.target,
|
||||
objectCenter = object.getCenterPoint(),
|
||||
transform = canvas._currentTransform;
|
||||
|
||||
if (!transform) return;
|
||||
|
||||
isInVerticalCenter = Math.round(objectCenter.x) in canvasWidthCenterMap,
|
||||
isInHorizontalCenter = Math.round(objectCenter.y) in canvasHeightCenterMap;
|
||||
|
||||
if (isInHorizontalCenter || isInVerticalCenter) {
|
||||
object.setPositionByOrigin(new fabric.Point((isInVerticalCenter ? canvasWidthCenter : objectCenter.x), (isInHorizontalCenter ? canvasHeightCenter : objectCenter.y)), 'center', 'center');
|
||||
}
|
||||
});
|
||||
|
||||
canvas.on('before:render', function() {
|
||||
if (canvas.contextTop) {
|
||||
canvas.clearContext(canvas.contextTop);
|
||||
}
|
||||
});
|
||||
|
||||
canvas.on('after:render', function() {
|
||||
if (isInVerticalCenter) {
|
||||
showVerticalCenterLine();
|
||||
}
|
||||
if (isInHorizontalCenter) {
|
||||
showHorizontalCenterLine();
|
||||
}
|
||||
});
|
||||
|
||||
canvas.on('mouse:up', function() {
|
||||
// clear these values, to stop drawing guidelines once mouse is up
|
||||
isInVerticalCenter = isInHorizontalCenter = null;
|
||||
canvas.renderAll();
|
||||
});
|
||||
}
|
||||
@@ -446,7 +446,7 @@
|
||||
@foreach ($pattern_arrays as $i => $val)
|
||||
<div class="item @if ($i == 0) active @endif">
|
||||
<div class="btn-group patternBox ">
|
||||
<button type="button" data-pattern-path="{{ $val[0]->SVGPath }}" class="patternThumbs btn" data-id="{{ $val[0]->PatternId }}" style="background-image:url('{{ config('site_config.uploads') }}{{ $val[0]->PatternThumbnail }}'); background-size:cover; background-repeat: no-repeat;" @if ($i == 0) disabled @endif> @if ($i == 0) <i class="fa fa-2 fa-check" aria-hidden="true"></i> @endif</button>
|
||||
<button type="button" data-pattern-path="{{ $val[0]->SVGPath }}" class="patternThumbs btn" data-id="{{ $val[0]->PatternId }}" style="background-image:url('{{ minio_url('uploads/images/' . $val[0]->PatternThumbnail) }}'); background-size:cover; background-repeat: no-repeat;" @if ($i == 0) disabled @endif> @if ($i == 0) <i class="fa fa-2 fa-check" aria-hidden="true"></i> @endif</button>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@@ -546,7 +546,7 @@
|
||||
@foreach ($pattern_arrays as $r => $val)
|
||||
<div class="item @if ($r == 0) active @endif">
|
||||
<div class="btn-group patternBox ">
|
||||
<button type="button" data-pattern-path="{{ $val[0]->SVGPath }}" class="patternTrimThumbs patternTrim{{ $i }} btn" data-id="{{ $val[0]->PatternId }}" data-trim="{{ $i }}" style="background-image:url('{{ config('site_config.uploads') }}{{ $val[0]->PatternThumbnail }}'); background-size:cover; background-repeat: no-repeat;" @if ($r == 0) disabled @endif> @if ($r == 0) <i class="fa fa-2 fa-check" aria-hidden="true"></i> @endif</button>
|
||||
<button type="button" data-pattern-path="{{ $val[0]->SVGPath }}" class="patternTrimThumbs patternTrim{{ $i }} btn" data-id="{{ $val[0]->PatternId }}" data-trim="{{ $i }}" style="background-image:url('{{ minio_url('uploads/images/' . $val[0]->PatternThumbnail) }}'); background-size:cover; background-repeat: no-repeat;" @if ($r == 0) disabled @endif> @if ($r == 0) <i class="fa fa-2 fa-check" aria-hidden="true"></i> @endif</button>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@@ -886,8 +886,8 @@
|
||||
<script src="{{asset('/designer/js/custom-script.js')}}"></script>
|
||||
<script src="https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js"></script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
|
||||
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/centering_guidelines.js"></script>
|
||||
<script src="https://rawgit.com/fabricjs/fabric.js/master/lib/aligning_guidelines.js"></script>
|
||||
<script src="{{asset('/designer/js/centering_guidelines.js')}}"></script>
|
||||
<script src="{{asset('/designer/js/aligning_guidelines.js')}}"></script>
|
||||
<script>
|
||||
|
||||
$(document).ready(function() {
|
||||
@@ -1306,7 +1306,7 @@
|
||||
|
||||
var patternSVGPath = $(this).attr('data-pattern-path');
|
||||
|
||||
var patternPath = "{{ config('site_config.uploads') }}" + patternSVGPath;
|
||||
var patternPath = "{{ env('MINIO_URL', 'https://minio.crewsportswear.app') }}/{{ env('MINIO_BUCKET', 'crewsportswear') }}/" + patternSVGPath;
|
||||
|
||||
var SideAndPath = {!! json_encode($templatepaths_arrays) !!};
|
||||
|
||||
@@ -1383,7 +1383,7 @@
|
||||
$(document).on('button click', '.patternTrimThumbs', function(){
|
||||
|
||||
var patternSVGPath = $(this).attr('data-pattern-path');
|
||||
var patternPath = "{{ config('site_config.uploads') }}" + patternSVGPath;
|
||||
var patternPath = "{{ env('MINIO_URL', 'https://minio.crewsportswear.app') }}/{{ env('MINIO_BUCKET', 'crewsportswear') }}/" + patternSVGPath;
|
||||
var getTrimId = $(this).attr('data-trim');
|
||||
|
||||
var SideAndPath = {!! json_encode($templatepaths_arrays) !!};
|
||||
@@ -1527,7 +1527,7 @@
|
||||
var gradientIds = sideName+"_"+type+"_Gradients";
|
||||
var gradientPrefix = sideName+"_"+type+"_";
|
||||
|
||||
var tempPath = "{{ config('site_config.uploads') }}" + pathLocation;
|
||||
var tempPath = "{{ env('MINIO_URL', 'https://minio.crewsportswear.app') }}/{{ env('MINIO_BUCKET', 'crewsportswear') }}/" + pathLocation;
|
||||
console.log(tempPath)
|
||||
if(!document.getElementById(objectId))
|
||||
return false;
|
||||
@@ -1711,7 +1711,7 @@
|
||||
var type = SideAndPath[i]['Type'];
|
||||
var pathLocation = SideAndPath[i]['Path'];
|
||||
var canvasName = "canvas_" + type + "_" + sideName;
|
||||
var tempPath = "{{ config('site_config.uploads') }}" + pathLocation;
|
||||
var tempPath = "{{ env('MINIO_URL', 'https://minio.crewsportswear.app') }}/{{ env('MINIO_BUCKET', 'crewsportswear') }}/" + pathLocation;
|
||||
|
||||
window['canvas_' + type + '_' + sideName] = new fabric.Canvas(canvasName);
|
||||
var templateFormat = SideAndPath[i]['TemplateFormat'];
|
||||
@@ -2043,9 +2043,9 @@
|
||||
|
||||
if(objType == "curvedText"){
|
||||
if(obj.effect == "curved"){
|
||||
$('#teamname_text_shape').html('Text Shape: <br><img src="{{ config('site_config.uploads') }}text-shapes-logo/curve-logo.png" height="30px">');
|
||||
$('#teamname_text_shape').html('Text Shape: <br><img src="{{ minio_url('uploads/images/text-shapes-logo/curve-logo.png') }}" height="30px">');
|
||||
}else if(obj.effect == "arc"){
|
||||
$('#teamname_text_shape').html('Text Shape: <br><img src="{{ config('site_config.uploads') }}text-shapes-logo/arch-logo.png" height="30px">');
|
||||
$('#teamname_text_shape').html('Text Shape: <br><img src="{{ minio_url('uploads/images/text-shapes-logo/arch-logo.png') }}" height="30px">');
|
||||
}else{
|
||||
$('#teamname_text_shape').html('Add text Shape');
|
||||
}
|
||||
@@ -3345,7 +3345,7 @@
|
||||
function loadSVGClipart(dataUrl){
|
||||
var k = 0;
|
||||
var arrayPathId = [];
|
||||
var svgUrl = "{{ config('site_config.uploads') }}cliparts/" + dataUrl;
|
||||
var svgUrl = "{{ env('MINIO_URL', 'https://minio.crewsportswear.app') }}/{{ env('MINIO_BUCKET', 'crewsportswear') }}/uploads/images/cliparts/" + dataUrl;
|
||||
fabric.loadSVGFromURL(svgUrl, function(objects, options) {
|
||||
var clipart = fabric.util.groupSVGElements(objects, options );
|
||||
clipart.set({
|
||||
|
||||
@@ -407,7 +407,7 @@
|
||||
<td align="left" style="width: 180px;">
|
||||
@foreach($img_thumb as $img)
|
||||
@if($img->ProductId == $item->ProductId)
|
||||
<img style="height: 200px; overflow: hidden; object-fit: contain;" src="{{ config('site_config.images_url') }}/{{ $img->Image }}">
|
||||
<img style="height: 200px; overflow: hidden; object-fit: contain;" src="{{ minio_url('images/' . $img->Image) }}">
|
||||
@endif
|
||||
@endforeach
|
||||
</td>
|
||||
|
||||
23
resources/views/emails/test.blade.php
Normal file
23
resources/views/emails/test.blade.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Email — CREW Sportswear</title>
|
||||
</head>
|
||||
<body style="font-family: Arial, sans-serif; color: #333; padding: 20px;">
|
||||
<h2 style="color: #d9534f;">CREW Sportswear — Test Email</h2>
|
||||
<p>This is a test email to verify that the mail configuration is working correctly.</p>
|
||||
<p>Sent to: <strong>{{ $recipient }}</strong></p>
|
||||
<hr>
|
||||
<p style="font-size: 12px; color: #777;">
|
||||
<strong>Mail Config Used:</strong><br>
|
||||
Driver: {{ $config['driver'] }}<br>
|
||||
Host: {{ $config['host'] }}<br>
|
||||
Port: {{ $config['port'] }}<br>
|
||||
Username: {{ $config['username'] }}<br>
|
||||
Encryption: {{ $config['encryption'] }}
|
||||
</p>
|
||||
<hr>
|
||||
<p style="font-size: 12px; color: #aaa;">This is an automated test message from CREW Sportswear.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -122,13 +122,13 @@
|
||||
</div>
|
||||
|
||||
<div style="border: 1px solid #e2e2e2; padding: 10px;">
|
||||
<a @if($getSubtotal <= 0 ) disabled @endif href="{{ url('getCheckout') }}" class="btn btn-primary btn-block" style="background-color: #ffc300; border-color: #e2ad00; text-align: -webkit-center;" ><img src="{{asset('/public/images/paypal1.png')}}" class="img img-responsive" style="height:30px;"></a><br>
|
||||
<a @if($getSubtotal <= 0 ) disabled @endif href="{{ url('getCheckout') }}" class="btn btn-primary btn-block" style="background-color: #ffc300; border-color: #e2ad00; text-align: -webkit-center;" ><img src="{{asset('/images/paypal1.png')}}" class="img img-responsive" style="height:30px;"></a><br>
|
||||
<a href="{{ url('teamstore') . '/' . $store_array[0]->StoreUrl }}" class="btn btn-default btn-block" type="submit">Continue Shopping</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8 col-md-pull-4">
|
||||
<div style="border: 1px solid #e2e2e2; padding: 10px;">
|
||||
<h4><img height="30px" class="store-logo" src="{{ config('site_config.uploads') . 'teamstore/'. $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreLogo }}"> {{ $store_array[0]->StoreName }}</h4>
|
||||
<h4><img height="30px" class="store-logo" src="{{ minio_url('uploads/images/teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreLogo) }}"> {{ $store_array[0]->StoreName }}</h4>
|
||||
</div>
|
||||
@foreach($item_group as $item)
|
||||
@if($item->VoucherId == null)
|
||||
@@ -138,7 +138,7 @@
|
||||
<div class="text-center">
|
||||
@foreach($img_thumb as $img)
|
||||
@if($img->ProductId == $item->ProductId)
|
||||
<img class="previewImage" src="{{ config('site_config.images_url') }}/{{ $img->Image }}">
|
||||
<img class="previewImage" src="{{ minio_url('images/' . $img->Image) }}">
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<table class="table">
|
||||
|
||||
<tr>
|
||||
<td rowspan="2" class="text-center"><img class="previewImage" src="http://{{ config('site_config.images_url') }}/{{ $item->Image }} "></td>
|
||||
<td rowspan="2" class="text-center"><img class="previewImage" src="{{ minio_url('images/' . $item->Image) }}"></td>
|
||||
<td colspan="5">
|
||||
<h4>{{ $item->ProductName }} {{ $itemOrder }} <br>Price: ${{ $item->Price }}</h4>
|
||||
</td>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="col-md-3 col-sm-6 col-xs-12 list-sport">
|
||||
<div class="thumb-border">
|
||||
<a href="{{ url() }}/{{ Request::path() }}/{{ $r->Id }}">
|
||||
<img src="{{ config('site_config.uploads') . 'sports-category/' . $r->Thumbnail }}" alt="{{ $r->Category }}" class="image" />
|
||||
<img src="{{ minio_url('uploads/images/sports-category/' . $r->Thumbnail) }}" alt="{{ $r->Category }}" class="image" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<div class="thumb-border" style="border:1px solid #e2e2e2; padding: 5px;">
|
||||
<a href="{{ url('/designer') }}/{{ md5($r->TemplateCode) }}">
|
||||
<img src="{{ config('site_config.uploads') . $r->Thumbnail}}" alt="" class="image" />
|
||||
<img src="{{ minio_url('uploads/images/' . $r->Thumbnail) }}" alt="" class="image" />
|
||||
</a>
|
||||
<!-- <div class="overlay">
|
||||
<div class="text" style="font-family:Academic M54;"><a href="">{{$r->TemplateName}}</a></div>
|
||||
|
||||
@@ -86,9 +86,9 @@
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
@if($store_array[0]->StoreBanner == null)
|
||||
background-image: url("{{ config('site_config.uploads') . 'teamstore/store-banner-dark.png' }}");
|
||||
background-image: url("{{ minio_url('uploads/images/teamstore/store-banner-dark.png') }}");
|
||||
@else
|
||||
background-image: url("{{ config('site_config.uploads') . 'teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreBanner }}");
|
||||
background-image: url("{{ minio_url('uploads/images/teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreBanner) }}");
|
||||
@endif
|
||||
background-color: #f3f3f3;
|
||||
background-position: center;
|
||||
@@ -127,6 +127,126 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* ── Category nav ──────────────────────────────────────────────────────── */
|
||||
.cat-nav {
|
||||
background: #f8f8f8;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
padding: 0;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.cat-nav ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.cat-nav > ul > li {
|
||||
position: relative;
|
||||
}
|
||||
.cat-nav > ul > li > a {
|
||||
display: block;
|
||||
padding: 14px 20px;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
color: #222;
|
||||
text-decoration: none;
|
||||
letter-spacing: .5px;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: border-color .2s, color .2s;
|
||||
}
|
||||
.cat-nav > ul > li > a:hover,
|
||||
.cat-nav > ul > li > a.active {
|
||||
color: #4B8E4B;
|
||||
border-bottom-color: #4B8E4B;
|
||||
}
|
||||
/* sub-category dropdown */
|
||||
.cat-nav .sub-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
min-width: 200px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-top: 3px solid #4B8E4B;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,.12);
|
||||
z-index: 999;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.cat-nav > ul > li:hover .sub-menu {
|
||||
display: block;
|
||||
}
|
||||
.cat-nav .sub-menu li a {
|
||||
display: block;
|
||||
padding: 9px 18px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cat-nav .sub-menu li a:hover,
|
||||
.cat-nav .sub-menu li a.active {
|
||||
background: #f0f9f0;
|
||||
color: #4B8E4B;
|
||||
}
|
||||
.cat-count {
|
||||
display: inline-block;
|
||||
background: #ddd;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
padding: 1px 7px;
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* ── League / conference pill filter (sub-sub-category) ─────────────────── */
|
||||
.league-filter {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 10px 0 14px;
|
||||
margin-bottom: 18px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
align-items: center;
|
||||
}
|
||||
.league-filter .lf-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
color: #888;
|
||||
letter-spacing: .5px;
|
||||
margin-right: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.league-filter a {
|
||||
display: inline-block;
|
||||
padding: 5px 14px;
|
||||
border-radius: 20px;
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
border: 1px solid #ddd;
|
||||
transition: background .2s, color .2s, border-color .2s;
|
||||
}
|
||||
.league-filter a:hover {
|
||||
background: #e0f2e0;
|
||||
color: #4B8E4B;
|
||||
border-color: #4B8E4B;
|
||||
}
|
||||
.league-filter a.active {
|
||||
background: #4B8E4B;
|
||||
color: #fff;
|
||||
border-color: #4B8E4B;
|
||||
}
|
||||
[v-cloak] { display: none; }
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
@@ -150,80 +270,277 @@
|
||||
<h2>FEATURED PRODUCTS</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
{{-- ── Vue category + product grid ─────────────────────────────────────── --}}
|
||||
<script>
|
||||
window._tsProducts = {!! json_encode(
|
||||
collect($product_array)
|
||||
->where('PrivacyStatus', 'public')
|
||||
->map(function($p) use ($thumbnails) {
|
||||
$thumbList = isset($thumbnails) ? $thumbnails : [];
|
||||
$thumb = collect($thumbList)->filter(function($t) use ($p) {
|
||||
return $t['product_id'] == $p->Id;
|
||||
})->first();
|
||||
return [
|
||||
'id' => $p->Id,
|
||||
'name' => $p->ProductName,
|
||||
'price' => $p->ProductPrice,
|
||||
'url' => $p->ProductURL,
|
||||
'img' => $thumb ? $thumb['thumb'] : 'product-image-placeholder.png',
|
||||
'folder'=> $thumb ? $thumb['folder'] : '',
|
||||
];
|
||||
})->values()->toArray(),
|
||||
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
|
||||
) !!};
|
||||
window._tsStore = {
|
||||
url : {!! json_encode($store_array[0]->StoreUrl, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) !!},
|
||||
currency : {!! json_encode($store_array[0]->StoreCurrency, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) !!},
|
||||
minoBase : {!! json_encode(rtrim(config('filesystems.disks.minio.url', ''), '/') . '/' . env('MINIO_BUCKET', 'crewsportswear') . '/', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) !!},
|
||||
};
|
||||
</script>
|
||||
|
||||
<div id="ts-app" v-cloak>
|
||||
|
||||
{{-- ── Category nav ── --}}
|
||||
<nav class="cat-nav" v-if="categories.length > 0">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" :class="{active: activeCategory===null && activeSubCategory===null}"
|
||||
@click.prevent="activeCategory=null; activeSubCategory=null">
|
||||
All
|
||||
<span class="cat-count">@{{ totalPublic }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li v-for="cat in categories" :key="cat.name">
|
||||
<a href="#"
|
||||
:class="{active: activeCategory===cat.name}"
|
||||
@click.prevent="selectCategory(cat.name)">
|
||||
@{{ cat.name }}
|
||||
<span class="cat-count">@{{ cat.count }}</span>
|
||||
</a>
|
||||
<ul class="sub-menu" v-if="cat.subs.length > 0">
|
||||
<li v-for="sub in cat.subs" :key="sub.name">
|
||||
<a href="#"
|
||||
:class="{active: activeCategory===cat.name && activeSubCategory===sub.name}"
|
||||
@click.prevent="selectSub(cat.name, sub.name)">
|
||||
@{{ sub.name }}
|
||||
<span class="cat-count">@{{ sub.count }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{{-- ── League / conference pill filter ── --}}
|
||||
<div class="league-filter" v-if="isLeagueStore && activeCategory !== null && leagues.length > 0">
|
||||
<span class="lf-label">League / Conf:</span>
|
||||
<a href="#"
|
||||
:class="{active: activeLeague === null}"
|
||||
@click.prevent="activeLeague = null">All</a>
|
||||
<a href="#"
|
||||
v-for="lg in leagues" :key="lg.name"
|
||||
:class="{active: activeLeague === lg.name}"
|
||||
@click.prevent="activeLeague = lg.name">
|
||||
@{{ lg.name }}
|
||||
<span class="cat-count">@{{ lg.count }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- ── Announcements (kept outside loop) ── --}}
|
||||
@if ($announcement->IsActive)
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-info">
|
||||
<p><b>Shop Announcements:</b></p>
|
||||
{!! nl2br(e($announcement->Announcement)) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($store_array[0]->Id == 174 || $store_array[0]->Id == 175 || $store_array[0]->Id == 178 || $store_array[0]->Id == 179 || $store_array[0]->Id == 177 || $store_array[0]->Id == 189 || $store_array[0]->Id == 176 || $store_array[0]->Id == 190 || $store_array[0]->Id == 191 || $store_array[0]->Id == 192 || $store_array[0]->Id == 194)
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-warning">
|
||||
<p><b>Please read:</b></p>
|
||||
1. All orders will be batch shipped to your school for pick up.<br>
|
||||
2. Orders will be batch processed on a weekly basis, please allow 2-3 weeks for delivery.<br>
|
||||
3. Masks and gaiters sold on Crew are not intended for medical use. Crew does not make any medical or health claims.<br>
|
||||
4. Refunds and exchanges are not allowed due to the hygenic nature of the product.<br>
|
||||
@if($store_array[0]->Id == 175)
|
||||
5. $1 from every item sold will benefit the 2020-2021 Maine South Schoolwide Fundraiser.
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-warning">
|
||||
@if($store_array[0]->Id == 174 || $store_array[0]->Id == 175 || $store_array[0]->Id == 178 || $store_array[0]->Id == 179 || $store_array[0]->Id == 177 || $store_array[0]->Id == 189 || $store_array[0]->Id == 176 || $store_array[0]->Id == 190 || $store_array[0]->Id == 191 || $store_array[0]->Id == 192 || $store_array[0]->Id == 194)
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-warning">
|
||||
<p><b>Please read:</b></p>
|
||||
1. Items purchased are made on demand. Orders will be shipped based on dates set by your store administrator.<br>
|
||||
2. Store payments are processed through PayPal. A PayPal account is not required to make a purchase.<br>
|
||||
@if($store_array[0]->Id == 222)
|
||||
3. Orders will be batch processed on a monthly basis, please allow 4-6 weeks for delivery.<br>
|
||||
@else
|
||||
3. Orders will be batch processed on a weekly basis, please allow 2-3 weeks for delivery.<br>
|
||||
1. All orders will be batch shipped to your school for pick up.<br>
|
||||
2. Orders will be batch processed on a weekly basis, please allow 2-3 weeks for delivery.<br>
|
||||
3. Masks and gaiters sold on Crew are not intended for medical use. Crew does not make any medical or health claims.<br>
|
||||
4. Refunds and exchanges are not allowed due to the hygenic nature of the product.<br>
|
||||
@if($store_array[0]->Id == 175)
|
||||
5. $1 from every item sold will benefit the 2020-2021 Maine South Schoolwide Fundraiser.
|
||||
@endif
|
||||
4. We are currently only shipping to US locations. For international orders, please contact <b>orders@crewsportswear.com</b> if you'd like to place an order.<br>
|
||||
5. Expect shipping delays due to COVID-19.<br>
|
||||
6. All sales are final. No returns or exchanges will be accepted.<br><br>
|
||||
|
||||
<b>DISCLAIMER:</b> Masks and gaiters sold by Crew Sportswear are not intended for medical use. Crew Sportswear does not make any medical or health claims.
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<!-- BEGIN PRODUCTS -->
|
||||
|
||||
@foreach($product_array as $i => $product)
|
||||
@if($product->PrivacyStatus == "public")
|
||||
@foreach($thumbnails as $t => $thumb)
|
||||
@if($thumb['product_id'] == $product->Id)
|
||||
@define $storeFolder = $thumb['folder']
|
||||
@define $filename = $thumb['thumb']
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<span class="thumbnail">
|
||||
<a href="{{ url('teamstore') }}/{{ $store_array[0]->StoreUrl }}/product/{{ $product->ProductURL }}">
|
||||
<img style="height: 201.84px;" src="{{ config('site_config.images_url') }}/{{ $filename }}" alt="{{ $product->ProductName }}" >
|
||||
</a>
|
||||
<h4 class="text-center product-name-holder">{{ $product->ProductName }}</h4>
|
||||
<hr class="line">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-sm-7">
|
||||
<p class="price">{{ $product->ProductPrice }} <small style="font-size: 15px;"> {{ $store_array[0]->StoreCurrency }}</small></p>
|
||||
</div>
|
||||
<div class="col-md-5 col-sm-5">
|
||||
<a href="{{ url('teamstore') }}/{{ $store_array[0]->StoreUrl }}/product/{{ $product->ProductURL }}" class="btn btn-success right" > View Details</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
<!-- END PRODUCTS -->
|
||||
|
||||
</div>
|
||||
</div> <!-- cotainer -->
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- ── Product grid ── --}}
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center" v-if="filtered.length === 0">
|
||||
<p style="margin:40px 0;color:#888;">No products found in this category.</p>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6" v-for="p in filtered" :key="p.id">
|
||||
<span class="thumbnail">
|
||||
<a :href="productUrl(p)">
|
||||
<img style="height:201.84px;" :src="imgUrl(p)" :alt="p.name">
|
||||
</a>
|
||||
<h4 class="text-center product-name-holder">@{{ p.name }}</h4>
|
||||
<hr class="line">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-sm-7">
|
||||
<p class="price">@{{ p.price }} <small style="font-size:15px;">@{{ store.currency }}</small></p>
|
||||
</div>
|
||||
<div class="col-md-5 col-sm-5">
|
||||
<a :href="productUrl(p)" class="btn btn-success right">View Details</a>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>{{-- /ts-app --}}
|
||||
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
// ── sport & item-type keyword maps ──────────────────────────────────
|
||||
const SPORTS = [
|
||||
'Basketball','Football','Soccer','Baseball','Softball',
|
||||
'Volleyball','Hockey','Lacrosse','Wrestling','Tennis',
|
||||
'Swimming','Track','Cross Country','Golf','Cheerleading',
|
||||
'Dance','Rugby','Bowling','Gymnastics','Cycling',
|
||||
];
|
||||
const ITEMS = [
|
||||
'Jersey','T-Shirt','Tee','Hoodie','Sweatshirt','Jacket',
|
||||
'Shorts','Pants','Tank','Top','Pullover','Zip-Up',
|
||||
'Hat','Cap','Beanie','Polo','Uniform','Warmup','Pinnie',
|
||||
];
|
||||
// ── league / conference keyword map ──────────────────────────────────
|
||||
// Order matters: more specific phrases first
|
||||
const LEAGUES = [
|
||||
{ key: 'NBA', terms: ['nba'] },
|
||||
{ key: 'NFL', terms: ['nfl'] },
|
||||
{ key: 'MLB', terms: ['mlb'] },
|
||||
{ key: 'NHL', terms: ['nhl'] },
|
||||
{ key: 'MLS', terms: ['mls'] },
|
||||
{ key: 'WNBA', terms: ['wnba'] },
|
||||
{ key: 'Big Ten', terms: ['big ten','big10','big 10'] },
|
||||
{ key: 'ACC', terms: ['acc'] },
|
||||
{ key: 'SEC', terms: ['sec'] },
|
||||
{ key: 'Big 12', terms: ['big 12','big12'] },
|
||||
{ key: 'Pac-12', terms: ['pac-12','pac12','pac 12'] },
|
||||
{ key: 'AAC', terms: ['aac'] },
|
||||
{ key: 'CUSA', terms: ['cusa','c-usa'] },
|
||||
{ key: 'MAC', terms: ['\bmac\b'] },
|
||||
{ key: 'Sun Belt',terms: ['sun belt'] },
|
||||
{ key: 'Mountain West', terms: ['mountain west','mwc'] },
|
||||
];
|
||||
|
||||
function classify(name) {
|
||||
const n = name.toLowerCase();
|
||||
const sport = SPORTS.find(s => n.includes(s.toLowerCase())) || 'Other';
|
||||
const item = ITEMS.find(i => n.includes(i.toLowerCase())) || 'Other';
|
||||
let league = null;
|
||||
for (const lg of LEAGUES) {
|
||||
if (lg.terms.some(t => {
|
||||
try { return new RegExp(t, 'i').test(n); } catch(e) { return n.includes(t); }
|
||||
})) {
|
||||
league = lg.key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { sport, item, league };
|
||||
}
|
||||
|
||||
const { createApp } = Vue;
|
||||
const store = window._tsStore || { url:'', currency:'', minoBase:'' };
|
||||
const allProducts = Array.isArray(window._tsProducts) ? window._tsProducts : [];
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
products : allProducts,
|
||||
store : store,
|
||||
activeCategory : null,
|
||||
activeSubCategory: null,
|
||||
activeLeague : null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
totalPublic() {
|
||||
return this.products.length;
|
||||
},
|
||||
isLeagueStore() {
|
||||
return this.store.url === 'hi-five-franchise-store';
|
||||
},
|
||||
// Build [ { name:'Basketball', count:N, subs:[{name:'Jersey',count:N},...] }, ... ]
|
||||
categories() {
|
||||
const map = {};
|
||||
this.products.forEach(p => {
|
||||
const { sport, item } = classify(p.name);
|
||||
if (!map[sport]) map[sport] = {};
|
||||
map[sport][item] = (map[sport][item] || 0) + 1;
|
||||
});
|
||||
return Object.entries(map)
|
||||
.sort((a,b) => a[0].localeCompare(b[0]))
|
||||
.map(([name, subs]) => ({
|
||||
name,
|
||||
count: Object.values(subs).reduce((a,b) => a+b, 0),
|
||||
subs: Object.entries(subs)
|
||||
.sort((a,b) => a[0].localeCompare(b[0]))
|
||||
.map(([s, count]) => ({ name: s, count }))
|
||||
.filter(s => s.name !== 'Other' || Object.keys(subs).length === 1),
|
||||
}));
|
||||
},
|
||||
// Available leagues for the currently active category (+ optional sub)
|
||||
// Only computed for hi-five-franchise-store
|
||||
leagues() {
|
||||
if (!this.isLeagueStore) return [];
|
||||
const map = {};
|
||||
this.products.forEach(p => {
|
||||
const { sport, item, league } = classify(p.name);
|
||||
if (!league) return;
|
||||
if (this.activeCategory && sport !== this.activeCategory) return;
|
||||
if (this.activeSubCategory && item !== this.activeSubCategory) return;
|
||||
map[league] = (map[league] || 0) + 1;
|
||||
});
|
||||
return Object.entries(map)
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([name, count]) => ({ name, count }));
|
||||
},
|
||||
filtered() {
|
||||
if (!this.activeCategory && !this.activeLeague) return this.products;
|
||||
return this.products.filter(p => {
|
||||
const { sport, item, league } = classify(p.name);
|
||||
if (this.activeCategory && sport !== this.activeCategory) return false;
|
||||
if (this.activeSubCategory && item !== this.activeSubCategory) return false;
|
||||
if (this.isLeagueStore && this.activeLeague && league !== this.activeLeague) return false;
|
||||
return true;
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
selectCategory(cat) {
|
||||
this.activeCategory = cat;
|
||||
this.activeSubCategory = null;
|
||||
this.activeLeague = null;
|
||||
},
|
||||
selectSub(cat, sub) {
|
||||
this.activeCategory = cat;
|
||||
this.activeSubCategory = sub;
|
||||
this.activeLeague = null;
|
||||
},
|
||||
productUrl(p) {
|
||||
return '/teamstore/' + store.url + '/product/' + p.url;
|
||||
},
|
||||
imgUrl(p) {
|
||||
return store.minoBase + 'images/' + p.img;
|
||||
},
|
||||
},
|
||||
}).mount('#ts-app');
|
||||
})();
|
||||
</script>
|
||||
|
||||
</div>{{-- /container --}}
|
||||
@endsection
|
||||
|
||||
@@ -234,13 +234,13 @@
|
||||
@if($thumbnail->ImageClass == 'active')
|
||||
<div class="active item text-center" data-slide-number="{{ $i }}">
|
||||
<span class="zoom img-zoom">
|
||||
<img style="height:400px;" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image }}">
|
||||
<img style="height:400px;" src="{{ minio_url('images/' . $thumbnail->Image) }}">
|
||||
</span>
|
||||
</div>
|
||||
@else
|
||||
<div class="item text-center" data-slide-number="{{ $i }}">
|
||||
<span class="zoom img-zoom">
|
||||
<img style="height:400px;" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image }}">
|
||||
<img style="height:400px;" src="{{ minio_url('images/' . $thumbnail->Image) }}">
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
@@ -269,7 +269,7 @@
|
||||
@foreach($thumbnails_array as $thumbnail)
|
||||
<li class="col-sm-3 col-xs-3">
|
||||
<a class="thumbnail a_thumbnail {{ $thumbnail->ImageClass }}" id="carousel-selector-{{ $j }}">
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image }}"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $thumbnail->Image) }}"/>
|
||||
</a>
|
||||
</li>
|
||||
@define $j++
|
||||
|
||||
@@ -82,12 +82,12 @@
|
||||
<div style="border: 1px solid #dddddd; padding: 5px;">
|
||||
@if($store->Password != null )
|
||||
<a class="thumbnail password-protected" href="#" data-store-id="{{ $store->Id }}" data-store-url="{{ $store->StoreUrl }}">
|
||||
<img class="store-logo" src="{{ config('site_config.uploads') . 'teamstore/'. $store->ImageFolder . '/' . $store->StoreLogo }}">
|
||||
<img class="store-logo" src="{{ minio_url('uploads/images/teamstore/' . $store->ImageFolder . '/' . $store->StoreLogo) }}">
|
||||
</a>
|
||||
<h4 style="border-top: 1px solid #dddddd; padding: 10px; font-size: 16px; font-weight: bold; text-transform: uppercase;" class="text-center">{{ $store->StoreName }} <i class="fa fa-lock" title="This store is password protected."></i></h4>
|
||||
@else
|
||||
<a class="thumbnail" href="{{ url('teamstore') . '/' . $store->StoreUrl }}">
|
||||
<img class="store-logo" src="{{ config('site_config.uploads') . 'teamstore/' . $store->ImageFolder . '/' . $store->StoreLogo }}">
|
||||
<img class="store-logo" src="{{ minio_url('uploads/images/teamstore/' . $store->ImageFolder . '/' . $store->StoreLogo) }}">
|
||||
</a>
|
||||
<h4 style="border-top: 1px solid #dddddd; padding: 10px; font-size: 16px; font-weight: bold; text-transform: uppercase;" class="text-center">{{ $store->StoreName }}</h4>
|
||||
@endif
|
||||
|
||||
56
resources/views/test-email.blade.php
Normal file
56
resources/views/test-email.blade.php
Normal file
@@ -0,0 +1,56 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3" style="margin-top: 40px; margin-bottom: 40px;">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><strong>Send Test Email</strong></h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
@if (session('message'))
|
||||
<div class="alert alert-{{ session('status') }} alert-dismissible" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
{{ session('message') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Mail Config Info -->
|
||||
<div class="well well-sm" style="font-size: 12px;">
|
||||
<strong>Current Mail Config</strong><br>
|
||||
Driver: <code>{{ config('mail.driver') ?: '(not set)' }}</code><br>
|
||||
Host: <code>{{ config('mail.host') ?: '(not set)' }}</code><br>
|
||||
Port: <code>{{ config('mail.port') ?: '(not set)' }}</code><br>
|
||||
Username: <code>{{ config('mail.username') ?: '(not set)' }}</code><br>
|
||||
Encryption: <code>{{ config('mail.encryption') ?: '(not set)' }}</code>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url('test-email/send') }}">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="token" value="{{ $token }}">
|
||||
<div class="form-group{{ $errors->has('recipient') ? ' has-error' : '' }}">
|
||||
<label for="recipient">Recipient Email</label>
|
||||
<input type="email"
|
||||
name="recipient"
|
||||
id="recipient"
|
||||
class="form-control"
|
||||
placeholder="you@example.com"
|
||||
value="{{ old('recipient') }}"
|
||||
required>
|
||||
@if ($errors->has('recipient'))
|
||||
<span class="help-block">{{ $errors->first('recipient') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<i class="fa fa-envelope"></i> Send Test Email
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -42,7 +42,7 @@
|
||||
@foreach($array_client_designs as $row)
|
||||
@foreach($array_template_paths as $key => $row1)
|
||||
@if($key == 0)
|
||||
<img src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-front-thumbnail.png" alt="{{ $row->DesignName }}" id="main-thumbnail" class="img img-responsive">
|
||||
<img src="{{ minio_url('images/' . $row->DesignCode . '-front-thumbnail.png') }}" alt="{{ $row->DesignName }}" id="main-thumbnail" class="img img-responsive">
|
||||
@endif
|
||||
@endforeach
|
||||
@endforeach
|
||||
@@ -56,7 +56,7 @@
|
||||
@if($key == 0)
|
||||
<li class="col-sm-3 col-xs-3">
|
||||
<a class="thumbnail a_thumbnail active">
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-front-thumbnail.png"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $row->DesignCode . '-front-thumbnail.png') }}"/>
|
||||
</a>
|
||||
<!-- <p>Select Default Thumbnail:</p>
|
||||
<div class="text-center">
|
||||
@@ -66,7 +66,7 @@
|
||||
@else
|
||||
<li class="col-sm-3 col-xs-3">
|
||||
<a class="thumbnail a_thumbnail">
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-{{ strtolower($row1->Side) }}-thumbnail.png"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $row->DesignCode . '-' . strtolower($row1->Side) . '-thumbnail.png') }}"/>
|
||||
</a>
|
||||
<!-- <p> </p>
|
||||
<div class="text-center">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<!-- Control Sidebar Toggle Button -->
|
||||
<li class="user user-menu">
|
||||
<a href="#">
|
||||
<img src="{{ config('site_config.uploads') . 'user/default-user.png' }}" class="user-image" alt="User Image">
|
||||
<img src="{{ minio_url('uploads/images/user/default-user.png') }}" class="user-image" alt="User Image">
|
||||
<span class="hidden-xs">{{ Auth::user()->username }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
@foreach($array_client_designs as $row)
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<span class="thumbnail">
|
||||
<img src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-front-thumbnail.png" alt="{{ $row->DesignName }}" >
|
||||
<img src="{{ minio_url('images/' . $row->DesignCode . '-front-thumbnail.png') }}" alt="{{ $row->DesignName }}" >
|
||||
<h4 class="design-name-holder">{{ $row->DesignName }}</h4>
|
||||
<small>{{ date('F j, Y g:i a', strtotime($row->DateCreated)) }}</small>
|
||||
<hr class="line">
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="text-center">
|
||||
<img class="previewImage" id="active_thumbnail" src="{{ config('site_config.images_url') . '/images/' . $array_thumbnail_display[0]->Image }}">
|
||||
<img class="previewImage" id="active_thumbnail" src="{{ minio_url('images/' . $array_thumbnail_display[0]->Image) }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<div class="text-center">
|
||||
@foreach($img_thumb as $img)
|
||||
@if($img->ProductId == $item->ProductId)
|
||||
<img class="previewImage" src="{{ config('site_config.images_url') }}/{{ $img->Image }}">
|
||||
<img class="previewImage" src="{{ minio_url('images/' . $img->Image) }}">
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
@foreach($array_client_designs as $row)
|
||||
@foreach($array_template_paths as $key => $row1)
|
||||
@if($key == 0)
|
||||
<img src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-front-thumbnail.png" alt="{{ $row->DesignName }}" id="main-thumbnail" class="img img-responsive">
|
||||
<img src="{{ minio_url('images/' . $row->DesignCode . '-front-thumbnail.png') }}" alt="{{ $row->DesignName }}" id="main-thumbnail" class="img img-responsive">
|
||||
@endif
|
||||
@endforeach
|
||||
@endforeach
|
||||
@@ -56,7 +56,7 @@
|
||||
@if($key == 0)
|
||||
<li class="col-sm-3 col-xs-3">
|
||||
<a class="thumbnail a_thumbnail active">
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-front-thumbnail.png"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $row->DesignCode . '-front-thumbnail.png') }}"/>
|
||||
</a>
|
||||
<!-- <p>Select Default Thumbnail:</p>
|
||||
<div class="text-center">
|
||||
@@ -66,7 +66,7 @@
|
||||
@else
|
||||
<li class="col-sm-3 col-xs-3">
|
||||
<a class="thumbnail a_thumbnail">
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-{{ strtolower($row1->Side) }}-thumbnail.png"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $row->DesignCode . '-' . strtolower($row1->Side) . '-thumbnail.png') }}"/>
|
||||
</a>
|
||||
<!-- <p> </p>
|
||||
<div class="text-center">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<!-- Sidebar user panel -->
|
||||
<div class="user-panel">
|
||||
<div class="pull-left image">
|
||||
<img src="{{ config('site_config.uploads') . 'user/default-user.png' }}" class="img-circle" alt="User Image">
|
||||
<img src="{{ minio_url('uploads/images/user/default-user.png') }}" class="img-circle" alt="User Image">
|
||||
</div>
|
||||
<div class="pull-left info">
|
||||
<p>{{ Auth::user()->name }}</p>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div class="thumbnail" >
|
||||
<a href="{{ url('user/store-items/item') }}/{{ $product->ProductURL }}">
|
||||
<img style="height:200px" src="{{ config('site_config.images_url') }}/{{ $filename . '?t=' . time() }}" alt="{{ $product->ProductName }}" >
|
||||
<img style="height:200px" src="{{ minio_url('images/' . $filename) . '?t=' . time() }}" alt="{{ $product->ProductName }}" >
|
||||
</a>
|
||||
<hr class="line">
|
||||
<div class="pull-right">
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<div id="{{ 'order_number_' . $product->Id }}">
|
||||
<div class="thumbnail" >
|
||||
<a href="#">
|
||||
<img style="height:200px" src="{{ config('site_config.images_url') }}/{{ $filename . '?t=' . time() }}" alt="{{ $product->ProductName }}" >
|
||||
<img style="height:200px" src="{{ minio_url('images/' . $filename) . '?t=' . time() }}" alt="{{ $product->ProductName }}" >
|
||||
</a>
|
||||
<hr class="line">
|
||||
<div class="pull-right">
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
<div class="col-sm-8">
|
||||
<p>Store Logo Preview:</p>
|
||||
<div class="store-logo-holder">
|
||||
<a href="{{ config('site_config.uploads') . 'teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreLogo }}?v={{ time() }}" class="img_store_logo_href" data-toggle="lightbox">
|
||||
<img class="img_store_logo_img" id="img_store_logo" src="{{ config('site_config.uploads') . 'teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreLogo }}?v={{ time() }}" style="max-width: 100%; max-height: 100%; ">
|
||||
<a href="{{ minio_url('uploads/images/teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreLogo) }}?v={{ time() }}" class="img_store_logo_href" data-toggle="lightbox">
|
||||
<img class="img_store_logo_img" id="img_store_logo" src="{{ minio_url('uploads/images/teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreLogo) }}?v={{ time() }}" style="max-width: 100%; max-height: 100%; ">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -82,8 +82,8 @@
|
||||
<div class="col-sm-8">
|
||||
<p>Store Banner Preview:</p>
|
||||
<div class="store-banner-holder">
|
||||
<a href="{{ config('site_config.uploads') . 'teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreBanner }}?v={{ time() }}" class="img_store_banner_href" data-toggle="lightbox">
|
||||
<img class="img_store_banner_img" id="img_store_banner" src="{{ config('site_config.uploads') . 'teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreBanner }}?v={{ time() }}" style="max-width: 100%; max-height: 100%;">
|
||||
<a href="{{ minio_url('uploads/images/teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreBanner) }}?v={{ time() }}" class="img_store_banner_href" data-toggle="lightbox">
|
||||
<img class="img_store_banner_img" id="img_store_banner" src="{{ minio_url('uploads/images/teamstore/' . $store_array[0]->ImageFolder . '/' . $store_array[0]->StoreBanner) }}?v={{ time() }}" style="max-width: 100%; max-height: 100%;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
@foreach($array_client_designs as $row)
|
||||
@foreach($array_template_paths as $key => $row1)
|
||||
@if($key == 0)
|
||||
<img src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-front-thumbnail.png" alt="{{ $row->DesignName }}" id="main-thumbnail" class="img img-responsive">
|
||||
<img src="{{ minio_url('images/' . $row->DesignCode . '-front-thumbnail.png') }}" alt="{{ $row->DesignName }}" id="main-thumbnail" class="img img-responsive">
|
||||
@endif
|
||||
@endforeach
|
||||
@endforeach
|
||||
@@ -53,13 +53,13 @@
|
||||
@if($key == 0)
|
||||
<li class="col-sm-3 col-xs-3">
|
||||
<a class="thumbnail a_thumbnail active">
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-front-thumbnail.png"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $row->DesignCode . '-front-thumbnail.png') }}"/>
|
||||
</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="col-sm-3 col-xs-3">
|
||||
<a class="thumbnail a_thumbnail">
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $row->DesignCode }}-{{ strtolower($row1->Side) }}-thumbnail.png"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $row->DesignCode . '-' . strtolower($row1->Side) . '-thumbnail.png') }}"/>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<div class="col-md-12 text-center">
|
||||
@foreach($thumbnails_array as $thumbnail)
|
||||
@if($thumbnail->ImageClass == 'active')
|
||||
<img style="height:400px" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}" id="main-thumbnail">
|
||||
<img style="height:400px" src="{{ minio_url('images/' . $thumbnail->Image) . '?t=' . time() }}" id="main-thumbnail">
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<li class="col-sm-3 col-xs-4">
|
||||
<a class="thumbnail a_thumbnail {{ $thumbnail->ImageClass }}" style="border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; margin-bottom: -28px;">
|
||||
<!-- <span class="close">×</span> -->
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}"/>
|
||||
<img class="img img-responsive product-center image-thumbnails" style="height: 59.45px;" src="{{ minio_url('images/' . $thumbnail->Image) . '?t=' . time() }}"/>
|
||||
</a>
|
||||
<div class="funkyradio">
|
||||
<div class="funkyradio-primary">
|
||||
@@ -200,7 +200,7 @@
|
||||
|
||||
<tr id="{{ 'item-' . $thumbnail->Id }}">
|
||||
<td class="text-center" style="width: 50px"><i class="fa fa-bars"></i></td>
|
||||
<td><img class="img img-responsive product-center" style="height: 59.45px;" src="{{ config('site_config.images_url') }}/{{ $thumbnail->Image . '?t=' . time() }}"/></td>
|
||||
<td><img class="img img-responsive product-center" style="height: 59.45px;" src="{{ minio_url('images/' . $thumbnail->Image) . '?t=' . time() }}"/></td>
|
||||
<td class="text-center">
|
||||
<!-- <button class="btn btn-default btn-xs btn-edit-clipart" data-id="#"><i class="fa fa-edit"></i></button> -->
|
||||
<button class="btn btn-default btn-sm btn-delete-item-image" data-id="{{ $thumbnail->Id }}" data-filename="{{ $thumbnail->Image }}" title="Delete Image"><i class="fa fa-trash"></i></button>
|
||||
|
||||
Reference in New Issue
Block a user