first change

This commit is contained in:
2025-07-27 17:40:56 +05:45
commit f8b9a6725b
3152 changed files with 229528 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
{{ html()->form('POST')->route('task.store')->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
@include('pms::task.partials.action')
{{ html()->form()->close() }}
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@@ -0,0 +1,17 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
{{ html()->modelForm($task, 'PUT')->route('task.update', $task->id)->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
@include('pms::task.partials.action')
{{ html()->closeModelForm() }}
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@@ -0,0 +1,120 @@
@extends('layouts.app')
@inject('employeeRepository', 'Modules\Employee\Repositories\EmployeeRepository')
@push('css')
<link rel="stylesheet" type="text/css" href="{{ asset('assets/libs/dragula/dragula.min.css') }}" />
@endpush
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
@include('pms::task.gantt.filter')
<div class="card">
<div class="card-header">
<h4 class="card-title mb-0">Gantt Chart</h4>
</div>
<div class="card-body">
<div id="chart_div"></div>
</div>
</div>
</div>
@endsection
@push('js')
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {
'packages': ['gantt']
});
google.charts.setOnLoadCallback(drawChart);
async function drawChart(formData) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Task ID');
data.addColumn('string', 'Task Name');
data.addColumn('string', 'Resource');
data.addColumn('date', 'Start Date');
data.addColumn('date', 'End Date');
data.addColumn('number', 'Duration');
data.addColumn('number', 'Percent Complete');
data.addColumn('string', 'Dependencies');
const fetchData = await getData(formData)
fetchData.data.forEach(element => {
const startDateParts = element.startDate.split('-');
const endDateParts = element.endDate.split('-');
const startDate = new Date(startDateParts[0], startDateParts[1] - 1, startDateParts[2]);
const endDate = new Date(endDateParts[0], endDateParts[1] - 1, endDateParts[2]);
data.addRow([
element.taskID.toString(),
element.taskName,
element.resource,
startDate,
endDate,
element.duration,
element.percentComplete,
element.dependencies
]);
});
var options = {
height: 400,
responsive: true,
gantt: {
trackHeight: 30
}
};
var chart = new google.visualization.Gantt(document.getElementById('chart_div'));
chart.draw(data, options);
}
const getData = async (formData) => {
const queryString = new URLSearchParams(formData).toString();
return await fetch(`{{ route('task.getGantChartTask') }}?${queryString}`, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'url': '/task/get-gant-chart-ajax',
"X-CSRF-Token": document.querySelector('input[name=_token]').value
},
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
return data;
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
}
$('.ganttChartForm').on('submit', function(e) {
e.preventDefault();
formData = $(this).serialize();
drawChart(formData);
})
$('.btn-reset').on('click', function(e) {
e.preventDefault();
$('.ganttChartForm')[0].reset();
drawChart();
})
$(window).resize(function() {
drawChart();
});
</script>
@endpush

View File

@@ -0,0 +1,45 @@
<div class="card">
<div class="card-body">
<div class="row g-2">
<div class="col-sm-8">
{{ html()->form('GET')->route('task.getGantChartTask')->attributes(['class' => 'ganttChartForm'])->open() }}
<div class="row g-3">
<div class="col-sm-6">
<div class="">
{{ html()->text('date')->class('form-control')->value(request('date'))->placeholder('Date Range')->attributes([
'id' => 'datepicker-range',
'data-provider' => 'flatpickr',
'data-date-format' => 'Y-m-d',
'data-range-date' => 'true',
]) }}
</div>
</div>
<div class="col-sm-3">
<div>
{{ html()->select('project_id', $projectList)->placeholder('Select Project')->value(request('project_id'))->class('form-control select2') }}
</div>
</div>
<!--end col-->
<div class="col-sm-2">
<div class="d-flex flex-row gap-2">
<button type="submit" class="btn btn-primary">
<i class="ri-equalizer-fill me-2"></i>Filter</button>
<a href="javascript:void(0)" class="btn btn-danger btn-reset">
<i class="ri-bin-fill me-2"></i>Reset</a>
</div>
</div>
<!--end col-->
</div>
<!--end row-->
{{ html()->form()->close() }}
</div>
<!--end col-->
</div>
<!--end row-->
</div>
</div>

View File

@@ -0,0 +1,99 @@
@extends('layouts.app')
@inject('employeeRepository', 'Modules\Employee\Repositories\EmployeeRepository')
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
@include('pms::task.partials.filter')
@include('pms::task.partials.menu')
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table id="datatable" class="display table-sm table-bordered table" style="width:100%">
<thead class="table-dark">
<tr>
<th>S.N</th>
<th>Task</th>
<th>Product</th>
<th>Start Date</th>
<th>Deadline</th>
<th>Assigned</th>
<th>Priority</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@forelse ($tasks as $key => $task)
<tr>
<td>{{ $key + 1 }}</td>
<td> <a href="{{ route('task.show', $task->id) }}">
{{ $task->title }}
</a></td>
<td>{{ $task->product?->name }}</td>
<td>{{ $task->start_date }}</td>
<td>
{{ $task->due_date }}
</td>
<td>
<div class="avatar-group flex-nowrap">
@isset($task->assignedUser)
@foreach ($task->assignedUser as $assignedId)
<div class="avatar-group-item">
<a href="javascript: void(0);" class="d-inline-block" data-bs-toggle="tooltip"
data-bs-trigger="hover" data-bs-placement="top" title="{{ $assignedId->full_name }}">
<img src="{{ asset($assignedId->profile_pic) }}" alt=""
class="rounded-circle avatar-xxs">
</a>
</div>
@endforeach
@endisset
</div>
</td>
<td>{!! $task->priority_status !!}</td>
<td>
<span class="badge" style="background-color: {{ $task->column?->color }}">
{{ $task->column?->name }} </span>
</td>
<td>
<div class="hstack flex-wrap gap-3">
@can('task.show')
<a href="{{ route('task.show', $task->id) }}" class="link-secondary fs-15">
<i class="ri-eye-fill"></i>
</a>
@endcan
@can('task.edit')
<a href="{{ route('task.edit', $task->id) }}" class="link-primary fs-15"><i
class="ri-edit-2-fill"></i></a>
@endcan
@can('task.destroy')
<a href="javascript:void(0);" data-link="{{ route('task.destroy', $task->id) }}"
data-id="{{ $task->id }}" class="link-danger fs-15 remove-item-btn"><i
class="ri-delete-bin-fill"></i></a>
@endcan
</div>
</td>
</tr>
@empty
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@include('pms::task-category.modal')
@include('pms::task.modal.task-status')
@endsection

View File

@@ -0,0 +1,151 @@
@extends('layouts.app')
@inject('employeeRepository', 'Modules\Employee\Repositories\EmployeeRepository')
@push('css')
<link rel="stylesheet" type="text/css" href="{{ asset('assets/libs/dragula/dragula.min.css') }}" />
@endpush
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
@include('pms::task.partials.filter')
@include('pms::task.partials.menu')
<div class="card">
<div class="card-body">
<div class="tasks-board mb-3" id="kanbanboard">
@foreach ($columnsWithTasks as $column)
<div class="tasks-list border-end pe-3" data-status-key="{{ $column->id }}">
<div class="d-flex border-bottom mb-2">
<div class="flex-grow-1">
<h6 class="fs-12 text-uppercase fw-semibold mb-0">{{ $column->name }} <small
class="badge totaltask-badge ms-1 align-bottom"
style="background: {{ $column->color }}">{{ (int) $column->tasks->count() }}</small>
</h6>
</div>
<div class="flex-shrink-0">
<a href="javascript:void(0);" class="text-muted" id="dropdownMenuLink12" data-bs-toggle="dropdown"
aria-expanded="false"><i class="ri-more-fill"></i></a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuLink12">
@can('kanbanColumn.edit')
<li><a class="dropdown-item edit-item-btn" href="javascript:void(0);"
data-link="{{ route('kanbanColumn.edit', $column->id) }}"><i
class="ri-edit-2-fill text-muted me-2 align-bottom"></i>
Edit</a></li>
@endcan
@can('kanbanColumn.destroy')
<li><a class="dropdown-item remove-item-btn" href="javascript:void(0);"
data-link="{{ route('kanbanColumn.destroy', $column->id) }}" data-id="{{ $column->id }}"><i
class="ri-delete-bin-5-line text-muted me-2 align-bottom"></i>
Delete</a></li>
@endcan
</ul>
</div>
</div>
<div data-simplebar class="tasks-wrapper mx-n3 px-3">
<div id="{{ str()->slug($column->name) }}-task" @class(['tasks', 'noTask' => !$column->tasks()->exists()])>
@foreach ($column->tasks as $task)
<div class="card tasks-box" data-id="{{ $task->id }}">
<div class="card-body">
<div class="d-flex border-bottom mb-3">
<div class="flex-grow-1 flex-wrap">
<h6 class="fs-15 text-truncate task-title mb-0"><a
href="{{ route('task.show', $task->id) }}" class="d-block">{{ $task->title }}</a>
</h6>
</div>
<div class="flex-shrink-0">
<a href="javascript:void(0);" class="text-muted" id="dropdownMenuLink12"
data-bs-toggle="dropdown" aria-expanded="false"><i class="ri-more-fill"></i></a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuLink12">
@can('task.show')
<li><a class="dropdown-item" href="{{ route('task.show', $task->id) }}"><i
class="ri-eye-fill text-muted me-2 align-bottom"></i>
View</a></li>
@endcan
@can('task.edit')
<li><a class="dropdown-item" href="{{ route('task.edit', $task->id) }}"><i
class="ri-edit-2-fill text-muted me-2 align-bottom"></i>
Edit</a></li>
@endcan
@can('task.destroy')
<li><a class="dropdown-item remove-item-btn" href="javascript:void(0);"
data-link="{{ route('task.destroy', $task->id) }}" data-id="{{ $task->id }}"><i
class="ri-delete-bin-5-line text-muted me-2 align-bottom"></i>
Delete</a></li>
@endcan
</ul>
</div>
</div>
@if ($task->desc)
<span class="fs-14 mb-0">
{!! Str::limit($task->desc, 150) !!}
</span>
@endif
<p class="fs-14 mb-0"><i class="ri-stack-line"></i>{{ $task->product?->name }}</p>
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<span
class="badge bg-secondary-subtle text-dark fs-12"><em>{{ $task->taskCategory?->title }}</em></span>
</div>
<div class="flex-shrink-0">
<div class="avatar-group">
@foreach ($task->assignedUser as $assignedId)
<a href="javascript: void(0);" class="avatar-group-item" data-bs-toggle="tooltip"
data-bs-trigger="hover" data-bs-placement="top" title="{{ $assignedId->full_name }}">
<img src="{{ $assignedId->profile_pic }}" alt=""
class="rounded-circle avatar-xxs">
</a>
@endforeach
</div>
</div>
</div>
</div>
<div class="card-footer border-top-dashed">
<div class="d-flex">
<div class="flex-grow-1">
<span class="fs-12"><i class="ri-time-line text-success align-bottom"></i>
{{ date('d M, Y', strtotime($task->start_date)) }}</span>
</div>
<div class="flex-shrink-0">
{!! $task->priority_status !!}
</div>
</div>
</div>
<div class="progress progress-sm">
<div class="progress-bar"
style="background-color: {{ $column->color }}; --vz-bg-opacity: 1; width: 100%;"
role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
</div>
</div>
</div>
</div>
@include('pms::task-category.modal')
@include('pms::task.modal.task-status')
@endsection
@push('js')
<script>
var columnLists = @json($columnLists);
</script>
<script src="{{ asset('assets/libs/dragula/dragula.min.js') }}"></script>
<script src="{{ asset('assets/libs/dom-autoscroller/dom-autoscroller.min.js') }}"></script>
<script src="{{ asset('assets/js/pages/tasks-kanban.init.js') }}"></script>
@endpush

View File

@@ -0,0 +1,50 @@
<div id="taskStatusModal" class="modal fade" tabindex="-1" aria-labelledby="taskStatusModalLabel" aria-hidden="true"
style="display: none;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="taskStatusModalLabel">Add Status Column</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"> </button>
</div>
<div class="modal-body">
{{ html()->form('POST')->route('kanbanColumn.store')->class(['needs-validation'])->attributes(['novalidate'])->open() }}
@include('pms::kanbanColumn.partials.action')
{{ html()->form()->close() }}
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
<div class="modal fade" id="editTaskStatusModal" tabindex="-1" aria-labelledby="editTaskStatusModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-light p-3">
<h5 class="modal-title" id="editTaskStatusModalLabel">Edit Status Column</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"
id="close-modal"></button>
</div>
<div class="modal-body">
</div>
</div>
</div>
</div>
@push('js')
<script>
$(document).on('click', '.edit-item-btn', function() {
const url = $(this).attr('data-link');
const myModal = new bootstrap.Modal(document.getElementById("editTaskStatusModal"));
$("#editTaskStatusModal .modal-body").html(
'<div class="text-center my-5"><i class="spinner-border text-primary" role="status"></i></div>'
);
myModal.show();
$.get(url, function(res, status) {
$("#editTaskStatusModal .modal-body").html(res);
});
});
</script>
@endpush

View File

@@ -0,0 +1,127 @@
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-body">
<div class="row gy-1">
<div class="col-md-12">
{{ html()->label('Title')->class('form-label') }}
{{ html()->text('title')->class('form-control')->placeholder('Enter Title')->required() }}
{{ html()->div('Please enter title')->class('invalid-feedback') }}
</div>
<div class="col-md-6">
{{ html()->label('Task Category')->class('form-label') }}
{{ html()->select('task_category_id', $categoryList)->class('form-select select2')->placeholder('Select Task Category')->required() }}
{{ html()->div('Please select task category')->class('invalid-feedback') }}
</div>
<div class="col-md-6">
{{ html()->label('Product')->class('form-label') }}
{{ html()->select('product_id', $product)->class('form-select select2')->placeholder('Select Product')->required() }}
{{ html()->div('Please select product')->class('invalid-feedback') }}
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Team Members</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
{{ html()->multiselect('assigned_id[ids][]', $memberList)->class('form-select select2')->value($task->assigned_id['ids'] ?? null)->attributes(['multiple', 'id' => 'assigned_id'])->required() }}
{{ html()->div('Please select team members')->class('invalid-feedback') }}
</div>
</div>
</div>
<!-- end card body -->
</div>
<!-- end card -->
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-12">
{{ html()->label('Description')->class('form-label') }}
{{ html()->textarea('desc')->class('form-control ckeditor-classic')->placeholder('..') }}
</div>
</div>
</div>
<!-- end card body -->
</div>
<!-- end card -->
<div class="mb-3 text-end">
<a href="{{ route('task.index') }}" class="btn btn-danger w-sm">Cancel</a>
<button type="submit" class="btn btn-success w-sm">Save</button>
</div>
</div>
<!-- end col -->
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Publish</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
{{ html()->label('Status')->class('form-label') }}
{{ html()->select('status', $status)->class('form-select')->placeholder('Select') }}
</div>
</div>
</div>
<!-- end card body -->
</div>
<!-- end card -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Date</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
{{ html()->label('Priority')->class('form-label') }}
{{ html()->select('priority', $priority)->class('form-select')->required() }}
</div>
<div class="col-md-12">
{{ html()->label('Start Date')->class('form-label') }}
{{ html()->date('start_date')->class('form-control')->placeholder('Choose Start Date') }}
</div>
<div class="col-md-12">
{{ html()->label('Due Date')->class('form-label') }}
{{ html()->date('due_date')->class('form-control')->placeholder('Choose Due Date') }}
</div>
</div>
</div>
<!-- end card body -->
</div>
<!-- end card -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Upload File</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
{{ html()->label('File')->class('form-label visually-hidden') }}
{{ html()->file('file')->class('form-control dropify')->attributes([
// 'data-default-file' => $editable ? asset('storage/' . $task?->documents?->document_path) : null,
'data-height' => 200,
]) }}
</div>
</div>
</div>
</div>
<!-- end card -->
</div>
<!-- end col -->
</div>

View File

@@ -0,0 +1,16 @@
<div data-simplebar style="height: 200px;" class="mx-n3 mb-2 px-3">
@forelse ($task->comments as $comment)
<div class="d-flex mb-4">
<div class="flex-shrink-0">
<img src="{{ asset('assets/images/user.png') }}" alt="" class="avatar-xs rounded-circle material-shadow" />
</div>
<div class="flex-grow-1 ms-3">
<h5 class="fs-13"><a href="#">{{ $comment->user?->name }}</a> <small
class="text-muted">{{ $comment->created_at_formatted }}</small></h5>
<p class="text-muted">{!! $comment->content !!}</p>
</div>
</div>
@empty
<p class="text-danger"> No Comment Found.. </p>
@endforelse
</div>

View File

@@ -0,0 +1,62 @@
<div class="card bg-light">
<div class="card-header border-bottom-dashed">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="card-title mb-0">Advance Filter</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="#collapseExample2" 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="collapseExample2">
{{ html()->form('GET')->open() }}
<div class="row">
<div class="col-sm-3 mb-1">
{{ html()->label('Search')->class('form-label') }}
<div class="search-box">
{{ html()->text('search')->class('form-control form-control-sm')->placeholder('Search...') }}
<i class="ri-search-line search-icon"></i>
</div>
</div>
<div class="col-sm-3 mb-1">
{{ html()->label('Date')->class('form-label') }}
{{ html()->text('date')->class('form-control form-control-sm daterange-custom-picker')->value(request('date'))->placeholder('Date Range') }}
</div>
<div class="col-sm-3 mb-1">
{{ html()->label('Status')->class('form-label') }}
{{ html()->select('status', $statusList)->placeholder('Select Status')->value(request('status'))->class('form-control form-control-sm') }}
</div>
<div class="col-sm-3 mb-1">
{{ html()->label('Assigned to')->class('form-label') }}
{{ html()->select('assigned_to', $memberList)->placeholder('Select Assignee')->value(request('assigned_to'))->class('form-control form-control-sm') }}
</div>
<div class="col-sm-3 mb-1">
{{ html()->label('Priority')->class('form-label') }}
{{ html()->select('priority', Modules\PMS\Models\Task::PRIORITY)->placeholder('Select Priority')->value(request('priority'))->class('form-control form-control-sm') }}
</div>
<!--end col-->
</div>
<div class="d-flex justify-content-center list-grid-nav hstack mt-2 gap-1">
<button type="submit" class="btn btn-warning btn-sm">Filter</button>
<a href="{{ route(Route::currentRouteName()) }}" class="btn btn-danger btn-sm reset-filter">Reset</a>
</div>
{{ html()->form()->close() }}
</div>
</div>

View File

@@ -0,0 +1,40 @@
<div class="row justify-content-between my-2">
<div class="col-sm-auto">
<div class="list-grid-nav hstack gap-1">
@can('task.create')
<a href="{{ route('task.create') }}" class="btn btn-sm btn-primary btn-md waves-effect waves-light"><i
class="ri-add-fill align-bottom"></i> Add</a>
@endcan
@can('kanbanColumn.index')
<button href="javascript:void(0)" class="btn btn-sm btn-secondary waves-effect waves-light fs-14 text-white"
data-bs-toggle="modal" data-bs-target="#taskStatusModal"><i class="ri-add-line"></i> <span class="">Status
Column</span></button>
@endcan
@can('taskCategory.index')
<button href="javascript:void(0)" class="btn btn-sm btn-success waves-effect waves-light fs-14"
data-bs-toggle="modal" data-bs-target="#myModal"><i class="ri-book-3-line"></i> <span class="">Add
Category</span></button>
@endcan
</div>
</div>
<div class="col-sm-auto">
<div class="btn-group material-shadow mt-2" role="group" aria-label="Basic example">
@can('task.index')
<a href="{{ route('task.index') }}" id="list-view-button"
class="btn btn-sm btn-{{ Route::is('task.index') ? 'primary' : 'secondary' }} btn-icon material-shadow-none fs-14"><i
class="ri-list-unordered"></i></a>
@endcan
@can('task.kanban')
<a href="{{ route('task.kanban') }}" id="grid-view-button"
class="btn btn-sm btn-{{ Route::is('task.kanban') ? 'primary' : 'secondary' }} btn-icon material-shadow-none fs-14"><i
class="ri-grid-fill"></i></a>
@endcan
@can('task.export')
<a href="{{ route('task.export', request()->all()) }}" id=""
class="btn btn-sm btn-secondary btn-icon material-shadow-none fs-14"><i class="ri-download-2-line"></i></a>
@endcan
</div>
</div>
<!--end col-->
</div>

View File

@@ -0,0 +1,679 @@
@extends('layouts.app')
@section('css')
{{-- <link rel="stylesheet" type="text/css" href="{{ asset('assets/libs/glightbox/css/glightbox.min.css') }}" /> --}}
{{-- <link rel="stylesheet" type="text/css" href="{{ asset('assets/libs/fancybox/jquery.fancybox.css') }}" /> --}}
@endsection
@section('content')
<div class="page-content">
<div class="container-fluid">
<div class="row">
<div class="col-xxl-9">
<div class="card">
<div class="card-body">
<div class="row mb-3">
<div class="col-md">
<div class="row align-items-center g-3">
<div class="col-md d-flex justify-content-between">
<h4 class="fw-bold">{{ $task->title }}</h4>
<h4 class="fw-bold text-end">{{ $clientName }}</h4>
</div>
</div>
</div>
<div class="col-md-auto">
<div class="hstack flex-wrap gap-1">
<span class="badge bg-{{ $task->status_name['color'] }} fs-12"> {!! $task->status_name['status'] !!} </span>
</div>
</div>
</div>
<div class="text-muted">
<h6 class="fw-semibold text-uppercase mb-3">Description</h6>
<p>{!! $task->desc !!}</p>
</div>
@if ($task->subTask->count() > 0)
<div class="text-muted">
<h6 class="fw-semibold text-uppercase mb-2">Sub Task</h6>
<div class="acitivity-timeline">
@foreach ($task->subTask as $subTask)
<div class="acitivity-item d-flex">
<div class="flex-shrink-0">
<i class="mdi mdi-checkbox-blank-circle text-info me-2"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-1">{{ $subTask->title }}
<span class="text-danger ms-1 align-middle"><em>
{{ $subTask->start_date }}</em></span>
</h6>
<p class="text-muted mb-2">{!! $subTask->desc !!}</p>
</div>
</div>
@endforeach
</div>
</div>
@endif
<div class="border-top border-top-dashed mt-4 pt-3">
<div class="row gy-2">
<div class="col-lg-3 col-sm-4">
<div>
<p class="text-uppercase fw-medium mb-2">Product :</p>
<h5 class="fs-15 mb-0">{{ $task->product?->name }}</h5>
</div>
</div>
<div class="col-lg-3 col-sm-4">
<div>
<p class="text-uppercase fw-medium mb-2">Category :</p>
<h5 class="fs-15 mb-0">{{ $task->taskCategory?->title }}</h5>
</div>
</div>
<div class="col-lg-2 col-sm-4">
<div>
<p class="text-uppercase fw-medium mb-2">Create Date :</p>
<h5 class="fs-15 mb-0">{{ $task->start_date }}</h5>
</div>
</div>
<div class="col-lg-2 col-sm-4">
<div>
<p class="text-uppercase fw-medium mb-2">Due Date :</p>
<h5 class="fs-15 mb-0">{{ $task->due_date }}</h5>
</div>
</div>
<div class="col-lg-2 col-sm-4">
<div>
<p class="text-uppercase fw-medium mb-2">Priority :</p>
{!! $task->priority_status !!}
</div>
</div>
</div>
</div>
</div>
</div>
<!--end card-->
<div class="card">
<div class="card-header">
<div>
<ul class="nav nav-tabs-custom card-header-tabs border-bottom-0 rounded" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="tab" href="#home-1" role="tab">
Comments ({{ $task->comments->count() }})
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#messages-1" role="tab">
File ({{ $task->documents->count() }})
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#sub-task-1" role="tab">
Sub Task
</a>
</li>
</ul>
<!--end nav-->
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane active" id="home-1" role="tabpanel">
<h5 class="card-title mb-4">Comments</h5>
<div class="comment-box">
</div>
{{ html()->form('POST')->route('task.storeComment')->class(['needs-validation commentForm'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
<div class="row g-3">
<div class="col-lg-12">
<label for="exampleFormControlTextarea1" class="form-label">Leave a Comments</label>
<textarea class="form-control bg-light border-light" id="exampleFormControlTextarea1" name="content" rows="3"
placeholder="Enter comments"></textarea>
{{ html()->hidden('task_id', $task->id) }}
</div>
<!--end col-->
<div class="col-12 text-end">
<input type="submit" class="btn btn-success submit-btn" value="Add Comment" />
</div>
</div>
<!--end row-->
{{ html()->form()->close() }}
</div>
<!--end tab-pane-->
<div class="tab-pane" id="messages-1" role="tabpanel">
<div class="drop-content">
<div class="dropzone">
<div class="fallback">
<input name="file" type="file" multiple="multiple">
</div>
<div class="dz-message needsclick">
<div class="mb-3">
<i class="display-4 text-muted ri-upload-cloud-2-fill"></i>
</div>
<h4>Drop files here or click to upload.</h4>
</div>
</div>
<ul class="list-unstyled mb-0" id="dropzone-preview">
<li class="mt-2" id="dropzone-preview-list">
<div class="rounded border">
<div class="d-flex p-2">
<div class="me-3 flex-shrink-0">
<div class="avatar-sm bg-light rounded">
<img data-dz-thumbnail class="img-fluid d-block rounded"
src="assets/images/new-document.png" alt="Dropzone-Image" />
</div>
</div>
<div class="flex-grow-1">
<div class="pt-1">
<h5 class="fs-14 mb-1" data-dz-name>&nbsp;</h5>
<p class="fs-13 text-muted mb-0" data-dz-size></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
</div>
<div class="ms-3 flex-shrink-0">
<button data-dz-remove class="btn btn-sm btn-danger">Delete</button>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="row gx-2 gy-2 rounded border border-dashed p-2">
@foreach ($task->documents as $document)
<div class="col-3">
<a href="{{ asset('storage/' . $document->document_path) }}" data-fancybox="images"
target="_blank">
<img src="{{ asset('storage/' . $document->document_path) }}" alt=""
class="img-fluid material-shadow-none1 rounded" style="width: 100%;height: 150px;object-fit: cover;">
</a>
</div>
@endforeach
</div>
</div>
<!--end tab-pane-->
<div class="tab-pane" id="sub-task-1" role="tabpanel">
{{-- <h5 class="card-title mb-4">Sub Task</h5> --}}
{{ html()->form('POST')->route('task.storeSubTask')->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
@include('pms::task.subTask.create')
{{ html()->form()->close() }}
</div>
<!--end tab-pane-->
</div>
<!--end tab-content-->
</div>
</div>
<!--end card-->
</div>
<!--end col-->
<div class="col-xxl-3">
<div class="mb-2 text-end">
<a href="javascript:void(0)" class="btn btn-sm btn-warning" data-bs-toggle="modal"
data-bs-target="#emailModal">
<i class="ri-mail-fill me-2 align-middle"></i>Send Reminder
</a>
</div>
<div class="card">
<div class="card-body text-center">
<h6 class="card-title flex-grow-1 mb-3 text-start">Time Tracking</h6>
<div class="mb-0">
<lord-icon id="time-icon" src="https://cdn.lordicon.com/kbtmbyzy.json"
colors="primary:#405189,secondary:#02a8b5" style="width:90px;height:90px"></lord-icon>
</div>
<div class="stopwatch fs-20"></div>
<ul class="results"></ul>
<div class="hstack justify-content-center control gap-2">
<button class="btn btn-success btn-sm start">
<i class="ri-play-circle-line me-1 align-bottom"></i> Start
</button>
<button class="btn btn-info btn-sm stop">
<i class="ri-stop-circle-line me-1 align-bottom"></i> Stop
</button>
</div>
</div>
</div>
<!--end card-->
<div class="card mb-3">
<div class="card-body">
<div class="mb-4">
{{ html()->label('Status')->class('form-label') }}
{{ html()->select('status', $status, $task->status)->class('form-control')->required() }}
</div>
</div>
</div>
<!--end card-->
<div class="card mb-3">
<div class="card-body">
<div class="d-flex mb-3">
<h6 class="card-title flex-grow-1 mb-0">Assigned To</h6>
</div>
<ul class="list-unstyled vstack mb-0 gap-3">
@foreach ($task->assignedUser as $assignedUser)
<li>
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<img src="{{ $assignedUser->profile_pic }}" alt=""
class="avatar-xs rounded-circle material-shadow">
</div>
<div class="flex-grow-1 ms-2">
<h6 class="mb-1"><a href="#">{{ $assignedUser->full_name }}</a></h6>
<p class="text-muted mb-0">{{ $assignedUser->email }}</p>
</div>
{{-- <div class="flex-shrink-0">
<div class="dropdown">
<button class="btn btn-icon btn-sm fs-16 text-muted dropdown material-shadow-none"
type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="ri-more-fill"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:void(0);"><i
class="ri-eye-fill text-muted me-2 align-bottom"></i>View</a></li>
</ul>
</div>
</div> --}}
</div>
</li>
@endforeach
</ul>
</div>
</div>
<!--end card-->
<div class="card">
<div class="card-body">
<h5 class="card-title mb-3">Attachments</h5>
<div class="vstack gap-2">
@foreach ($task->documents as $document)
<div class="rounded border border-dashed p-2">
<div class="d-flex align-items-center">
<div class="me-3 flex-shrink-0">
<div class="avatar-xs">
<img src="{{ asset('storage/' . $document->document_path) }}" alt=""
class="avatar-xs material-shadow rounded">
</div>
</div>
<div class="flex-grow-1 overflow-hidden">
<h5 class="fs-13 mb-1"><a href="javascript:void(0);"
class="text-body text-truncate d-block">{{ $document->document_name }}</a></h5>
</div>
{{-- <div class="ms-2 flex-shrink-0">
<div class="d-flex gap-1">
<button type="button" class="btn btn-icon text-muted btn-sm fs-18 material-shadow-none"><i
class="ri-download-2-line"></i></button>
<div class="dropdown">
<button class="btn btn-icon text-muted btn-sm fs-18 dropdown material-shadow-none"
type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="ri-more-fill"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item remove-item-btn" href="javascript:void(0);"
data-link="{{ route('document.destroy', $document->id) }}"
data-id="{{ $document->id }}"><i
class="ri-delete-bin-fill text-muted me-2 align-bottom"></i> Delete</a></li>
</ul>
</div>
</div>
</div> --}}
</div>
</div>
@endforeach
</div>
</div>
</div>
<!--end card-->
</div>
<!---end col-->
</div>
<!--end row-->
</div>
<!-- container-fluid -->
</div>
<div class="modal fade" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true"
style="display: none;">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header border-0">
<h5 class="modal-title" id="emailModalLabel">Send Email</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{ html()->form('POST', route('task.sendMail'))->class(['needs-validation sendMailForm'])->attributes(['novalidate'])->open() }}
{{ html()->hidden('task_id', $task->id) }}
<ul class="list-inline d-flex flex-column flex-wrap gap-2">
<li class="list-inline-item">
<span class="fw-bold">To:</span>
<span class="selected-names">
@foreach ($task->assignedUser as $assignedUser)
{{ $assignedUser->email }},
{{-- {{ html()->hidden('email[]')->value($assignedUser->email) }} --}}
@endforeach
</span>
</li>
<li class="list-inline-item">
<span class="fw-bold">From:</span> {{ setting('email') }}
</li>
</ul>
<div class="card card-body bg-white">
<div class="row g-3">
<div class="col-lg-12">
{{ html()->label('Select Template')->class('form-label') }}
{{ html()->select('template', @$template['email'])->class('form-control change-template')->placeholder('Select Template')->attributes(['data-type' => 'email']) }}
</div>
<div class="col-lg-12">
{{ html()->label('Subject')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->text('subject')->class('form-control subject')->required() }}
{{ html()->div('Subject is required!')->class('invalid-feedback') }}
</div>
<div class="col-lg-12">
{{ html()->label('Message')->class('form-label') }}
{{ html()->textarea('message')->class('form-control message ckeditor-classic')->id('email-editor') }}
</div>
</div>
</div>
<div class="hstack justify-content-end mt-2 gap-2">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Send</button>
</div>
{{ html()->form()->close() }}
</div>
</div>
</div>
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
<script src="{{ asset('assets/js/pages/plugins/lord-icon-2.1.0.js') }}"></script>
<script src="{{ asset('assets/js/pages/timer.js') }}"></script>
<script src="{{ asset('assets/libs/dropzone/dropzone-min.js') }}"></script>
{{-- <script src="{{ asset('assets/libs/fancybox/jquery.fancybox.js') }}"></script> --}}
{{-- <script src="{{ asset('assets/libs/glightbox/js/glightbox.min.js') }}"></script>
<script src="{{ asset('assets/libs/isotope-layout/isotope.pkgd.min.js') }}"></script>
<script src="{{ asset('assets/js/pages/gallery.init.js') }}"></script> --}}
<script>
var previewTemplate, dropzone, dropzonePreviewNode = document
.querySelector("#dropzone-preview-list");
var task_id = "{{ $task->id }}";
dropzonePreviewNode.id = "";
if (dropzonePreviewNode) {
previewTemplate = dropzonePreviewNode.parentNode.innerHTML;
dropzonePreviewNode.parentNode.removeChild(dropzonePreviewNode);
dropzone = new Dropzone(".dropzone", {
url: "uploadFile",
method: "post",
previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview",
headers: {
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
}
});
dropzone.on("sending", function(file, xhr, formData) {
formData.append("task_id", '{{ $task->id }}');
});
}
$(document).ready(function() {
fetchComment();
let time = '{{ $task->latest_task_time?->end_time }}';
let timeArray = [0, 0, 0];
if (time) {
timeArray = time.split(':').map(numStr => parseInt(numStr, 10));
}
let stopwatch = new Stopwatch(
$('.stopwatch'),
$('.results'),
timeArray
);
$('.start').click(function() {
console.log('time start vayo');
stopwatch.start();
});
$('.lap').click(function() {
stopwatch.lap();
});
$('.stop').click(function() {
stopwatch.stop();
let formData = {
task_id: task_id,
time: $('.stopwatch').text()
};
storeTime(formData)
});
$('.restart').click(function() {
stopwatch.restart();
});
$('.clear').click(function() {
stopwatch.clear();
});
});
$('body').on('change', '#status', function() {
status = $(this).val();
$.ajax({
type: "GET",
url: '{{ route('task.changeStatus') }}',
data: {
id: task_id,
changeStatus: status
},
success: function(data) {
flasher.success('Status Changed');
location.reload()
},
});
})
$('body').on('submit', '.commentForm', function(event) {
event.preventDefault();
const url = $(this).attr('action');
const method = $(this).attr('method');
let formData = new FormData($(this)[0]);
let button = $(this).find('.submit-btn');
// button.prop('disabled', true);
// $('.loader').removeClass('d-none');
// $('.icon').addClass('d-none');
$.ajax({
url: url,
type: method,
data: formData,
dataType: 'json',
processData: false,
contentType: false,
headers: {
'X-CSRF-TOKEN': $("meta[name='csrf-token']").attr('content'),
},
}).done(function(response) {
if (response.status == true) {
flasher.success(response.msg);
$('.commentForm')[0].reset();
fetchComment();
}
}).fail(function(jqXHR, textStatus, errorThrown) {
flasher.error("Failed to send message!");
})
});
const fetchComment = () => {
fetch(base_url + `/admin/task/fetchComment/${task_id}`)
.then(response => response.text())
.then(html => {
document.querySelector('.comment-box').innerHTML = html;
})
}
const storeTime = (formData) => {
$.ajax({
url: '{{ route('taskTime.store') }}',
type: 'POST',
data: formData,
dataType: 'json',
headers: {
'X-CSRF-TOKEN': $("meta[name='csrf-token']").attr('content'),
},
}).done(function(response) {
if (response.status == true) {
flasher.success(response.msg);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
flasher.error("Failed to send message!");
})
}
</script>
<script>
$(document).ready(function() {
const icon = $('#time-icon');
$('.start').on('click', function() {
icon.attr('trigger', 'loop');
});
$('.stop').on('click', function() {
icon.attr('trigger', '');
});
$('body').on('change', '.change-template', function() {
id = $(this).val();
that = $(this)
if (id == '') {
toastr.error('Choose Template First');
return false;
}
$.ajax({
type: "GET",
url: '{{ route('template.findByAjax') }}',
data: {
id: id,
},
success: function(res) {
result = res.data;
if (result.status) {
that.parents('.card').find('.subject').val(result.subject);
let messageFieldId = that.parents('.card').find('.message').attr('id');
let editor = CKEDITOR.instances[messageFieldId];
if (editor) {
let currentContent = editor.getData();
editor.setData(result.message);
}
}
},
});
})
$(document).on('submit', '.sendMailForm', function(event) {
event.preventDefault();
const url = $(this).attr('action');
const method = $(this).attr('method');
let form = $(this);
let formData = new FormData(form[0]);
let myModalEl = $(this).closest('.modal').attr('id');
var emailModal = bootstrap.Modal.getOrCreateInstance($('#' + myModalEl))
const button = $(this).find('button[type="submit"]');
$.ajax({
url: url,
type: method,
data: formData,
dataType: 'json',
processData: false,
contentType: false,
headers: {
'X-CSRF-TOKEN': $("meta[name='csrf-token']").attr('content'),
},
beforeSend: () => {
button.text('Sending...');
button.prop('disabled', true);
},
success: function(response) {
if (response.status == true) {
emailModal.hide()
button.text('Send');
button.prop('disabled', false);
let messageFieldId = form.find('.message').attr('id');
let editor = CKEDITOR.instances[messageFieldId];
if (editor) {
editor.setData('');
}
form[0].reset();
toastr.success(response.msg);
}
$('#student-table').DataTable().ajax.reload();
var statusModal = bootstrap.Modal.getInstance($('#bulkStatusModal'));
statusModal.hide();
},
error: function(xhr) {
if (xhr.status == 422) {
let errors = xhr.responseJSON.errors;
$('.error-message').remove();
$.each(errors, function(key, value) {
let errorMessage = $(
'<span class="error-message text-danger mt-2"></span>'
)
.text(
value[0]);
$('#' + key).after(errorMessage);
});
} else {
console.log(xhr);
}
},
complete: () => {
button.text('Save');
button.prop('disabled', false);
}
})
})
});
</script>
@endpush

View File

@@ -0,0 +1,33 @@
<div class="row gy-1">
<div class="col-md-12">
{{ html()->label('Title')->class('form-label') }}
{{ html()->text('title')->class('form-control')->placeholder('Enter Title')->required() }}
{{ html()->div('Please enter title')->class('invalid-feedback') }}
</div>
<div class="col-md-6">
{{ html()->label('Start Date')->class('form-label') }}
{{ html()->date('start_date')->class('form-control')->placeholder('Choose Start Date')->required() }}
{{ html()->div('Please choose start date')->class('invalid-feedback') }}
</div>
<div class="col-md-6">
{{ html()->label('Due Date')->class('form-label') }}
{{ html()->date('due_date')->class('form-control')->placeholder('Choose Due Date') }}
</div>
<div class="col-md-12">
{{ html()->label('Description')->class('form-label') }}
{{ html()->textarea('desc')->class('form-control ckeditor-classic')->placeholder('..') }}
</div>
</div>
<div class="mt-3 text-end">
{{ html()->hidden('parent_id', $task->id) }}
<a href="{{ route('task.index') }}" class="btn btn-danger w-sm">Cancel</a>
<button type="submit" class="btn btn-success w-sm">Save</button>
</div>