Added: Product & Upload Module
This commit is contained in:
parent
017ea56668
commit
cb5a3f00ae
|
@ -16,6 +16,7 @@ class CreateProductsTable extends Migration
|
||||||
Schema::create('products', function (Blueprint $table) {
|
Schema::create('products', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->unsignedBigInteger('category_id');
|
$table->unsignedBigInteger('category_id');
|
||||||
|
$table->string('product_image')->nullable();
|
||||||
$table->string('product_name');
|
$table->string('product_name');
|
||||||
$table->string('product_code')->unique()->nullable();
|
$table->string('product_code')->unique()->nullable();
|
||||||
$table->string('product_barcode_symbology')->nullable();
|
$table->string('product_barcode_symbology')->nullable();
|
||||||
|
@ -24,7 +25,7 @@ class CreateProductsTable extends Migration
|
||||||
$table->integer('product_price');
|
$table->integer('product_price');
|
||||||
$table->integer('product_stock_alert');
|
$table->integer('product_stock_alert');
|
||||||
$table->integer('product_order_tax')->nullable();
|
$table->integer('product_order_tax')->nullable();
|
||||||
$table->boolean('product_tax_type')->nullable();
|
$table->tinyInteger('product_tax_type')->nullable();
|
||||||
$table->text('product_note')->nullable();
|
$table->text('product_note')->nullable();
|
||||||
$table->foreign('category_id')->references('id')->on('categories')->restrictOnDelete();
|
$table->foreign('category_id')->references('id')->on('categories')->restrictOnDelete();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
|
@ -12,6 +12,6 @@ class Category extends Model
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
public function products() {
|
public function products() {
|
||||||
return $this->hasMany(Product::class, 'id', 'category_id');
|
return $this->hasMany(Product::class, 'category_id', 'id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ class Product extends Model implements HasMedia
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
protected $with = ['media'];
|
||||||
|
|
||||||
public function category() {
|
public function category() {
|
||||||
return $this->belongsTo(Category::class, 'category_id', 'id');
|
return $this->belongsTo(Category::class, 'category_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@ use App\DataTables\ProductDataTable;
|
||||||
use Illuminate\Contracts\Support\Renderable;
|
use Illuminate\Contracts\Support\Renderable;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Controller;
|
use Illuminate\Routing\Controller;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Modules\Product\Entities\Product;
|
||||||
|
use Modules\Product\Http\Requests\ProductCreateRequest;
|
||||||
|
use Modules\Product\Http\Requests\ProductUpdateRequest;
|
||||||
|
use Modules\Upload\Entities\Upload;
|
||||||
|
|
||||||
class ProductController extends Controller
|
class ProductController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -31,8 +36,32 @@ class ProductController extends Controller
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function store(Request $request) {
|
public function store(ProductCreateRequest $request) {
|
||||||
//
|
$product = Product::create($request->except('image'));
|
||||||
|
|
||||||
|
if ($request->has('image')) {
|
||||||
|
$tempFile = Upload::where('folder', $request->image)->first();
|
||||||
|
|
||||||
|
if ($tempFile) {
|
||||||
|
$product->addMedia(Storage::path('public/temp/' . $request->image . '/' . $tempFile->filename))->toMediaCollection();
|
||||||
|
|
||||||
|
Storage::deleteDirectory('public/temp/' . $request->image);
|
||||||
|
$tempFile->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toast('Product Created!', 'success');
|
||||||
|
|
||||||
|
return redirect()->route('products.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the details for the specified resource.
|
||||||
|
* @param int $id
|
||||||
|
* @return Renderable
|
||||||
|
*/
|
||||||
|
public function show(Product $product) {
|
||||||
|
return view('product::products.show', compact('product'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,8 +69,8 @@ class ProductController extends Controller
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function edit($id) {
|
public function edit(Product $product) {
|
||||||
return view('product::products.edit');
|
return view('product::products.edit', compact('product'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,8 +79,26 @@ class ProductController extends Controller
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function update(Request $request, $id) {
|
public function update(ProductUpdateRequest $request, Product $product) {
|
||||||
//
|
$product->update($request->except('image'));
|
||||||
|
|
||||||
|
if ($request->has('image')) {
|
||||||
|
$tempFile = Upload::where('folder', $request->image)->first();
|
||||||
|
|
||||||
|
$media = $product->getMedia();
|
||||||
|
$media[0]->delete();
|
||||||
|
|
||||||
|
if ($tempFile) {
|
||||||
|
$product->addMedia(Storage::path('public/temp/' . $request->image . '/' . $tempFile->filename))->toMediaCollection();
|
||||||
|
|
||||||
|
Storage::deleteDirectory('public/temp/' . $request->image);
|
||||||
|
$tempFile->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toast('Product Updated!', 'info');
|
||||||
|
|
||||||
|
return redirect()->route('products.index');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +106,11 @@ class ProductController extends Controller
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return Renderable
|
* @return Renderable
|
||||||
*/
|
*/
|
||||||
public function destroy($id) {
|
public function destroy(Product $product) {
|
||||||
//
|
$product->delete();
|
||||||
|
|
||||||
|
toast('Product Deleted!', 'warning');
|
||||||
|
|
||||||
|
return redirect()->route('products.index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\Product\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ProductCreateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'product_name' => ['required', 'string', 'max:255'],
|
||||||
|
'product_code' => ['required', 'string', 'max:255', 'unique:products,product_code'],
|
||||||
|
'product_barcode_symbology' => ['required', 'string', 'max:255'],
|
||||||
|
'product_quantity' => ['required', 'integer', 'min:1'],
|
||||||
|
'product_cost' => ['required', 'integer'],
|
||||||
|
'product_price' => ['required', 'integer'],
|
||||||
|
'product_stock_alert' => ['required', 'integer', 'min:0'],
|
||||||
|
'product_order_tax' => ['nullable', 'integer', 'min:1'],
|
||||||
|
'product_tax_type' => ['nullable', 'integer'],
|
||||||
|
'product_note' => ['nullable', 'string', 'max:1000'],
|
||||||
|
'category_id' => ['required', 'integer']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\Product\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class ProductUpdateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'product_name' => ['required', 'string', 'max:255'],
|
||||||
|
'product_code' => ['required', 'string', 'max:255', 'unique:products,product_code,' . $this->product->id],
|
||||||
|
'product_barcode_symbology' => ['required', 'string', 'max:255'],
|
||||||
|
'product_quantity' => ['required', 'integer', 'min:1'],
|
||||||
|
'product_cost' => ['required', 'integer'],
|
||||||
|
'product_price' => ['required', 'integer'],
|
||||||
|
'product_stock_alert' => ['required', 'integer', 'min:0'],
|
||||||
|
'product_order_tax' => ['nullable', 'integer', 'min:1'],
|
||||||
|
'product_tax_type' => ['nullable', 'integer'],
|
||||||
|
'product_note' => ['nullable', 'string', 'max:1000'],
|
||||||
|
'category_id' => ['required', 'integer']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,12 @@
|
||||||
<a href="{{ route('product-categories.edit', $data->id) }}" class="btn btn-info btn-sm">
|
<a href="{{ route('product-categories.edit', $data->id) }}" class="btn btn-info btn-sm">
|
||||||
<i class="bi bi-pencil"></i>
|
<i class="bi bi-pencil"></i>
|
||||||
</a>
|
</a>
|
||||||
<button id="delete" class="btn btn-danger btn-sm" onclick="e.preventDefault();document.getElementById('destroy{{ $data->id }}').submit();">
|
<button id="delete" class="btn btn-danger btn-sm" onclick="
|
||||||
|
e.preventDefault();
|
||||||
|
if (confirm('Are you sure? It will delete the data permanently!')) {
|
||||||
|
document.getElementById('destroy{{ $data->id }}').submit();
|
||||||
|
}
|
||||||
|
">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
<form id="destroy{{ $data->id }}" class="d-none" action="{{ route('product-categories.destroy', $data->id) }}">
|
<form id="destroy{{ $data->id }}" class="d-none" action="{{ route('product-categories.destroy', $data->id) }}">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
|
@ -7,14 +7,6 @@
|
||||||
<link href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" rel="stylesheet">
|
<link href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" rel="stylesheet">
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('page_css')
|
|
||||||
<style>
|
|
||||||
.filepond--credits {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@endpush
|
|
||||||
|
|
||||||
@section('breadcrumb')
|
@section('breadcrumb')
|
||||||
<ol class="breadcrumb border-0 m-0">
|
<ol class="breadcrumb border-0 m-0">
|
||||||
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
|
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
|
||||||
|
@ -29,6 +21,7 @@
|
||||||
@csrf
|
@csrf
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
|
@include('utils.alerts')
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-primary">Create Product <i class="bi bi-check"></i></button>
|
<button class="btn btn-primary">Create Product <i class="bi bi-check"></i></button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,7 +59,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="barcode_symbology">Barcode Symbology <span class="text-danger">*</span></label>
|
<label for="barcode_symbology">Barcode Symbology <span class="text-danger">*</span></label>
|
||||||
<select class="form-control" name="barcode_symbology" id="barcode_symbology" required>
|
<select class="form-control" name="product_barcode_symbology" id="barcode_symbology" required>
|
||||||
<option value="" selected disabled>Select Symbology</option>
|
<option value="" selected disabled>Select Symbology</option>
|
||||||
<option value="C128">Code 128</option>
|
<option value="C128">Code 128</option>
|
||||||
<option value="C39">Code 39</option>
|
<option value="C39">Code 39</option>
|
||||||
|
@ -102,8 +95,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="stock_alert">Alert Quantity <span class="text-danger">*</span></label>
|
<label for="product_stock_alert">Alert Quantity <span class="text-danger">*</span></label>
|
||||||
<input type="number" class="form-control" name="stock_alert" required value="{{ old('stock_alert') }}" min="0">
|
<input type="number" class="form-control" name="product_stock_alert" required value="{{ old('stock_alert') }}" min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,16 +105,16 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="product_order_tax">Tax (%)</label>
|
<label for="product_order_tax">Tax (%)</label>
|
||||||
<input type="number" class="form-control" name="product_order_tax" required value="{{ old('product_order_tax') }}" min="1">
|
<input type="number" class="form-control" name="product_order_tax" value="{{ old('product_order_tax') }}" min="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="product_tax_type">Tax type</label>
|
<label for="product_tax_type">Tax type</label>
|
||||||
<select class="form-control" name="product_tax_type" id="product_tax_type" required>
|
<select class="form-control" name="product_tax_type" id="product_tax_type">
|
||||||
<option value="" selected disabled>Select Tax Type</option>
|
<option value="" selected disabled>Select Tax Type</option>
|
||||||
<option value="0">Exclusive</option>
|
<option value="1">Exclusive</option>
|
||||||
<option value="1">Inclusive</option>
|
<option value="2">Inclusive</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -135,7 +128,7 @@
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="image">Product Image <span class="text-danger">*</span></label>
|
<label for="image">Product Image <span class="text-danger">*</span></label>
|
||||||
<input id="image" type="file" name="product_image" required data-max-file-size="500KB">
|
<input id="image" type="file" name="image" required data-max-file-size="500KB">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('title', 'Edit Product')
|
||||||
|
|
||||||
|
@section('third_party_stylesheets')
|
||||||
|
<link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet" />
|
||||||
|
<link href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" rel="stylesheet">
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('breadcrumb')
|
||||||
|
<ol class="breadcrumb border-0 m-0">
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('products.index') }}">Products</a></li>
|
||||||
|
<li class="breadcrumb-item active">Edit</li>
|
||||||
|
</ol>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid mb-4">
|
||||||
|
<form action="{{ route('products.update', $product->id) }}" method="POST" enctype="multipart/form-data">
|
||||||
|
@csrf
|
||||||
|
@method('patch')
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
@include('utils.alerts')
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-primary">Update Product <i class="bi bi-check"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-md-7">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_name">Product Name <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" name="product_name" required value="{{ $product->product_name }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_code">Code <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" name="product_code" required value="{{ $product->product_code }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="category_id">Category <span class="text-danger">*</span></label>
|
||||||
|
<select class="form-control" name="category_id" id="category_id" required>
|
||||||
|
@foreach(\Modules\Product\Entities\Category::all() as $category)
|
||||||
|
<option {{ $category->id == $product->category->id ? 'selected' : '' }} value="{{ $category->id }}">{{ $category->category_name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="barcode_symbology">Barcode Symbology <span class="text-danger">*</span></label>
|
||||||
|
<select class="form-control" name="product_barcode_symbology" id="barcode_symbology" required>
|
||||||
|
<option {{ $product->product_barcode_symbology == 'C128' ? 'selected' : '' }} value="C128">Code 128</option>
|
||||||
|
<option {{ $product->product_barcode_symbology == 'C39' ? 'selected' : '' }} value="C39">Code 39</option>
|
||||||
|
<option {{ $product->product_barcode_symbology == 'UPCA' ? 'selected' : '' }} value="UPCA">UPC-A</option>
|
||||||
|
<option {{ $product->product_barcode_symbology == 'UPCE' ? 'selected' : '' }} value="UPCE">UPC-E</option>
|
||||||
|
<option {{ $product->product_barcode_symbology == 'EAN13' ? 'selected' : '' }} value="EAN13">EAN-13</option>
|
||||||
|
<option {{ $product->product_barcode_symbology == 'EAN8' ? 'selected' : '' }} value="EAN8">EAN-8</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_cost">Cost <span class="text-danger">*</span></label>
|
||||||
|
<input type="number" class="form-control" name="product_cost" required value="{{ $product->product_cost }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_price">Price <span class="text-danger">*</span></label>
|
||||||
|
<input type="number" class="form-control" name="product_price" required value="{{ $product->product_price }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_quantity">Staring Quantity <span class="text-danger">*</span></label>
|
||||||
|
<input type="number" class="form-control" name="product_quantity" required value="{{ $product->product_quantity }}" min="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_stock_alert">Alert Quantity <span class="text-danger">*</span></label>
|
||||||
|
<input type="number" class="form-control" name="product_stock_alert" required value="{{ $product->product_stock_alert }}" min="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_order_tax">Tax (%)</label>
|
||||||
|
<input type="number" class="form-control" name="product_order_tax" value="{{ $product->product_order_tax }}" min="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_tax_type">Tax type</label>
|
||||||
|
<select class="form-control" name="product_tax_type" id="product_tax_type">
|
||||||
|
<option value="" selected disabled>Select Tax Type</option>
|
||||||
|
<option {{ $product->product_tax_type == 1 ? 'selected' : '' }} value="1">Exclusive</option>
|
||||||
|
<option {{ $product->product_tax_type == 2 ? 'selected' : '' }} value="2">Inclusive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="image">Product Image</label>
|
||||||
|
<img class="img-thumbnail img-fluid mb-3" src="{{ $product->getFirstMediaUrl() }}" alt="Product Image">
|
||||||
|
<input id="image" type="file" name="image" data-max-file-size="500KB">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('third_party_scripts')
|
||||||
|
<script src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"></script>
|
||||||
|
<script src="https://unpkg.com/filepond-plugin-file-validate-size/dist/filepond-plugin-file-validate-size.js"></script>
|
||||||
|
<script src="https://unpkg.com/filepond-plugin-file-validate-type/dist/filepond-plugin-file-validate-type.js"></script>
|
||||||
|
<script src="https://unpkg.com/filepond/dist/filepond.js"></script>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@push('page_scripts')
|
||||||
|
<script>
|
||||||
|
FilePond.registerPlugin(
|
||||||
|
FilePondPluginImagePreview,
|
||||||
|
FilePondPluginFileValidateSize,
|
||||||
|
FilePondPluginFileValidateType
|
||||||
|
);
|
||||||
|
const fileElement = document.querySelector('input[id="image"]');
|
||||||
|
const pond = FilePond.create( fileElement, {
|
||||||
|
acceptedFileTypes: ['image/png', 'image/jpg', 'image/jpeg'],
|
||||||
|
} );
|
||||||
|
FilePond.setOptions({
|
||||||
|
server: {
|
||||||
|
url: "{{ route('filepond.upload') }}",
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-TOKEN": "{{ csrf_token() }}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
|
|
@ -4,7 +4,12 @@
|
||||||
<a href="{{ route('products.show', $data->id) }}" class="btn btn-primary btn-sm">
|
<a href="{{ route('products.show', $data->id) }}" class="btn btn-primary btn-sm">
|
||||||
<i class="bi bi-eye"></i>
|
<i class="bi bi-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
<button id="delete" class="btn btn-danger btn-sm" onclick="event.preventDefault();document.getElementById('destroy{{ $data->id }}').submit();">
|
<button id="delete" class="btn btn-danger btn-sm" onclick="
|
||||||
|
event.preventDefault();
|
||||||
|
if (confirm('Are you sure? It will delete the data permanently!')) {
|
||||||
|
document.getElementById('destroy{{ $data->id }}').submit()
|
||||||
|
}
|
||||||
|
">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
<form id="destroy{{ $data->id }}" class="d-none" action="{{ route('products.destroy', $data->id) }}" method="POST">
|
<form id="destroy{{ $data->id }}" class="d-none" action="{{ route('products.destroy', $data->id) }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('title', 'Product Details')
|
||||||
|
|
||||||
|
@section('breadcrumb')
|
||||||
|
<ol class="breadcrumb border-0 m-0">
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('products.index') }}">Products</a></li>
|
||||||
|
<li class="breadcrumb-item active">Details</li>
|
||||||
|
</ol>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid mb-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-7">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-hover">
|
||||||
|
<tr>
|
||||||
|
<th>Product Code</th>
|
||||||
|
<td>{{ $product->product_code }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<td>{{ $product->product_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Category</th>
|
||||||
|
<td>{{ $product->category->category_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Cost</th>
|
||||||
|
<td>{{ $product->product_cost }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Price</th>
|
||||||
|
<td>{{ $product->product_price }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<td>{{ $product->product_quantity }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Alert Quantity</th>
|
||||||
|
<td>{{ $product->product_stock_alert }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Tax (%)</th>
|
||||||
|
<td>{{ $product->product_order_tax ?? 'N/A' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Tax Type</th>
|
||||||
|
<td>
|
||||||
|
@if($product->product_tax_type == 1)
|
||||||
|
Exclusive
|
||||||
|
@elseif($product->product_tax_type == 2)
|
||||||
|
Inclusive
|
||||||
|
@else
|
||||||
|
N/A
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<img src="{{ $product->getFirstMediaUrl() }}" alt="Product Image" class="img-fluid img-thumbnail">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//Product
|
Route::group(['middleware' => 'auth'], function () {
|
||||||
Route::resource('products', 'ProductController');
|
//Product
|
||||||
|
Route::resource('products', 'ProductController');
|
||||||
|
//Product Category
|
||||||
|
Route::resource('product-categories', 'CategoriesController')->except('create', 'show');
|
||||||
|
});
|
||||||
|
|
||||||
//Product Category
|
|
||||||
Route::resource('product-categories', 'CategoriesController')->except('create', 'show');
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class CreateUploadsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('uploads', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('folder');
|
||||||
|
$table->string('filename');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('uploads');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\Upload\Entities;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
|
||||||
|
class Upload extends Model implements HasMedia
|
||||||
|
{
|
||||||
|
use HasFactory, InteractsWithMedia;
|
||||||
|
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -5,11 +5,35 @@ namespace Modules\Upload\Http\Controllers;
|
||||||
use Illuminate\Contracts\Support\Renderable;
|
use Illuminate\Contracts\Support\Renderable;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Controller;
|
use Illuminate\Routing\Controller;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Intervention\Image\Facades\Image;
|
||||||
|
use Modules\Upload\Entities\Upload;
|
||||||
|
|
||||||
class UploadController extends Controller
|
class UploadController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function filepondUpload(Request $request) {
|
public function filepondUpload(Request $request) {
|
||||||
//
|
$request->validate([
|
||||||
|
'image' => 'required|image|mimes:png,jpeg,jpg'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($request->hasFile('image')) {
|
||||||
|
$uploaded_file = $request->file('image');
|
||||||
|
$filename = now()->timestamp . '.' . $uploaded_file->getClientOriginalExtension();
|
||||||
|
$folder = uniqid() . '-' . now()->timestamp;
|
||||||
|
|
||||||
|
$file = Image::make($uploaded_file)->encode($uploaded_file->getClientOriginalExtension());
|
||||||
|
|
||||||
|
Storage::put('public/temp/' . $folder . '/' . $filename, $file);
|
||||||
|
|
||||||
|
Upload::create([
|
||||||
|
'folder' => $folder,
|
||||||
|
'filename' => $filename
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,8 @@
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Route::post('/filepond/upload', 'UploadController@filepondUpload')->name('filepond.upload');
|
Route::group(['middleware' => 'auth'], function () {
|
||||||
|
Route::post('/filepond/upload', 'UploadController@filepondUpload')->name('filepond.upload');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class ProductCategoriesDataTable extends DataTable
|
||||||
*/
|
*/
|
||||||
public function query(Category $model)
|
public function query(Category $model)
|
||||||
{
|
{
|
||||||
return $model->newQuery();
|
return $model->newQuery()->withCount('products');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,6 +70,8 @@ class ProductCategoriesDataTable extends DataTable
|
||||||
->addClass('text-center'),
|
->addClass('text-center'),
|
||||||
Column::make('category_name')
|
Column::make('category_name')
|
||||||
->addClass('text-center'),
|
->addClass('text-center'),
|
||||||
|
Column::make('products_count')
|
||||||
|
->addClass('text-center'),
|
||||||
Column::computed('action')
|
Column::computed('action')
|
||||||
->exportable(false)
|
->exportable(false)
|
||||||
->printable(false)
|
->printable(false)
|
||||||
|
|
|
@ -20,17 +20,15 @@ class ProductDataTable extends DataTable
|
||||||
public function dataTable($query)
|
public function dataTable($query)
|
||||||
{
|
{
|
||||||
return datatables()
|
return datatables()
|
||||||
->eloquent($query)
|
->eloquent($query)->with('category')
|
||||||
->addColumn('action', function ($data) {
|
->addColumn('action', function ($data) {
|
||||||
return view('product::categories.partials.actions', compact('data'));
|
return view('product::products.partials.actions', compact('data'));
|
||||||
})
|
})
|
||||||
->addColumn('product_image', function ($data) {
|
->addColumn('product_image', function ($data) {
|
||||||
$url = asset('storage/product_images/' . $data->product_image);
|
$url = $data->getFirstMediaUrl();
|
||||||
return '<img src='.$url.' border="0" width="40" class="img-rounded" align="center" />';
|
return '<img src="'.$url.'" border="0" width="50" class="img-thumbnail" align="center" />';
|
||||||
})
|
})
|
||||||
->addColumn('category_name', function ($data) {
|
->rawColumns(['product_image']);
|
||||||
return $data->category->category_name;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +40,7 @@ class ProductDataTable extends DataTable
|
||||||
*/
|
*/
|
||||||
public function query(Product $model)
|
public function query(Product $model)
|
||||||
{
|
{
|
||||||
return $model->newQuery();
|
return $model->newQuery()->with('category');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,36 +74,39 @@ class ProductDataTable extends DataTable
|
||||||
return [
|
return [
|
||||||
Column::computed('product_image')
|
Column::computed('product_image')
|
||||||
->title('Image')
|
->title('Image')
|
||||||
->addClass('text-center'),
|
->addClass('text-center')
|
||||||
|
->addClass('align-middle'),
|
||||||
|
|
||||||
Column::make('product_name')
|
Column::make('product_name')
|
||||||
->title('Name')
|
->title('Name')
|
||||||
->addClass('text-center'),
|
->addClass('text-center')
|
||||||
|
->addClass('align-middle'),
|
||||||
|
|
||||||
Column::make('product_code')
|
Column::make('product_code')
|
||||||
->title('Code')
|
->title('Code')
|
||||||
->addClass('text-center'),
|
->addClass('text-center')
|
||||||
|
->addClass('align-middle'),
|
||||||
|
|
||||||
Column::make('product_price')
|
Column::make('product_price')
|
||||||
->title('Price')
|
->title('Price')
|
||||||
->addClass('text-center'),
|
->addClass('text-center')
|
||||||
|
->addClass('align-middle'),
|
||||||
Column::make('product_unit')
|
|
||||||
->title('Unit')
|
|
||||||
->addClass('text-center'),
|
|
||||||
|
|
||||||
Column::make('product_quantity')
|
Column::make('product_quantity')
|
||||||
->title('Quantity')
|
->title('Quantity')
|
||||||
->addClass('text-center'),
|
->addClass('text-center')
|
||||||
|
->addClass('align-middle'),
|
||||||
|
|
||||||
Column::computed('category_name')
|
Column::make('category.category_name')
|
||||||
->title('Category')
|
->title('Category')
|
||||||
->addClass('text-center'),
|
->addClass('text-center')
|
||||||
|
->addClass('align-middle'),
|
||||||
|
|
||||||
Column::computed('action')
|
Column::computed('action')
|
||||||
->exportable(false)
|
->exportable(false)
|
||||||
->printable(false)
|
->printable(false)
|
||||||
->addClass('text-center'),
|
->addClass('text-center')
|
||||||
|
->addClass('align-middle'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
@ -23,6 +24,6 @@ class AppServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function boot()
|
public function boot()
|
||||||
{
|
{
|
||||||
//
|
Model::preventLazyLoading(!app()->isProduction());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'timer' => env('SWEET_ALERT_TIMER', 5000),
|
'timer' => env('SWEET_ALERT_TIMER', 3000),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
@ -34,4 +34,7 @@
|
||||||
@endforeach
|
@endforeach
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
|
<script>
|
||||||
|
window.print();
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -14,5 +14,5 @@ Route::get('/', function () {
|
||||||
|
|
||||||
Auth::routes();
|
Auth::routes();
|
||||||
|
|
||||||
Route::get('/home', 'HomeController@index')->name('home');
|
Route::get('/home', 'HomeController@index')->name('home')->middleware('auth');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue