49 changed files with 1143 additions and 247 deletions
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Academic\Queries\Selection; |
|||
|
|||
use App\Domains\Academic\Models\Faculty; |
|||
use Illuminate\Support\Collection; |
|||
|
|||
class SelectFacultyQuery |
|||
{ |
|||
public static function fetch(): Collection |
|||
{ |
|||
return once(fn() => Faculty::all(['id', 'name']) |
|||
->pluck('name', 'id')); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Academic\Queries\Selection; |
|||
|
|||
use App\Domains\Academic\Models\StudyProgram; |
|||
use Illuminate\Support\Collection; |
|||
|
|||
class SelectStudyProgramQuery |
|||
{ |
|||
public static function fetch(array $context = []): Collection |
|||
{ |
|||
if(!isset($context['faculty'])) { |
|||
return new Collection(); |
|||
} |
|||
|
|||
return once(fn() => StudyProgram::select('id', 'name') |
|||
->where('faculty_id', $context['faculty']) |
|||
->get() |
|||
->pluck('name', 'id')); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Academic\Queries\Selection; |
|||
|
|||
use App\Domains\Academic\Models\Term; |
|||
use Illuminate\Support\Collection; |
|||
|
|||
class SelectTermQuery |
|||
{ |
|||
public static function fetch(): Collection |
|||
{ |
|||
return once(fn() => Term::all(['id', 'name']) |
|||
->pluck('name', 'id')); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Admission\Queries\Selection; |
|||
|
|||
use Illuminate\Database\Eloquent\Collection; |
|||
|
|||
class SelectFeeTypeQuery |
|||
{ |
|||
public function fetch(): Collection |
|||
{ |
|||
return new Collection(); |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Finance\Actions\ProductConfiguration; |
|||
|
|||
class CreateProductMapping |
|||
{ |
|||
public function execute(): void |
|||
{ |
|||
// |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Finance\Actions\ProductConfiguration; |
|||
|
|||
use App\Domains\Finance\Models\ProductMapping; |
|||
|
|||
class ModifyProductMapping |
|||
{ |
|||
public function execute(ProductMapping $productMapping, string|int $coaId): void |
|||
{ |
|||
$productMapping->update(['chart_of_account_id' => $coaId]); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Finance\DTOs\PaymentProcessing\Detail; |
|||
|
|||
interface InvoiceDetailDTO |
|||
{ |
|||
public function toArray(): array; |
|||
|
|||
public static function fromArray(array $data): self; |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Finance\DTOs\PaymentProcessing\Detail; |
|||
|
|||
class StudentInvoiceDetailDTO implements InvoiceDetailDTO |
|||
{ |
|||
public function __construct( |
|||
public string $studentId, |
|||
public string $studentName, |
|||
public string $faculty, |
|||
public string $studyProgram, |
|||
public string $term, |
|||
) {} |
|||
|
|||
public function toArray(): array |
|||
{ |
|||
return [ |
|||
'student_id' => $this->studentId, |
|||
'student_name' => $this->studentName, |
|||
'faculty' => $this->faculty, |
|||
'study_program' => $this->studyProgram, |
|||
'term' => $this->term, |
|||
]; |
|||
} |
|||
|
|||
public static function fromArray(array $data): self |
|||
{ |
|||
return new self( |
|||
studentId: $data['student_id'], |
|||
studentName: $data['student_name'], |
|||
faculty: $data['faculty'], |
|||
studyProgram: $data['study_program'], |
|||
term: $data['term'], |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Finance\DTOs\ProductConfiguration; |
|||
|
|||
readonly class ModifyProductMappingDTO |
|||
{ |
|||
public function __construct( |
|||
// |
|||
) {} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Finance\Schemas\Invoice; |
|||
|
|||
use App\Domains\Academic\Queries\Selection\SelectFacultyQuery; |
|||
use App\Domains\Academic\Queries\Selection\SelectStudyProgramQuery; |
|||
use App\Domains\Academic\Queries\Selection\SelectTermQuery; |
|||
use App\Domains\Finance\Support\Schema\InvoiceSchema; |
|||
use App\UI\Enums\InputType; |
|||
use App\UI\Support\Settings\SettingSchema; |
|||
|
|||
class StudentInvoiceSchema |
|||
{ |
|||
public static function make(): InvoiceSchema |
|||
{ |
|||
return InvoiceSchema::make() |
|||
->addSchema( |
|||
key: 'student_id', |
|||
label: __('domains/finance/field.invoice.detail.student_id'), |
|||
schema: SettingSchema::make(InputType::TEXTLINE) |
|||
->rules(['required']) |
|||
)->addSchema( |
|||
key: 'student_name', |
|||
label: __('domains/finance/field.invoice.detail.student_name'), |
|||
schema: SettingSchema::make(InputType::TEXTLINE) |
|||
->rules(['required', 'string']) |
|||
)->addSchema( |
|||
key: 'faculty', |
|||
label: __('domains/finance/field.invoice.detail.faculty'), |
|||
schema: SettingSchema::make(InputType::SELECT) |
|||
->rules(['required', 'string']) |
|||
->select2() |
|||
->live() |
|||
->options(fn() => SelectFacultyQuery::fetch()) |
|||
)->addSchema( |
|||
key: 'study_program', |
|||
label: __('domains/finance/field.invoice.detail.study_program'), |
|||
schema: SettingSchema::make(InputType::SELECT) |
|||
->rules(['required', 'string']) |
|||
->select2() |
|||
->dependsOn('faculty') |
|||
->setContextKey('faculty') |
|||
->options(fn ($context) => SelectStudyProgramQuery::fetch($context)) |
|||
)->addSchema( |
|||
key: 'term', |
|||
label: __('domains/finance/field.invoice.detail.term'), |
|||
schema: SettingSchema::make(InputType::SELECT) |
|||
->rules(['required']) |
|||
->options(fn() => SelectTermQuery::fetch()) |
|||
->select2() |
|||
)->addSchema( |
|||
key: 'semester', |
|||
label: __('domains/finance/field.invoice.detail.semester'), |
|||
schema: SettingSchema::make(InputType::SELECT) |
|||
->rules(['required']) |
|||
->options(range(1, 13)) |
|||
->select2() |
|||
)->addSchema( |
|||
key: 'fee_type', |
|||
label: __('domains/finance/field.invoice.detail.fee_type'), |
|||
schema: SettingSchema::make(InputType::SELECT) |
|||
->rules(['required']) |
|||
->select2() |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
<?php |
|||
|
|||
namespace App\Domains\Finance\Support\Schema; |
|||
|
|||
use App\UI\Enums\InputType; |
|||
use App\UI\Support\Settings\BaseSchema; |
|||
use App\UI\Support\Settings\SettingSchema; |
|||
|
|||
class InvoiceSchema implements BaseSchema |
|||
{ |
|||
protected array $context = []; |
|||
protected string $form = ''; |
|||
|
|||
public function __construct( |
|||
protected array $uiSchema = [], |
|||
protected bool $coaVisibility = false |
|||
){} |
|||
|
|||
public static function make(bool $coaVisible = false): static |
|||
{ |
|||
return new self([], $coaVisible); |
|||
} |
|||
|
|||
public function addSchema(string$key, string $label, SettingSchema $schema): static |
|||
{ |
|||
$schema = $schema->attributes(['label' => $label]); |
|||
$uiSchema = array_merge($this->uiSchema, [[ |
|||
'key' => $key, |
|||
'schema' => $schema |
|||
]]); |
|||
return new self($uiSchema, $this->coaVisibility); |
|||
} |
|||
|
|||
public function withContext(array $context): static |
|||
{ |
|||
$this->context = $context; |
|||
return $this; |
|||
} |
|||
|
|||
public function withFormString(string $form): static |
|||
{ |
|||
$this->form = $form; |
|||
return $this; |
|||
} |
|||
|
|||
public function getSchema(): array |
|||
{ |
|||
$fields = []; |
|||
foreach($this->uiSchema as $field) { |
|||
/** @var SettingSchema $schema */ |
|||
$schema = $field['schema']; |
|||
$attributes['options'] =$schema->type->isSelect() ? $schema->resolveOptions($this->context) : ''; |
|||
|
|||
$fields[$field['key']] = (object) [ |
|||
'attributes' => array_merge($schema->attributes, $attributes), |
|||
'schema' => $schema, |
|||
]; |
|||
} |
|||
|
|||
return $fields; |
|||
} |
|||
|
|||
public function isCoaVisible(): bool |
|||
{ |
|||
return $this->coaVisibility; |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<?php |
|||
|
|||
namespace App\Livewire\Forms\Finance; |
|||
|
|||
use App\Domains\Finance\Enums\AccountClassification; |
|||
use App\Domains\System\Enums\LifecycleStatus; |
|||
use Livewire\Attributes\Validate; |
|||
use Livewire\Form; |
|||
|
|||
class ChartOfAccountForm extends Form |
|||
{ |
|||
#[Validate('required|string|max:255', as: 'domains/finance/field.coa.code')] |
|||
public string $code = ''; |
|||
|
|||
#[Validate('required|string|max:255', as: 'domains/finance/field.coa.name')] |
|||
public string $name = ''; |
|||
|
|||
#[Validate('required', as: 'domains/finance/field.coa.classification')] |
|||
public AccountClassification $classification = AccountClassification::REVENUE; |
|||
|
|||
#[Validate(as: 'domains/finance/field.coa.parent')] |
|||
public ?string $parent_id = null; |
|||
|
|||
#[Validate('required', as: 'domains/finance/field.coa.status')] |
|||
public LifecycleStatus $status = LifecycleStatus::ACTIVE; |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
<?php |
|||
|
|||
namespace App\Livewire\Forms\Finance; |
|||
|
|||
use App\Domains\Finance\DTOs\PaymentProcessing\IssueInvoiceDTO; |
|||
use App\Domains\Finance\Enums\InvoiceStatus; |
|||
use App\Domains\Finance\Enums\InvoiceType; |
|||
use App\UI\Support\Settings\SettingSchema; |
|||
use Livewire\Attributes\Validate; |
|||
use Livewire\Form; |
|||
|
|||
class InvoiceForm extends Form |
|||
{ |
|||
#[Validate('required|string|max:255', as: 'domains/finance/field.invoice.name')] |
|||
public string $name = ''; |
|||
|
|||
#[Validate('required', as: 'domains/finance/field.invoice.type')] |
|||
public ?InvoiceType $type = null; |
|||
|
|||
#[Validate] |
|||
public array $detail = []; |
|||
|
|||
#[Validate('required', as: 'domains/finance/field.invoice.type')] |
|||
public string $client_id = ''; |
|||
|
|||
#[Validate('required', as: 'domains/finance/field.invoice.type')] |
|||
public string $coa_id = ''; |
|||
|
|||
#[Validate('required', as: 'domains/finance/field.invoice.type')] |
|||
public int $amount = 0; |
|||
|
|||
public function updatedDetail($val, $prop): void |
|||
{ |
|||
$formSchema = $this->type->schema()->getSchema(); |
|||
|
|||
$this->resetValidation(); |
|||
foreach ($formSchema as $key => $schema) { |
|||
/** @var SettingSchema $schema */ |
|||
$schema = $schema->schema; |
|||
if ($schema->depends == $prop) { |
|||
$this->detail[$key] = ''; |
|||
} |
|||
} |
|||
$this->validateOnly($prop); |
|||
} |
|||
|
|||
public function updatedType(): void |
|||
{ |
|||
$this->reset('detail'); |
|||
|
|||
$schema = $this->type?->schema()?->getSchema() ?? []; |
|||
foreach ($schema as $key => $value) { |
|||
$this->detail[$key] = ''; |
|||
} |
|||
|
|||
$this->resetValidation(); |
|||
} |
|||
|
|||
public function rules(): array |
|||
{ |
|||
$rules = []; |
|||
$detailSchema = $this->type |
|||
?->schema() |
|||
->getSchema() ?? []; |
|||
foreach ($detailSchema as $key => $schema) { |
|||
$rules = array_merge($rules, [ |
|||
'detail.'.$key => $schema->schema->rules, |
|||
]); |
|||
} |
|||
|
|||
return $rules; |
|||
} |
|||
|
|||
public function validationAttributes(): array |
|||
{ |
|||
$attributes = []; |
|||
$detailSchema = $this->type |
|||
?->schema() |
|||
->getSchema() ?? []; |
|||
foreach ($detailSchema as $key => $schema) { |
|||
$attributes = array_merge($attributes, [ |
|||
'detail.'.$key => $schema->schema->attributes['label'], |
|||
]); |
|||
} |
|||
|
|||
return $attributes; |
|||
} |
|||
|
|||
public function toDto(): IssueInvoiceDTO |
|||
{ |
|||
|
|||
return new IssueInvoiceDTO( |
|||
name: $this->name, |
|||
clientRefId: $this->client_id, |
|||
detail: $this->type->dtoTransform($this->detail), |
|||
type: $this->type, |
|||
status: InvoiceStatus::PENDING, |
|||
clientId: $this->client_id |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
<?php |
|||
|
|||
namespace App\Livewire\Forms\Finance; |
|||
|
|||
use Livewire\Attributes\Validate; |
|||
use Livewire\Form; |
|||
|
|||
class PaymentForm extends Form |
|||
{ |
|||
// |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
<?php |
|||
|
|||
namespace App\Livewire\Forms\Finance; |
|||
|
|||
use Livewire\Attributes\Validate; |
|||
use Livewire\Form; |
|||
|
|||
class ProductMappingForm extends Form |
|||
{ |
|||
#[Validate('required|string', as: 'domains/finance/field.product-mapping.code')] |
|||
public string $code = ''; |
|||
|
|||
#[Validate('required|string', as: 'domains/finance/field.product-mapping.name')] |
|||
public string $name = ''; |
|||
|
|||
#[Validate('required|string', as: 'domains/finance/field.product-mapping.chart_of_account')] |
|||
public string $coa_id = ''; |
|||
|
|||
// #[Validate('required|string', as: 'domains/finance/field.product-mapping.client')] |
|||
// public string $client = ''; |
|||
} |
|||
@ -1,77 +1,140 @@ |
|||
|
|||
export default function alpineSelect2(Alpine) { |
|||
Alpine.directive('select2', function (el, { expression }, { evaluate, cleanup }) { |
|||
let config = evaluate(expression); |
|||
|
|||
if((window.jQuery||window.jquery||window.$) && (typeof window.$.fn.select2 !== 'undefined')) { |
|||
let select2Default = { |
|||
placeholder: config.placeholder || 'Select an option', |
|||
ajax: config.url ? { |
|||
url: config.url, |
|||
dataType: 'json', |
|||
delay: 250, |
|||
xhrFields: { |
|||
withCredentials: true |
|||
}, |
|||
data: function (params) { |
|||
return { |
|||
search: params.terms |
|||
} |
|||
}, |
|||
processResults: function (data) { |
|||
return { |
|||
results: data.data || data |
|||
} |
|||
Alpine.directive( |
|||
"select2", |
|||
function (el, { expression }, { evaluate, cleanup }) { |
|||
let config = evaluate(expression) || {}; |
|||
|
|||
if ( |
|||
(window.jQuery || window.jquery || window.$) && |
|||
typeof window.$.fn.select2 !== "undefined" |
|||
) { |
|||
let modalContainer = el.closest(".modal"); |
|||
let select2Default = { |
|||
placeholder: config.placeholder || "Select an option", |
|||
dropdownParent: config.dropdownParent || modalContainer || el.parentElement, |
|||
allowClear: config.allowClear ?? true, |
|||
ajax: config.url |
|||
? { |
|||
url: config.url, |
|||
dataType: "json", |
|||
delay: 250, |
|||
xhrFields: { |
|||
withCredentials: true, |
|||
}, |
|||
data: function (params) { |
|||
return { |
|||
search: params.terms, |
|||
}; |
|||
}, |
|||
processResults: function (data) { |
|||
return { |
|||
results: data.data || data, |
|||
}; |
|||
}, |
|||
} |
|||
: undefined, |
|||
}; |
|||
|
|||
const container = document.createElement("div"); |
|||
el.parentElement.insertBefore(container, el); |
|||
|
|||
// Keep wire:ignore on container so Livewire DOM diffing does not destroy Select2 UI
|
|||
container.setAttribute("wire:ignore", ""); |
|||
container.appendChild(el); |
|||
|
|||
config = Object.assign(select2Default, config); |
|||
let $el = $(el).select2(config); |
|||
|
|||
const $select2Container = $el.next(".select2-container"); |
|||
|
|||
const modelName = |
|||
el.getAttribute("wire:model") || |
|||
el.getAttribute("wire:model.live") || |
|||
el.getAttribute("wire:model.blur") || |
|||
"select2"; |
|||
|
|||
let isUpdating = false; |
|||
|
|||
$el.on("change", (e) => { |
|||
if (isUpdating) return; |
|||
isUpdating = true; |
|||
el.dispatchEvent(new Event("change", { bubbles: true })); |
|||
isUpdating = false; |
|||
}); |
|||
|
|||
// Fix for allowClear: prevent dropdown from opening when clicking the 'x'
|
|||
$el.on("select2:unselecting", function (e) { |
|||
$(this).data("unselecting", true); |
|||
}).on("select2:opening", function (e) { |
|||
if ($(this).data("unselecting")) { |
|||
$(this).removeData("unselecting"); |
|||
e.preventDefault(); |
|||
} |
|||
} : undefined |
|||
} |
|||
}); |
|||
|
|||
config = Object.assign(config, select2Default) |
|||
let $el = $(el).select2(config) |
|||
|
|||
const modelName = el.getAttribute('wire:model') || |
|||
el.getAttribute('wire:model.live') || |
|||
el.getAttribute('wire:model.blur') || 'select2-clear'; |
|||
|
|||
let isUpdating = false |
|||
|
|||
$el.on('change', (e) => { |
|||
if(isUpdating) return |
|||
isUpdating = true |
|||
el.dispatchEvent(new Event('change', {bubbles: true})) |
|||
isUpdating = false |
|||
}) |
|||
|
|||
// Fix for allowClear: prevent dropdown from opening when clicking the 'x'
|
|||
$el.on('select2:unselecting', function (e) { |
|||
$(this).data('unselecting', true); |
|||
}).on('select2:opening', function (e) { |
|||
if ($(this).data('unselecting')) { |
|||
$(this).removeData('unselecting'); |
|||
e.preventDefault(); |
|||
} |
|||
}); |
|||
const event = (e) => { |
|||
$el.val(null).trigger("change", { bubbles: true }); |
|||
}; |
|||
|
|||
const event = (e) => { |
|||
$el.val(null).trigger('change', {bubbles: true}) |
|||
} |
|||
window.addEventListener(`${modelName}-clear`, event); |
|||
|
|||
// Sync error state (is-invalid) from native select to Select2 container
|
|||
const syncErrorState = () => { |
|||
if (el.classList.contains("is-invalid")) { |
|||
container.classList.add("is-invalid"); |
|||
$select2Container |
|||
.find(".select2-selection") |
|||
.addClass("is-invalid border-danger"); |
|||
} else { |
|||
container.classList.remove("is-invalid"); |
|||
$select2Container |
|||
.find(".select2-selection") |
|||
.removeClass("is-invalid border-danger"); |
|||
} |
|||
}; |
|||
|
|||
// Run initial check on load
|
|||
syncErrorState(); |
|||
|
|||
// Observe attribute changes on the native <select> element
|
|||
const observer = new MutationObserver((mutations) => { |
|||
if (isUpdating) return; |
|||
|
|||
window.addEventListener(`${modelName}-clear`, event) |
|||
mutations.forEach((mutation) => { |
|||
// Update Select2 UI if value attribute changes
|
|||
if (mutation.attributeName === "value") { |
|||
isUpdating = true; |
|||
$el.trigger("change.select2"); |
|||
isUpdating = false; |
|||
} |
|||
|
|||
// Sync error styling if class attribute changes (e.g. is-invalid added/removed)
|
|||
if (mutation.attributeName === "class") { |
|||
syncErrorState(); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
const observer = new MutationObserver(() => { |
|||
if (isUpdating) return; |
|||
observer.observe(el, { |
|||
attributes: true, |
|||
attributeFilter: ["value", "class"], |
|||
}); |
|||
|
|||
isUpdating = true; |
|||
$el.trigger('change.select2'); // Sync Select2 UI with new value safely
|
|||
isUpdating = false; |
|||
}); |
|||
observer.observe(el, { attributes: true, attributeFilter: ['value'] }); |
|||
// Listen to Livewire DOM updates to force error styling inside wire:ignore wrapper
|
|||
if (window.Livewire) { |
|||
Livewire.hook("morph.updated", ({ el: updatedEl }) => { |
|||
if (updatedEl === el || updatedEl.contains(el)) { |
|||
syncErrorState(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
cleanup(() => { |
|||
$el.select2('destroy') |
|||
window.removeEventListener(`${modelName}-clear`, event) |
|||
observer.disconnect() |
|||
}) |
|||
} |
|||
}) |
|||
cleanup(() => { |
|||
$el.select2("destroy"); |
|||
window.removeEventListener(`${modelName}-clear`, event); |
|||
observer.disconnect(); |
|||
}); |
|||
} |
|||
}, |
|||
); |
|||
} |
|||
|
|||
@ -1,10 +1,47 @@ |
|||
@use(App\Domains\Finance\Enums\AccountClassification) |
|||
@use(App\Domains\System\Enums\LifecycleStatus) |
|||
<x-modal id="chartofaccount-form-modal" :title="$this->title" wire:submit="save" wire:loading form livewire> |
|||
<div class="d-flex flex-column gap-3"> |
|||
{{-- Form Fields --}} |
|||
<x-form.select |
|||
name="form.parent" |
|||
:label="__('domains/finance/field.coa.parent')" |
|||
wire:model.live="form.parent_id" |
|||
:options="$this->chartOfAccounts->pluck('name', 'ulid')" |
|||
/> |
|||
|
|||
<x-form.input |
|||
name="form.code" |
|||
wire:model="form.code" |
|||
:disabled="$mode == 'update'" |
|||
:label="__('domains/finance/field.coa.code')" |
|||
/> |
|||
|
|||
<x-form.input |
|||
name="form.name" |
|||
wire:model="form.name" |
|||
:disabled="$mode == 'update'" |
|||
:label="__('domains/finance/field.coa.name')" |
|||
/> |
|||
|
|||
<x-form.select |
|||
name="form.classification" |
|||
:label="__('domains/finance/field.coa.classification')" |
|||
wire:model="form.classification" |
|||
:disabled="$form->parent_id != null" |
|||
:options="AccountClassification::options()" |
|||
/> |
|||
|
|||
<x-form.select |
|||
name="form.status" |
|||
:label="__('domains/finance/field.coa.status')" |
|||
wire:model="form.status" |
|||
:disabled="$mode == 'update'" |
|||
:options="LifecycleStatus::options()" |
|||
/> |
|||
</div> |
|||
|
|||
<x-slot:footer> |
|||
<x-button type="button" theme="secondary" :label="__('ui/button.cancel')" data-bs-dismiss="modal" /> |
|||
<x-button type="submit" theme="primary" :label="__('ui/button.save')" /> |
|||
<x-button type="button" theme="secondary" :label="__('ui/button.cancel')" data-bs-dismiss="modal"/> |
|||
<x-button type="submit" theme="primary" :label="__('ui/button.save')"/> |
|||
</x-slot:footer> |
|||
</x-modal> |
|||
|
|||
@ -1,10 +1,68 @@ |
|||
<x-modal id="form-modal" :title="$this->title" wire:submit="save" wire:loading form livewire> |
|||
@use(App\Domains\Finance\Enums\InvoiceType) |
|||
<x-modal id="invoice-form-modal" size="modal-lg" :title="$this->title" wire:submit="save" wire:loading form livewire> |
|||
<div class="d-flex flex-column gap-3"> |
|||
{{-- Form Fields --}} |
|||
<x-form.select |
|||
name="form.type" |
|||
:label="__('domains/finance/field.invoice.type')" |
|||
wire:model.live="form.type" |
|||
:options="InvoiceType::options()" |
|||
/> |
|||
@forelse ($this->invoiceSchema as $key => $value) |
|||
@php($model = $value->schema?->isLive() ? 'wire:model.live' : 'wire:model') |
|||
<x-dynamic-component :key="$key" |
|||
:component="$value->schema?->type->component()" |
|||
:attributes="new Illuminate\View\ComponentAttributeBag($value->attributes)->merge([ |
|||
$model => 'form.detail.'.$key, |
|||
])" |
|||
/> |
|||
@empty |
|||
<div class="col-12"> |
|||
<div class="alert bg-body-secondary d-flex align-items-center border-0 shadow-sm p-3 my-2" |
|||
role="alert"> |
|||
<div class="me-3 fs-3 text-secondary"> |
|||
@svg('tabler-x', 'w-100 h-100') |
|||
</div> |
|||
<div> |
|||
<h6 class="alert-heading mb-1 fw-bold">Tidak Ada Field Tambahan</h6> |
|||
<p class="mb-0 small text-muted"> |
|||
Tipe transaksi <strong>{{ $form->type?->label() }}</strong> |
|||
belum tersedia. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@endforelse |
|||
|
|||
<x-form.input |
|||
name="form.name" |
|||
:label="__('domains/finance/field.invoice.name')" |
|||
wire:model="form.name" |
|||
/> |
|||
<x-form.input |
|||
name="form.amount" |
|||
:label="__('domains/finance/field.invoice.amount')" |
|||
wire:model="form.amount" |
|||
/> |
|||
<x-form.select |
|||
name="form.client_name" |
|||
:label="__('domains/finance/field.invoice.client_name')" |
|||
wire:model="form.client_name" |
|||
/> |
|||
@if ($this->form->type == null || $form->type?->schema()->isCoaVisible()) |
|||
<x-form.select |
|||
name="form.coa_name" |
|||
:label="__('domains/finance/field.invoice.coa_name')" |
|||
wire:model="form.coa_id" |
|||
/> |
|||
@endif |
|||
</div> |
|||
|
|||
@push('scripts') |
|||
@vite(['resources/js/plugin/select2.js']) |
|||
@endpush |
|||
|
|||
<x-slot:footer> |
|||
<x-button type="button" theme="secondary" :label="__('ui/button.cancel')" data-bs-dismiss="modal" /> |
|||
<x-button type="submit" theme="primary" :label="__('ui/button.save')" /> |
|||
<x-button type="button" theme="secondary" :label="__('ui/button.cancel')" data-bs-dismiss="modal"/> |
|||
<x-button type="submit" theme="primary" :label="__('ui/button.save')"/> |
|||
</x-slot:footer> |
|||
</x-modal> |
|||
|
|||
Loading…
Reference in new issue