partial update transaction work with stock product

This commit is contained in:
2025-06-24 19:42:19 +07:00
parent 33502e905d
commit c3233ea6b2
20 changed files with 3432 additions and 239 deletions

View File

@@ -0,0 +1,531 @@
@extends('layouts.backapp')
@section('content')
<div class="kt-portlet kt-portlet--mobile" id="kt_blockui_datatable">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<span class="kt-portlet__head-icon">
<i class="kt-font-brand flaticon2-box"></i>
</span>
<h3 class="kt-portlet__head-title">
Kelola Produk untuk Pekerjaan: {{ $work->name }}
</h3>
</div>
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-wrapper">
<div class="kt-portlet__head-actions">
<a href="{{ route('work.index') }}" class="btn btn-secondary btn-sm mr-2">
<i class="fa fa-arrow-left"></i> Kembali
</a>
<button type="button" class="btn btn-info btn-sm mr-2" id="showStockPrediction">
<i class="fa fa-chart-line"></i> Prediksi Stock
</button>
@can('create', $menus['work.index'] ?? null)
<button type="button" class="btn btn-bold btn-label-brand btn-sm" id="addWorkProduct">
<i class="fa fa-plus"></i> Tambah Produk
</button>
@endcan
</div>
</div>
</div>
</div>
<div class="kt-portlet__body">
<!-- Work Info -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
<i class="fa fa-info-circle mr-2"></i>
Informasi Pekerjaan
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="work-info-item mb-3">
<label class="font-weight-bold text-muted mb-1">Nama Pekerjaan:</label>
<div class="work-info-value">{{ $work->name }}</div>
</div>
<div class="work-info-item mb-3">
<label class="font-weight-bold text-muted mb-1">Short Name:</label>
<div class="work-info-value">{{ $work->shortname }}</div>
</div>
</div>
<div class="col-md-6">
<div class="work-info-item mb-3">
<label class="font-weight-bold text-muted mb-1">Kategori:</label>
<div class="work-info-value">
@if($work->category)
<span class="badge badge-info">{{ $work->category->name }}</span>
@else
<span class="text-muted">-</span>
@endif
</div>
</div>
<div class="work-info-item mb-3">
<label class="font-weight-bold text-muted mb-1">Deskripsi:</label>
<div class="work-info-value">
@if($work->desc)
{{ $work->desc }}
@else
<span class="text-muted">Tidak ada deskripsi</span>
@endif
</div>
</div>
</div>
</div>
</div>
</div>
<div class="table-responsive">
<!--begin: Datatable -->
<table class="table table-striped table-bordered table-hover table-checkable" id="workProductsTable">
<thead>
<tr>
<th>Kode Produk</th>
<th>Nama Produk</th>
<th>Kategori</th>
<th>Satuan</th>
<th>Qty Diperlukan</th>
<th>Catatan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<!--end: Datatable -->
</div>
</div>
</div>
<!--begin::Modal-->
<div class="modal fade" id="workProductModal" tabindex="-1" role="dialog" aria-labelledby="workProductModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<form id="workProductForm" class="kt-form">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalHeading">
<i class="fa fa-plus mr-2"></i>
Tambah Produk
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<input type="hidden" id="work_id" name="work_id" value="{{ $work->id }}">
<input type="hidden" id="work_product_id" name="work_product_id">
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="product_id" class="font-weight-bold">
<i class="fa fa-box mr-1"></i>
Pilih Produk <span class="text-danger">*</span>
</label>
<select name="product_id" id="product_id" class="form-control select2" required>
<option value="">-- Pilih Produk --</option>
@foreach ($products as $product)
<option value="{{ $product->id }}" data-code="{{ $product->code }}" data-unit="{{ $product->unit }}">
{{ $product->code }} - {{ $product->name }} ({{ $product->unit }})
</option>
@endforeach
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="quantity_required" class="font-weight-bold">
<i class="fa fa-calculator mr-1"></i>
Jumlah Diperlukan <span class="text-danger">*</span>
</label>
<input type="number" class="form-control" id="quantity_required" name="quantity_required"
placeholder="0.00" step="0.01" min="0.01" required>
</div>
</div>
</div>
<div class="form-group">
<label for="notes" class="font-weight-bold">
<i class="fa fa-sticky-note mr-1"></i>
Catatan
</label>
<textarea name="notes" id="notes" rows="3" class="form-control"
placeholder="Catatan atau keterangan tambahan (opsional)"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Batal
</button>
<button type="submit" class="btn btn-primary" id="saveBtn">
Simpan
</button>
</div>
</div>
</form>
</div>
</div>
<!--end::Modal-->
<!-- Stock Prediction Modal -->
<div class="modal fade" id="stockPredictionModal" tabindex="-1" role="dialog" aria-labelledby="stockPredictionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Prediksi Penggunaan Stock</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="prediction_quantity">Jumlah Pekerjaan:</label>
<div class="input-group">
<input type="number" class="form-control" id="prediction_quantity" value="1" min="1">
<div class="input-group-append">
<button type="button" class="btn btn-primary" id="applyPrediction">
Terapkan
</button>
</div>
</div>
</div>
<div id="stockPredictionContent">
<!-- Content will be loaded via AJAX -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
<!-- Hidden inputs for JavaScript -->
<input type="hidden" name="work_id" value="{{ $work->id }}">
<input type="hidden" name="index_url" value="{{ route('work.products.index', $work->id) }}">
<input type="hidden" name="store_url" value="{{ route('work.products.store', $work->id) }}">
<input type="hidden" name="stock_prediction_url" value="{{ route('work.products.stock-prediction', $work->id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="base_url" value="{{ route('work.products.index', $work->id) }}">
<input type="hidden" name="show_url_template" value="{{ route('work.products.show', ['work' => $work->id, 'workProduct' => ':id']) }}">
<input type="hidden" name="update_url_template" value="{{ route('work.products.update', ['work' => $work->id, 'workProduct' => ':id']) }}">
<input type="hidden" name="destroy_url_template" value="{{ route('work.products.destroy', ['work' => $work->id, 'workProduct' => ':id']) }}">
@endsection
@section('javascripts')
<script src="{{ url('js/pages/back/master/work-products.js') }}" type="text/javascript"></script>
@endsection
@section('styles')
<style>
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.modal .close {
cursor: pointer;
opacity: 0.7;
}
.modal .close:hover {
opacity: 1;
}
.input-group-append .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
/* Action button flex layout */
.d-flex.flex-row.gap-1 {
display: flex !important;
flex-direction: row !important;
gap: 0.25rem !important;
align-items: center;
flex-wrap: nowrap;
}
.d-flex.flex-row.gap-1 .btn {
white-space: nowrap;
margin: 0;
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
}
.d-flex.flex-row.gap-1 .btn i {
margin-right: 0.25rem;
}
/* Work Info Styling */
.work-info-item {
border-bottom: 1px solid #f0f0f0;
padding-bottom: 0.75rem;
}
.work-info-item:last-child {
border-bottom: none;
padding-bottom: 0;
}
.work-info-item label {
font-size: 0.875rem;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.work-info-value {
font-size: 1rem;
color: #495057;
font-weight: 500;
word-wrap: break-word;
}
.card-header.bg-primary {
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%) !important;
border: none;
}
.card-header.bg-primary h5 {
font-weight: 600;
}
.badge.badge-info {
background-color: #17a2b8;
color: white;
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
border-radius: 0.25rem;
}
/* Ensure DataTables doesn't break flex layout */
.dataTables_wrapper .dataTables_scrollBody .d-flex {
display: flex !important;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.d-flex.flex-row.gap-1 {
flex-direction: column !important;
gap: 0.125rem !important;
}
.d-flex.flex-row.gap-1 .btn {
width: 100%;
margin-bottom: 0.125rem;
}
.work-info-item {
margin-bottom: 1rem !important;
}
.card-body .row .col-md-6:first-child {
margin-bottom: 1rem;
}
}
/* Modal and Form Styling */
.modal-header {
border-bottom: 1px solid #dee2e6;
background-color: #f8f9fa;
}
.modal-header h5 {
font-weight: 600;
color: #495057;
}
.form-group label {
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.form-group label i {
color: #6c757d;
}
.form-control {
border-radius: 0.375rem;
border: 1px solid #ced4da;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.btn {
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.15s ease-in-out;
}
.btn i {
font-size: 0.875rem;
}
/* Required field indicator */
.text-danger {
color: #dc3545 !important;
}
/* Placeholder styling */
.form-control::placeholder {
color: #6c757d;
opacity: 0.7;
}
/* Select2 Styling */
.select2-container--default .select2-selection--single {
border: 1px solid #ced4da;
border-radius: 0.375rem;
height: 38px;
line-height: 36px;
background-color: #fff;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 36px;
padding-left: 12px;
padding-right: 30px;
color: #495057;
font-size: 14px;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #6c757d;
opacity: 0.7;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: calc(1.5em + 0.75rem);
top: 0;
}
.select2-container--default .select2-selection--single .select2-selection__clear {
color: #6c757d;
margin-right: 25px;
font-weight: bold;
}
.select2-container--default .select2-selection--single .select2-selection__clear:hover {
color: #dc3545;
}
.select2-container--default.select2-container--focus .select2-selection--single {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.select2-dropdown {
border: 1px solid #ced4da;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
background-color: #fff;
}
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #ced4da;
border-radius: 0.25rem;
padding: 8px 12px;
font-size: 14px;
}
.select2-container--default .select2-search--dropdown .select2-search__field:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
outline: none;
}
.select2-container--default .select2-results__option {
padding: 8px 12px;
font-size: 14px;
color: #495057;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #007bff;
color: #fff;
}
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #e9ecef;
color: #495057;
}
.select2-container--default .select2-results__option[aria-selected=true]:hover {
background-color: #007bff;
color: #fff;
}
.select2-container {
width: 100% !important;
}
/* Ensure Select2 works properly in modal */
.modal .select2-container {
z-index: 9999;
}
.modal .select2-dropdown {
z-index: 9999;
}
/* Fix Select2 height to match form controls */
.select2-container--default .select2-selection--single {
height: calc(1.5em + 0.75rem + 2px);
line-height: 1.5;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 1.5;
padding-top: 0.375rem;
padding-bottom: 0.375rem;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: calc(1.5em + 0.75rem);
top: 0;
}
/* SweetAlert Icon Centering */
.swal2-icon {
display: flex !important;
align-items: center !important;
justify-content: center !important;
margin: 0 auto !important;
}
.swal2-icon .swal2-icon-content {
display: flex !important;
align-items: center !important;
justify-content: center !important;
width: 100% !important;
height: 100% !important;
}
.swal2-popup {
text-align: center !important;
}
.swal2-title {
text-align: center !important;
}
.swal2-content {
text-align: center !important;
}
.swal2-actions {
justify-content: center !important;
}
</style>
@endsection

View File

@@ -6,6 +6,243 @@
#nama{
text-transform: uppercase;
}
/* Action button flex layout */
.d-flex.flex-row.gap-1 {
display: flex !important;
flex-direction: row !important;
gap: 0.25rem !important;
align-items: center;
flex-wrap: nowrap;
}
.d-flex.flex-row.gap-1 .btn {
white-space: nowrap;
margin: 0;
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
}
.d-flex.flex-row.gap-1 .btn i {
margin-right: 0.25rem;
}
/* Ensure DataTables doesn't break flex layout */
.dataTables_wrapper .dataTables_scrollBody .d-flex {
display: flex !important;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.d-flex.flex-row.gap-1 {
flex-direction: column !important;
gap: 0.125rem !important;
}
.d-flex.flex-row.gap-1 .btn {
width: 100%;
margin-bottom: 0.125rem;
}
}
/* Modal and Form Styling */
.modal-header {
border-bottom: 1px solid #dee2e6;
background-color: #f8f9fa;
}
.modal-header h5 {
font-weight: 600;
color: #495057;
}
.form-group label {
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.form-group label i {
color: #6c757d;
}
.form-control {
border-radius: 0.375rem;
border: 1px solid #ced4da;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.btn {
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.15s ease-in-out;
}
.btn i {
font-size: 0.875rem;
}
/* Required field indicator */
.text-danger {
color: #dc3545 !important;
}
/* Placeholder styling */
.form-control::placeholder {
color: #6c757d;
opacity: 0.7;
}
/* Select2 Styling */
.select2-container--default .select2-selection--single {
border: 1px solid #ced4da;
border-radius: 0.375rem;
height: 38px;
line-height: 36px;
background-color: #fff;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 36px;
padding-left: 12px;
padding-right: 30px;
color: #495057;
font-size: 14px;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #6c757d;
opacity: 0.7;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 36px;
width: 30px;
}
.select2-container--default .select2-selection--single .select2-selection__clear {
color: #6c757d;
margin-right: 25px;
font-weight: bold;
}
.select2-container--default .select2-selection--single .select2-selection__clear:hover {
color: #dc3545;
}
.select2-container--default.select2-container--focus .select2-selection--single {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.select2-dropdown {
border: 1px solid #ced4da;
border-radius: 0.375rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
background-color: #fff;
}
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #ced4da;
border-radius: 0.25rem;
padding: 8px 12px;
font-size: 14px;
}
.select2-container--default .select2-search--dropdown .select2-search__field:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
outline: none;
}
.select2-container--default .select2-results__option {
padding: 8px 12px;
font-size: 14px;
color: #495057;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #007bff;
color: #fff;
}
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #e9ecef;
color: #495057;
}
.select2-container--default .select2-results__option[aria-selected=true]:hover {
background-color: #007bff;
color: #fff;
}
.select2-container {
width: 100% !important;
}
/* Ensure Select2 works properly in modal */
.modal .select2-container {
z-index: 9999;
}
.modal .select2-dropdown {
z-index: 9999;
}
/* Fix Select2 height to match form controls */
.select2-container--default .select2-selection--single {
height: calc(1.5em + 0.75rem + 2px);
line-height: 1.5;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 1.5;
padding-top: 0.375rem;
padding-bottom: 0.375rem;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: calc(1.5em + 0.75rem);
top: 0;
}
/* SweetAlert Icon Centering */
.swal2-icon {
display: flex !important;
align-items: center !important;
justify-content: center !important;
margin: 0 auto !important;
}
.swal2-icon .swal2-icon-content {
display: flex !important;
align-items: center !important;
justify-content: center !important;
width: 100% !important;
height: 100% !important;
}
.swal2-popup {
text-align: center !important;
}
.swal2-title {
text-align: center !important;
}
.swal2-content {
text-align: center !important;
}
.swal2-actions {
justify-content: center !important;
}
</style>
<div class="kt-portlet kt-portlet--mobile" id="kt_blockui_datatable">
@@ -22,7 +259,7 @@
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-wrapper">
<div class="kt-portlet__head-actions">
<button type="button" class="btn btn-bold btn-label-brand btn-sm" id="addWork"> Tambah </button>
<button type="button" class="btn btn-bold btn-label-brand" id="addWork"> Tambah </button>
</div>
</div>
</div>
@@ -52,41 +289,73 @@
<!--begin::Modal-->
<div class="modal fade" id="workModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-dialog modal-lg" role="document">
<form id="workForm" class="kt-form">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalHeading"></h5>
<h5 class="modal-title" id="modalHeading">
<i class="fa fa-plus mr-2"></i>
Tambah Pekerjaan
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="kt-portlet__body">
<div class="form-group">
<label>Nama Pekerjaan</label>
<input type="text" class="form-control inputUppercase" id="name" name="name" placeholder="Masukan Nama Pekerjaan" value="" required="" autocomplete="off" />
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="name" class="font-weight-bold">
<i class="fa fa-tag mr-1"></i>
Nama Pekerjaan <span class="text-danger">*</span>
</label>
<input type="text" class="form-control inputUppercase" id="name" name="name"
placeholder="Masukan Nama Pekerjaan" value="" required="" autocomplete="off" />
</div>
</div>
<div class="form-group">
<label>Short Name</label>
<input type="text" class="form-control inputUppercase" id="shortname" name="shortname" placeholder="Masukan Short Name" value="" required="" autocomplete="off" />
<div class="col-md-6">
<div class="form-group">
<label for="shortname" class="font-weight-bold">
<i class="fa fa-code mr-1"></i>
Short Name <span class="text-danger">*</span>
</label>
<input type="text" class="form-control inputUppercase" id="shortname" name="shortname"
placeholder="Masukan Short Name" value="" required="" autocomplete="off" />
</div>
</div>
<div class="form-group">
<label for="category_id">Kategori</label>
<select name="category_id" id="category_id" class="form-control">
@foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->name }}</option>
@endforeach
</select>
</div>
<div class="form-group">
<label for="desc">Deskripsi Pekerjaan</label>
<textarea name="desc" id="desc" rows="5" class="form-control"></textarea>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="category_id" class="font-weight-bold">
<i class="fa fa-folder mr-1"></i>
Kategori <span class="text-danger">*</span>
</label>
<select name="category_id" id="category_id" class="form-control select2" required>
<option value="">-- Pilih Kategori --</option>
@foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->name }}</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="form-group">
<label for="desc" class="font-weight-bold">
<i class="fa fa-align-left mr-1"></i>
Deskripsi Pekerjaan
</label>
<textarea name="desc" id="desc" rows="4" class="form-control"
placeholder="Masukan deskripsi pekerjaan (opsional)"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary" id="saveBtn" value="create">Simpan</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Batal
</button>
<button type="submit" class="btn btn-primary" id="saveBtn" value="create">
Simpan
</button>
</div>
</div>
</form>