This commit is contained in:
2025-09-25 03:03:31 +05:00
commit ae480cf2f6
2768 changed files with 1485826 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
{
"name": "nurmuhammet/inventory-history-items",
"description": "A Laravel Nova field.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
"Nurmuhammet\\InventoryHistoryItems\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Nurmuhammet\\InventoryHistoryItems\\FieldServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1 @@
.grid{display:grid}.grid-col-3{grid-template-columns:1fr 1fr 1fr}.grid-col-gap-sm{grid-column-gap:6px}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <http://feross.org>
* @license MIT
*/
/*!
* vuex v4.1.0
* (c) 2022 Evan You
* @license MIT
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */

View File

@@ -0,0 +1,4 @@
{
"/js/field.js": "/js/field.js",
"/css/field.css": "/css/field.css"
}

View File

@@ -0,0 +1,40 @@
const mix = require('laravel-mix')
const webpack = require('webpack')
const path = require('path')
class NovaExtension {
name() {
return 'nova-extension'
}
register(name) {
this.name = name
}
webpackPlugins() {
return new webpack.ProvidePlugin({
_: 'lodash',
Errors: 'form-backend-validation',
})
}
webpackConfig(webpackConfig) {
webpackConfig.externals = {
vue: 'Vue',
}
webpackConfig.resolve.alias = {
...(webpackConfig.resolve.alias || {}),
'laravel-nova': path.join(
__dirname,
'../../vendor/laravel/nova/resources/js/mixins/packages.js'
),
}
webpackConfig.output = {
uniqueName: this.name,
}
}
}
mix.extend('nova', new NovaExtension())

View File

@@ -0,0 +1,22 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production",
"nova:install": "npm --prefix='../../vendor/laravel/nova' ci"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.22",
"form-backend-validation": "^2.3.3",
"laravel-mix": "^6.0.41",
"lodash": "^4.17.21",
"postcss": "^8.3.11",
"vue-loader": "^16.8.3"
},
"dependencies": {}
}

View File

@@ -0,0 +1 @@
module.exports = {}

View File

@@ -0,0 +1,11 @@
.grid {
display: grid;
}
.grid-col-3 {
grid-template-columns: 1fr 1fr 1fr;
}
.grid-col-gap-sm {
grid-column-gap: 6px;
}

View File

@@ -0,0 +1,9 @@
<template>
<PanelItem :index="index" :field="field" />
</template>
<script>
export default {
props: ['index', 'resource', 'resourceName', 'resourceId', 'field'],
}
</script>

View File

@@ -0,0 +1,231 @@
<template>
<DefaultField
:field="field"
:errors="errors"
:show-help-text="showHelpText"
:full-width-content="fullWidthContent"
>
<template #field>
<SearchInput
v-if="!currentlyIsReadonly"
:data-testid="`${field.attribute}-search-input`"
:error="hasError"
:value="selectedOption"
:data="options"
@input="performSearch"
@clear="clearSelection"
@selected="selectOption"
:clearable="field.nullable"
trackBy="value"
class="w-full"
>
<!-- The Selected Option Slot -->
<div v-if="selectedOption" class="flex items-center">
{{ selectedOption.label }}
</div>
<template #option="{ selected, option }">
<!-- Options List Slot -->
<div
class="flex items-center text-sm font-semibold leading-5"
:class="{ 'text-white': selected }"
>
{{ option.label }}
</div>
</template>
</SearchInput>
<div class="w-full grid grid-col-3 grid-col-gap-sm">
<div>
<label for="">Sany: </label>
<input
type="number"
v-model="form.quantity"
placeholder="Sany"
class="w-full form-control form-input form-input-bordered"
>
</div>
<div>
<label for="">Bahasy: </label>
<input
type="text"
v-model="form.price"
placeholder="Bahasy"
class="w-full form-control form-input form-input-bordered"
disabled
>
</div>
<div>
<label for="">Jemi: </label>
<input
type="text"
v-model="form.total"
placeholder="Jemi"
class="w-full form-control form-input form-input-bordered"
dusk="product-item-total"
disabled
>
</div>
</div>
</template>
</DefaultField>
</template>
<script>
import { DependentFormField, HandlesValidationErrors } from 'laravel-nova'
export default {
mixins: [DependentFormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data: () => ({
options: [],
search: '',
selectedOption: null,
form: {
product_id: null,
quantity: 0,
price: 0,
total: 0,
}
}),
mounted() {
},
watch: {
'form.quantity'() {
this.updateProductTotal()
},
selectedOption() {
if (! this.selectedOption || ! this.selectedOption.value) {
return;
}
this.form.product_id = this.selectedOption.value.id;
this.form.price = this.selectedOption.value.cost_amount;
this.updateProductTotal()
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
formData.append('product_id', this.form.product_id)
formData.append('quantity', this.form.quantity)
formData.append('total', this.form.total)
formData.append('cost_amount', this.form.price)
},
/**
* Set the search string to be used to filter the select field.
*/
performSearch(event) {
this.search = event
this.searchProducts()
},
/**
* Clear the current selection for the field.
*/
clearSelection() {
this.selectedOption = null
this.search = ''
this.$refs.searchable.close()
},
/**
* Select the given option.
*/
selectOption(option) {
this.selectedOption = option
this.value = option.value.id
},
handleChange() {
this.$store.commit(`${this.resourceName}/updateFilterState`, {
filterClass: this.filterKey,
value: this.value,
})
this.$emit('change')
},
serializeOptions(options) {
return options.map(item => {
return {
label: item.name,
value: item
}
})
},
updateProductTotal() {
if (this.form.price && this.form.quantity) {
this.form.total = this.form.price * this.form.quantity;
}
setTimeout(() => {
this.updateOveralProductTotal()
}, 200);
},
updateOveralProductTotal() {
let totalElement = document.querySelector('[dusk="readonly_total"]');
if (! totalElement) {
return;
}
let total = 0;
document.querySelectorAll('[dusk="product-item-total"]').forEach(item => {
total += Number(item.value)
})
totalElement.value = total
},
searchProducts() {
if (! this.search && this.search.length < 1) {
return;
}
Nova.$progress.start()
Nova.request().get(`${window.location.origin}/api/v1/search-product?internal=1&q=${encodeURIComponent(this.search)}`, {
headers: {
Accept: 'application/json',
'Api-Token': 'hello-bad-mf-s',
},
}).then(response => {
if (response.status !== 200) {
this.clearSelection();
return;
}
this.options = this.serializeOptions(response.data.data);
})
Nova.$progress.done()
}
},
computed: {},
}
</script>

View File

@@ -0,0 +1,7 @@
import DetailField from './components/DetailField'
import FormField from './components/FormField'
Nova.booting((app, store) => {
app.component('detail-inventory-history-items', DetailField)
app.component('form-inventory-history-items', FormField)
})

View File

@@ -0,0 +1,33 @@
<?php
namespace Nurmuhammet\InventoryHistoryItems;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
class FieldServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Nova::serving(function (ServingNova $event) {
Nova::script('inventory-history-items', __DIR__.'/../dist/js/field.js');
Nova::style('inventory-history-items', __DIR__.'/../dist/css/field.css');
});
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Nurmuhammet\InventoryHistoryItems;
use Illuminate\Support\Collection;
use Laravel\Nova\Fields\Field;
use Laravel\Nova\Fields\SupportsDependentFields;
class InventoryHistoryItems extends Field
{
use SupportsDependentFields;
/**
* The field's component.
*
* @var string
*/
public $component = 'inventory-history-items';
/**
* Set the options for the select menu.
*
* @param array<string|int, array<string, mixed>|string>|\Closure|callable|\Illuminate\Support\Collection $options
* @return $this
*
* @phpstan-param TOption|(callable(): (TOption))|(\Closure(): (TOption)) $options
*/
public function options(array|Collection $options): self
{
return $this->withMeta([
'options' => $this->serializeOptions($options),
]);
}
/**
* Serialize Options
*
* @param Collection $options
*/
public function serializeOptions(array|Collection $options): array|Collection
{
return collect($options)->map(fn ($label, $value) => ['label' => $label, 'value' => $value]);
}
}

View File

@@ -0,0 +1,10 @@
let mix = require('laravel-mix')
require('./nova.mix')
mix
.setPublicPath('dist')
.js('resources/js/field.js', 'js')
.vue({ version: 3 })
.css('resources/css/field.css', 'css')
.nova('nurmuhammet/inventory-history-items')