feat: Add CostCalculator module with CRUD functionality and frontend integration

- Created new module for CostCalculator with necessary routes and controllers.
- Implemented views for creating, editing, and displaying costs.
- Added form partials for cost input with validation.
- Integrated living status options in the form.
- Developed frontend logic for cost calculation steps with dynamic UI updates.
- Included necessary assets (JS and SCSS) for styling and functionality.
- Updated constants for living status options.
- Enhanced existing client-side cost calculator page with new features.
This commit is contained in:
2025-08-13 17:58:49 +05:45
parent 03c5955768
commit 165012ea56
38 changed files with 1405 additions and 162 deletions

View File

@@ -0,0 +1,10 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
{{ html()->form('POST')->route('costCalculator.store')->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data', 'onkeydown' => "return event.key != 'Enter';"])->open() }}
@include('costCalculator::cost.partials.form')
{{ html()->form()->close() }}
</div>
@endsection

View File

@@ -0,0 +1,326 @@
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="card-title mb-0">Cost Calculator</h6>
</div>
<div class="flex-shrink-0">
<ul class="list-inline card-toolbar-menu d-flex align-items-center mb-0">
<li class="list-inline-item">
<a class="minimize-card align-middle" data-bs-toggle="collapse"
href="#collapse-personal" role="button" aria-expanded="true"
aria-controls="collapseExample2">
<i class="mdi mdi-plus plus align-middle"></i>
<i class="mdi mdi-minus minus align-middle"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="card-body show collapse" id="collapse-personal">
<div class="row gy-3">
<div class="col-md-4">
{{ html()->label('Country')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->select('country_id', $countryOptions)->placeholder('Select')->class('form-select choices-select')->required() }}
{{ html()->div('Please select country')->class('invalid-feedback') }}
</div>
<div class="col-md-4">
{{ html()->label('Program Level')->class('form-label')->for('programlevel_id') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->select('programlevel_id', $programLevelOptions)->placeholder('Select')->class('form-select choices-select')->required() }}
{{ html()->div('Please select program level')->class('invalid-feedback') }}
</div>
<div class="col-md-4">
{{ html()->label('Living Status')->class('form-label')->for('institution_id') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->select('living_status_id', $livingStatusOptions)->placeholder('Select')->class('form-select choices-select')->required() }}
{{ html()->div('Please select Living Status')->class('invalid-feedback') }}
</div>
<div class="col-md-12">
{{ html()->label('Program')->class('form-label')->for('program_id') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->select('program_id', $programOptions)->placeholder('Select')->class('form-select choices-select')->required() }}
{{ html()->div('Please select program')->class('invalid-feedback') }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="card-title mb-0">Living Cost</h6>
</div>
<div class="flex-shrink-0">
<ul class="list-inline card-toolbar-menu d-flex align-items-center mb-0">
<li class="list-inline-item">
<a class="minimize-card collapsed align-middle" data-bs-toggle="collapse"
href="#collapse-preparation" role="button" aria-expanded="false"
aria-controls="collapseExample2">
<i class="mdi mdi-plus plus align-middle"></i>
<i class="mdi mdi-minus minus align-middle"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="card-body show collapse" id="collapse-preparation">
<div class="table-responsive">
<table class="table-borderless table-nowrap table-sm table" id="livingCostTable">
<thead class="table-primary text-center">
<tr>
<th scope="col" width=30%>Monthly</th>
<th scope="col" width=30%>Yearly</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@if ($editable)
@if ($program->level)
@forelse ($program->level as $key => $item)
@include('coursefinder::program.partials.qualification-form', [
'numInc' => $key,
'value' => $item,
])
@empty
@endforelse
@else
@include('coursefinder::program.partials.qualification-form', [
'numInc' => 0,
])
@endif
@else
@include('coursefinder::program.partials.qualification-form', [
'numInc' => 0,
])
@endif
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="card-title mb-0">Accomodation Cost</h6>
</div>
<div class="flex-shrink-0">
<ul class="list-inline card-toolbar-menu d-flex align-items-center mb-0">
<li class="list-inline-item">
<a class="minimize-card collapsed align-middle" data-bs-toggle="collapse"
href="#collapse-proficiency" role="button" aria-expanded="false"
aria-controls="collapseExample2">
<i class="mdi mdi-minus minus align-middle"></i>
<i class="mdi mdi-plus plus align-middle"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="card-body show collapse" id="collapse-proficiency">
<div class="table-responsive">
<table class="table-borderless table-nowrap table-sm table" id="proficiency-table">
<thead class="table-primary text-center">
<tr>
<th scope="col" width=30%>Monthly</th>
<th scope="col" width=30%>Yearly</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@if ($editable)
@forelse ($program->tests as $key => $item)
@include('coursefinder::program.partials.proficiency-form', [
'numInc' => $key,
'value' => $item,
])
@empty
@include('coursefinder::program.partials.proficiency-form', [
'numInc' => 0,
])
@endforelse
@else
@include('coursefinder::program.partials.proficiency-form', [
'numInc' => 0,
])
@endif
</tbody>
</table>
</div>
</div>
<!-- end card body -->
</div>
<!-- end card -->
<div class="card">
<div class="card-header">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="card-title mb-0">One Time Cost (Non-Refundable)</h6>
</div>
<div class="flex-shrink-0">
<ul class="list-inline card-toolbar-menu d-flex align-items-center mb-0">
<li class="list-inline-item">
<a class="minimize-card collapsed align-middle" data-bs-toggle="collapse"
href="#collapse-fee-breakdown" role="button" aria-expanded="false"
aria-controls="collapseExample2">
<i class="mdi mdi-plus plus align-middle"></i>
<i class="mdi mdi-minus minus align-middle"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="card-body show collapse" id="collapse-fee-breakdown">
<div class="table-responsive">
<table class="table-borderless table-nowrap table-sm table" id="feeBreakdown-table">
<thead class="table-primary text-center">
<tr>
<th scope="col">Visa</th>
<th scope="col">Biometrics</th>
<th scope="col">Sevis</th>
<th scope="col">Application</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@if ($editable)
@if ($program->fee_breakdown)
@forelse ($program->fee_breakdown as $key => $item)
@include('coursefinder::program.partials.feeBreakdown', [
'numInc' => $key,
'value' => $item,
])
@empty
@endforelse
@else
@include('coursefinder::program.partials.feeBreakdown', [
'numInc' => 0,
])
@endif
@else
@include('coursefinder::program.partials.feeBreakdown', [
'numInc' => 0,
])
@endif
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="card-title mb-0">Other Services</h6>
</div>
<div class="flex-shrink-0">
<ul class="list-inline card-toolbar-menu d-flex align-items-center mb-0">
<li class="list-inline-item">
<a class="minimize-card collapsed align-middle" data-bs-toggle="collapse"
href="#collapse-course-module" role="button" aria-expanded="false"
aria-controls="collapseExample2">
<i class="mdi mdi-plus plus align-middle"></i>
<i class="mdi mdi-minus minus align-middle"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="card-body show collapse" id="collapse-course-module">
<div class="table-responsive">
<table class="table-borderless table-nowrap table-sm table" id="courseModule-table">
<thead class="table-primary text-center">
<tr>
<th scope="col">Flight Ticket</th>
<th scope="col">Health Insurance</th>
<th scope="col">Extra</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@if ($editable)
@if ($program->course_module)
@forelse ($program->course_module as $key => $item)
@include('coursefinder::program.partials.courseModule', [
'numInc' => $key,
'value' => $item,
])
@empty
@endforelse
@else
@include('coursefinder::program.partials.courseModule', [
'numInc' => 0,
])
@endif
@else
@include('coursefinder::program.partials.courseModule', [
'numInc' => 0,
])
@endif
</tbody>
</table>
</div>
</div>
</div>
<div class="mb-3 text-end">
<a href="{{ route('program.index') }}" class="btn btn-danger w-sm">Cancel</a>
<button type="submit" class="btn btn-success w-sm">Save</button>
</div>
</div>
</div>
@push('js')
<script>
let numInc = 0;
const cloneRow = (element) => {
let newRow = $(element).closest('tr').clone();
numInc++;
newRow.find('input, select').each(function() {
let name = $(this).attr('name');
name = name.replace(/\[\d+\]/, '[' + numInc + ']');
$(this).attr('name', name);
});
newRow.find('input').val('');
$(element).parents('table').find('tbody').append(newRow);
}
const removeRow = (element) => {
count = $(element).closest('tbody').find('tr').length;
console.log(count);
if (count > 1) {
$(element).closest('tr').remove();
}
}
</script>
@endpush