Add UserRights branch from github changeset: 10a346901a9018735d9fd70a1a79a737595177ab
This commit is contained in:
parent
11241696b3
commit
ba302898b8
14
docker-compose.yml
Normal file
14
docker-compose.yml
Normal file
@ -0,0 +1,14 @@
|
||||
services:
|
||||
react:
|
||||
image: node:18
|
||||
container_name: react_app
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./react:/app
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- REACT_APP_API_URL=http://dotnet_api:5000
|
||||
command: sh -c "git clone https://github.com/marcoioitsoft/marco.pms.web.git /app && npm install && npm start"
|
||||
networks:
|
||||
- dev_network
|
@ -40,6 +40,9 @@
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/bootstrap-select/bootstrap-select.css" />
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/select2/select2.css" />
|
||||
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/animate-css/animate.css" />
|
||||
<link rel="stylesheet" href="/assets/vendor/libs/sweetalert2/sweetalert2.css" />
|
||||
|
||||
<!-- Helpers -->
|
||||
<script src="/assets/vendor/js/helpers.js"></script>
|
||||
<script src="/assets/js/config.js"></script>
|
||||
@ -64,6 +67,7 @@
|
||||
<script src="/assets/vendor/libs/hammer/hammer.js"></script>
|
||||
<script src="/assets/vendor/libs/i18n/i18n.js"></script>
|
||||
<script src="/assets/vendor/libs/typeahead-js/typeahead.js"></script>
|
||||
<script src="/assets/vendor/libs/select2/select2.js"></script>
|
||||
<script src="/assets/vendor/js/menu.js"></script>
|
||||
|
||||
|
||||
@ -91,7 +95,7 @@
|
||||
<!-- component -->
|
||||
<script src="./public/js/timppick.js"></script>
|
||||
|
||||
|
||||
<script src="/assets/vendor/libs/sweetalert2/sweetalert2.js" ></script>
|
||||
|
||||
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js"></script>
|
||||
|
825
public/assets/js/tables-datatables-basic.js
Normal file
825
public/assets/js/tables-datatables-basic.js
Normal file
@ -0,0 +1,825 @@
|
||||
/**
|
||||
* DataTables Basic
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let fv, offCanvasEl;
|
||||
document.addEventListener('DOMContentLoaded', function (e) {
|
||||
(function () {
|
||||
const formAddNewRecord = document.getElementById('form-add-new-record');
|
||||
|
||||
setTimeout(() => {
|
||||
const newRecord = document.querySelector('.create-new'),
|
||||
offCanvasElement = document.querySelector('#add-new-record');
|
||||
|
||||
// To open offCanvas, to add new record
|
||||
if (newRecord) {
|
||||
newRecord.addEventListener('click', function () {
|
||||
offCanvasEl = new bootstrap.Offcanvas(offCanvasElement);
|
||||
// Empty fields on offCanvas open
|
||||
(offCanvasElement.querySelector('.dt-full-name').value = ''),
|
||||
(offCanvasElement.querySelector('.dt-post').value = ''),
|
||||
(offCanvasElement.querySelector('.dt-email').value = ''),
|
||||
(offCanvasElement.querySelector('.dt-date').value = ''),
|
||||
(offCanvasElement.querySelector('.dt-salary').value = '');
|
||||
// Open offCanvas with form
|
||||
offCanvasEl.show();
|
||||
});
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// Form validation for Add new record
|
||||
fv = FormValidation.formValidation(formAddNewRecord, {
|
||||
fields: {
|
||||
basicFullname: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'The name is required'
|
||||
}
|
||||
}
|
||||
},
|
||||
basicPost: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'Post field is required'
|
||||
}
|
||||
}
|
||||
},
|
||||
basicEmail: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'The Email is required'
|
||||
},
|
||||
emailAddress: {
|
||||
message: 'The value is not a valid email address'
|
||||
}
|
||||
}
|
||||
},
|
||||
basicDate: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'Joining Date is required'
|
||||
},
|
||||
date: {
|
||||
format: 'MM/DD/YYYY',
|
||||
message: 'The value is not a valid date'
|
||||
}
|
||||
}
|
||||
},
|
||||
basicSalary: {
|
||||
validators: {
|
||||
notEmpty: {
|
||||
message: 'Basic Salary is required'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
trigger: new FormValidation.plugins.Trigger(),
|
||||
bootstrap5: new FormValidation.plugins.Bootstrap5({
|
||||
// Use this for enabling/changing valid/invalid class
|
||||
// eleInvalidClass: '',
|
||||
eleValidClass: '',
|
||||
rowSelector: '.col-sm-12'
|
||||
}),
|
||||
submitButton: new FormValidation.plugins.SubmitButton(),
|
||||
// defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
|
||||
autoFocus: new FormValidation.plugins.AutoFocus()
|
||||
},
|
||||
init: instance => {
|
||||
instance.on('plugins.message.placed', function (e) {
|
||||
if (e.element.parentElement.classList.contains('input-group')) {
|
||||
e.element.parentElement.insertAdjacentElement('afterend', e.messageElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// FlatPickr Initialization & Validation
|
||||
const flatpickrDate = document.querySelector('[name="basicDate"]');
|
||||
|
||||
if (flatpickrDate) {
|
||||
flatpickrDate.flatpickr({
|
||||
enableTime: false,
|
||||
// See https://flatpickr.js.org/formatting/
|
||||
dateFormat: 'm/d/Y',
|
||||
// After selecting a date, we need to revalidate the field
|
||||
onChange: function () {
|
||||
fv.revalidateField('basicDate');
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
// datatable (jquery)
|
||||
$(function () {
|
||||
var dt_basic_table = $('.datatables-basic'),
|
||||
dt_complex_header_table = $('.dt-complex-header'),
|
||||
dt_row_grouping_table = $('.dt-row-grouping'),
|
||||
dt_multilingual_table = $('.dt-multilingual'),
|
||||
dt_basic;
|
||||
|
||||
// DataTable with buttons
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
if (dt_basic_table.length) {
|
||||
dt_basic = dt_basic_table.DataTable({
|
||||
ajax: assetsPath + 'json/table-datatable.json',
|
||||
columns: [
|
||||
{ data: '' },
|
||||
{ data: 'id' },
|
||||
{ data: 'id' },
|
||||
{ data: 'full_name' },
|
||||
{ data: 'email' },
|
||||
{ data: 'start_date' },
|
||||
{ data: 'salary' },
|
||||
{ data: 'status' },
|
||||
{ data: '' }
|
||||
],
|
||||
columnDefs: [
|
||||
{
|
||||
// For Responsive
|
||||
className: 'control',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
responsivePriority: 2,
|
||||
targets: 0,
|
||||
render: function (data, type, full, meta) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
// For Checkboxes
|
||||
targets: 1,
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
responsivePriority: 3,
|
||||
checkboxes: true,
|
||||
render: function () {
|
||||
return '<input type="checkbox" class="dt-checkboxes form-check-input">';
|
||||
},
|
||||
checkboxes: {
|
||||
selectAllRender: '<input type="checkbox" class="form-check-input">'
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: 2,
|
||||
searchable: false,
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
// Avatar image/badge, Name and post
|
||||
targets: 3,
|
||||
responsivePriority: 4,
|
||||
render: function (data, type, full, meta) {
|
||||
var $user_img = full['avatar'],
|
||||
$name = full['full_name'],
|
||||
$post = full['post'];
|
||||
if ($user_img) {
|
||||
// For Avatar image
|
||||
var $output =
|
||||
'<img src="' + assetsPath + 'img/avatars/' + $user_img + '" alt="Avatar" class="rounded-circle">';
|
||||
} else {
|
||||
// For Avatar badge
|
||||
var stateNum = Math.floor(Math.random() * 6);
|
||||
var states = ['success', 'danger', 'warning', 'info', 'dark', 'primary', 'secondary'];
|
||||
var $state = states[stateNum],
|
||||
$name = full['full_name'],
|
||||
$initials = $name.match(/\b\w/g) || [];
|
||||
$initials = (($initials.shift() || '') + ($initials.pop() || '')).toUpperCase();
|
||||
$output = '<span class="avatar-initial rounded-circle bg-label-' + $state + '">' + $initials + '</span>';
|
||||
}
|
||||
// Creates full output for row
|
||||
var $row_output =
|
||||
'<div class="d-flex justify-content-start align-items-center user-name">' +
|
||||
'<div class="avatar-wrapper">' +
|
||||
'<div class="avatar me-2">' +
|
||||
$output +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="d-flex flex-column">' +
|
||||
'<span class="emp_name text-truncate">' +
|
||||
$name +
|
||||
'</span>' +
|
||||
'<small class="emp_post text-truncate text-muted">' +
|
||||
$post +
|
||||
'</small>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
return $row_output;
|
||||
}
|
||||
},
|
||||
{
|
||||
responsivePriority: 1,
|
||||
targets: 4
|
||||
},
|
||||
{
|
||||
// Label
|
||||
targets: -2,
|
||||
render: function (data, type, full, meta) {
|
||||
var $status_number = full['status'];
|
||||
var $status = {
|
||||
1: { title: 'Current', class: 'bg-label-primary' },
|
||||
2: { title: 'Professional', class: ' bg-label-success' },
|
||||
3: { title: 'Rejected', class: ' bg-label-danger' },
|
||||
4: { title: 'Resigned', class: ' bg-label-warning' },
|
||||
5: { title: 'Applied', class: ' bg-label-info' }
|
||||
};
|
||||
if (typeof $status[$status_number] === 'undefined') {
|
||||
return data;
|
||||
}
|
||||
return (
|
||||
'<span class="badge ' + $status[$status_number].class + '">' + $status[$status_number].title + '</span>'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
// Actions
|
||||
targets: -1,
|
||||
title: 'Actions',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
render: function (data, type, full, meta) {
|
||||
return (
|
||||
'<div class="d-inline-block">' +
|
||||
'<a href="javascript:;" class="btn btn-icon dropdown-toggle hide-arrow me-1" data-bs-toggle="dropdown"><i class="bx bx-dots-vertical-rounded bx-md"></i></a>' +
|
||||
'<ul class="dropdown-menu dropdown-menu-end m-0">' +
|
||||
'<li><a href="javascript:;" class="dropdown-item">Details</a></li>' +
|
||||
'<li><a href="javascript:;" class="dropdown-item">Archive</a></li>' +
|
||||
'<div class="dropdown-divider"></div>' +
|
||||
'<li><a href="javascript:;" class="dropdown-item text-danger delete-record">Delete</a></li>' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'<a href="javascript:;" class="btn btn-icon item-edit"><i class="bx bx-edit bx-md"></i></a>'
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
order: [[2, 'desc']],
|
||||
dom: '<"card-header flex-column flex-md-row pb-0"<"head-label text-center"><"dt-action-buttons text-end pt-6 pt-md-0"B>><"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6 d-flex justify-content-center justify-content-md-end mt-n6 mt-md-0"f>>t<"row"<"col-sm-12 col-md-6"i><"col-sm-12 col-md-6"p>>',
|
||||
displayLength: 7,
|
||||
lengthMenu: [7, 10, 25, 50, 75, 100],
|
||||
language: {
|
||||
paginate: {
|
||||
next: '<i class="bx bx-chevron-right bx-18px"></i>',
|
||||
previous: '<i class="bx bx-chevron-left bx-18px"></i>'
|
||||
}
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
extend: 'collection',
|
||||
className: 'btn btn-label-primary dropdown-toggle me-4',
|
||||
text: '<i class="bx bx-export bx-sm me-sm-2"></i> <span class="d-none d-sm-inline-block">Export</span>',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'print',
|
||||
text: '<i class="bx bx-printer me-1" ></i>Print',
|
||||
className: 'dropdown-item',
|
||||
exportOptions: {
|
||||
columns: [3, 4, 5, 6, 7],
|
||||
// prevent avatar to be display
|
||||
format: {
|
||||
body: function (inner, coldex, rowdex) {
|
||||
if (inner.length <= 0) return inner;
|
||||
var el = $.parseHTML(inner);
|
||||
var result = '';
|
||||
$.each(el, function (index, item) {
|
||||
if (item.classList !== undefined && item.classList.contains('user-name')) {
|
||||
result = result + item.lastChild.firstChild.textContent;
|
||||
} else if (item.innerText === undefined) {
|
||||
result = result + item.textContent;
|
||||
} else result = result + item.innerText;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
},
|
||||
customize: function (win) {
|
||||
//customize print view for dark
|
||||
$(win.document.body)
|
||||
.css('color', config.colors.headingColor)
|
||||
.css('border-color', config.colors.borderColor)
|
||||
.css('background-color', config.colors.bodyBg);
|
||||
$(win.document.body)
|
||||
.find('table')
|
||||
.addClass('compact')
|
||||
.css('color', 'inherit')
|
||||
.css('border-color', 'inherit')
|
||||
.css('background-color', 'inherit');
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="bx bx-file me-1" ></i>Csv',
|
||||
className: 'dropdown-item',
|
||||
exportOptions: {
|
||||
columns: [3, 4, 5, 6, 7],
|
||||
// prevent avatar to be display
|
||||
format: {
|
||||
body: function (inner, coldex, rowdex) {
|
||||
if (inner.length <= 0) return inner;
|
||||
var el = $.parseHTML(inner);
|
||||
var result = '';
|
||||
$.each(el, function (index, item) {
|
||||
if (item.classList !== undefined && item.classList.contains('user-name')) {
|
||||
result = result + item.lastChild.firstChild.textContent;
|
||||
} else if (item.innerText === undefined) {
|
||||
result = result + item.textContent;
|
||||
} else result = result + item.innerText;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'excel',
|
||||
text: '<i class="bx bxs-file-export me-1"></i>Excel',
|
||||
className: 'dropdown-item',
|
||||
exportOptions: {
|
||||
columns: [3, 4, 5, 6, 7],
|
||||
// prevent avatar to be display
|
||||
format: {
|
||||
body: function (inner, coldex, rowdex) {
|
||||
if (inner.length <= 0) return inner;
|
||||
var el = $.parseHTML(inner);
|
||||
var result = '';
|
||||
$.each(el, function (index, item) {
|
||||
if (item.classList !== undefined && item.classList.contains('user-name')) {
|
||||
result = result + item.lastChild.firstChild.textContent;
|
||||
} else if (item.innerText === undefined) {
|
||||
result = result + item.textContent;
|
||||
} else result = result + item.innerText;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'pdf',
|
||||
text: '<i class="bx bxs-file-pdf me-1"></i>Pdf',
|
||||
className: 'dropdown-item',
|
||||
exportOptions: {
|
||||
columns: [3, 4, 5, 6, 7],
|
||||
// prevent avatar to be display
|
||||
format: {
|
||||
body: function (inner, coldex, rowdex) {
|
||||
if (inner.length <= 0) return inner;
|
||||
var el = $.parseHTML(inner);
|
||||
var result = '';
|
||||
$.each(el, function (index, item) {
|
||||
if (item.classList !== undefined && item.classList.contains('user-name')) {
|
||||
result = result + item.lastChild.firstChild.textContent;
|
||||
} else if (item.innerText === undefined) {
|
||||
result = result + item.textContent;
|
||||
} else result = result + item.innerText;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'copy',
|
||||
text: '<i class="bx bx-copy me-1" ></i>Copy',
|
||||
className: 'dropdown-item',
|
||||
exportOptions: {
|
||||
columns: [3, 4, 5, 6, 7],
|
||||
// prevent avatar to be display
|
||||
format: {
|
||||
body: function (inner, coldex, rowdex) {
|
||||
if (inner.length <= 0) return inner;
|
||||
var el = $.parseHTML(inner);
|
||||
var result = '';
|
||||
$.each(el, function (index, item) {
|
||||
if (item.classList !== undefined && item.classList.contains('user-name')) {
|
||||
result = result + item.lastChild.firstChild.textContent;
|
||||
} else if (item.innerText === undefined) {
|
||||
result = result + item.textContent;
|
||||
} else result = result + item.innerText;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: '<i class="bx bx-plus bx-sm me-sm-2"></i> <span class="d-none d-sm-inline-block">Add New Record</span>',
|
||||
className: 'create-new btn btn-primary'
|
||||
}
|
||||
],
|
||||
responsive: {
|
||||
details: {
|
||||
display: $.fn.dataTable.Responsive.display.modal({
|
||||
header: function (row) {
|
||||
var data = row.data();
|
||||
return 'Details of ' + data['full_name'];
|
||||
}
|
||||
}),
|
||||
type: 'column',
|
||||
renderer: function (api, rowIdx, columns) {
|
||||
var data = $.map(columns, function (col, i) {
|
||||
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
|
||||
? '<tr data-dt-row="' +
|
||||
col.rowIndex +
|
||||
'" data-dt-column="' +
|
||||
col.columnIndex +
|
||||
'">' +
|
||||
'<td>' +
|
||||
col.title +
|
||||
':' +
|
||||
'</td> ' +
|
||||
'<td>' +
|
||||
col.data +
|
||||
'</td>' +
|
||||
'</tr>'
|
||||
: '';
|
||||
}).join('');
|
||||
|
||||
return data ? $('<table class="table"/><tbody />').append(data) : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$('div.head-label').html('<h5 class="card-title mb-0">DataTable with Buttons</h5>');
|
||||
// To remove default btn-secondary in export buttons
|
||||
$('.dt-buttons > .btn-group > button').removeClass('btn-secondary');
|
||||
}
|
||||
|
||||
// Add New record
|
||||
// ? Remove/Update this code as per your requirements
|
||||
var count = 101;
|
||||
// On form submit, if form is valid
|
||||
fv.on('core.form.valid', function () {
|
||||
var $new_name = $('.add-new-record .dt-full-name').val(),
|
||||
$new_post = $('.add-new-record .dt-post').val(),
|
||||
$new_email = $('.add-new-record .dt-email').val(),
|
||||
$new_date = $('.add-new-record .dt-date').val(),
|
||||
$new_salary = $('.add-new-record .dt-salary').val();
|
||||
|
||||
if ($new_name != '') {
|
||||
dt_basic.row
|
||||
.add({
|
||||
id: count,
|
||||
full_name: $new_name,
|
||||
post: $new_post,
|
||||
email: $new_email,
|
||||
start_date: $new_date,
|
||||
salary: '$' + $new_salary,
|
||||
status: 5
|
||||
})
|
||||
.draw();
|
||||
count++;
|
||||
|
||||
// Hide offcanvas using javascript method
|
||||
offCanvasEl.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Delete Record
|
||||
$('.datatables-basic tbody').on('click', '.delete-record', function () {
|
||||
dt_basic.row($(this).parents('tr')).remove().draw();
|
||||
});
|
||||
|
||||
// Complex Header DataTable
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
if (dt_complex_header_table.length) {
|
||||
var dt_complex = dt_complex_header_table.DataTable({
|
||||
ajax: assetsPath + 'json/table-datatable.json',
|
||||
columns: [
|
||||
{ data: 'full_name' },
|
||||
{ data: 'email' },
|
||||
{ data: 'city' },
|
||||
{ data: 'post' },
|
||||
{ data: 'salary' },
|
||||
{ data: 'status' },
|
||||
{ data: '' }
|
||||
],
|
||||
columnDefs: [
|
||||
{
|
||||
// Label
|
||||
targets: -2,
|
||||
render: function (data, type, full, meta) {
|
||||
var $status_number = full['status'];
|
||||
var $status = {
|
||||
1: { title: 'Current', class: 'bg-label-primary' },
|
||||
2: { title: 'Professional', class: ' bg-label-success' },
|
||||
3: { title: 'Rejected', class: ' bg-label-danger' },
|
||||
4: { title: 'Resigned', class: ' bg-label-warning' },
|
||||
5: { title: 'Applied', class: ' bg-label-info' }
|
||||
};
|
||||
if (typeof $status[$status_number] === 'undefined') {
|
||||
return data;
|
||||
}
|
||||
return (
|
||||
'<span class="badge ' + $status[$status_number].class + '">' + $status[$status_number].title + '</span>'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
// Actions
|
||||
targets: -1,
|
||||
title: 'Actions',
|
||||
orderable: false,
|
||||
render: function (data, type, full, meta) {
|
||||
return (
|
||||
'<div class="d-inline-block">' +
|
||||
'<a href="javascript:;" class="btn btn-icon dropdown-toggle hide-arrow me-1" data-bs-toggle="dropdown"><i class="bx bx-dots-vertical-rounded bx-md"></i></a>' +
|
||||
'<div class="dropdown-menu dropdown-menu-end m-0">' +
|
||||
'<a href="javascript:;" class="dropdown-item">Details</a>' +
|
||||
'<a href="javascript:;" class="dropdown-item">Archive</a>' +
|
||||
'<div class="dropdown-divider"></div>' +
|
||||
'<a href="javascript:;" class="dropdown-item text-danger delete-record">Delete</a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a href="javascript:;" class="btn btn-icon item-edit"><i class="bx bx-edit bx-md"></i></a>'
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
dom: '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6 d-flex justify-content-center justify-content-md-end mt-n6 mt-md-0"f>><"table-responsive"t><"row"<"col-sm-12 col-md-6"i><"col-sm-12 col-md-6"p>>',
|
||||
displayLength: 7,
|
||||
lengthMenu: [7, 10, 25, 50, 75, 100],
|
||||
language: {
|
||||
paginate: {
|
||||
next: '<i class="bx bx-chevron-right bx-18px"></i>',
|
||||
previous: '<i class="bx bx-chevron-left bx-18px"></i>'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Row Grouping
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
var groupColumn = 2;
|
||||
if (dt_row_grouping_table.length) {
|
||||
var groupingTable = dt_row_grouping_table.DataTable({
|
||||
ajax: assetsPath + 'json/table-datatable.json',
|
||||
columns: [
|
||||
{ data: '' },
|
||||
{ data: 'full_name' },
|
||||
{ data: 'post' },
|
||||
{ data: 'email' },
|
||||
{ data: 'city' },
|
||||
{ data: 'start_date' },
|
||||
{ data: 'salary' },
|
||||
{ data: 'status' },
|
||||
{ data: '' }
|
||||
],
|
||||
columnDefs: [
|
||||
{
|
||||
// For Responsive
|
||||
className: 'control',
|
||||
orderable: false,
|
||||
targets: 0,
|
||||
searchable: false,
|
||||
render: function (data, type, full, meta) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{ visible: false, targets: groupColumn },
|
||||
{
|
||||
// Label
|
||||
targets: -2,
|
||||
render: function (data, type, full, meta) {
|
||||
var $status_number = full['status'];
|
||||
var $status = {
|
||||
1: { title: 'Current', class: 'bg-label-primary' },
|
||||
2: { title: 'Professional', class: ' bg-label-success' },
|
||||
3: { title: 'Rejected', class: ' bg-label-danger' },
|
||||
4: { title: 'Resigned', class: ' bg-label-warning' },
|
||||
5: { title: 'Applied', class: ' bg-label-info' }
|
||||
};
|
||||
if (typeof $status[$status_number] === 'undefined') {
|
||||
return data;
|
||||
}
|
||||
return (
|
||||
'<span class="badge ' + $status[$status_number].class + '">' + $status[$status_number].title + '</span>'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
// Actions
|
||||
targets: -1,
|
||||
title: 'Actions',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
render: function (data, type, full, meta) {
|
||||
return (
|
||||
'<div class="d-inline-block">' +
|
||||
'<a href="javascript:;" class="btn btn-icon dropdown-toggle hide-arrow me-1" data-bs-toggle="dropdown"><i class="bx bx-dots-vertical-rounded bx-sm"></i></a>' +
|
||||
'<div class="dropdown-menu dropdown-menu-end m-0">' +
|
||||
'<a href="javascript:;" class="dropdown-item">Details</a>' +
|
||||
'<a href="javascript:;" class="dropdown-item">Archive</a>' +
|
||||
'<div class="dropdown-divider"></div>' +
|
||||
'<a href="javascript:;" class="dropdown-item text-danger delete-record">Delete</a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a href="javascript:;" class="btn btn-icon item-edit"><i class="bx bx-edit bx-sm"></i></a>'
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
order: [[groupColumn, 'asc']],
|
||||
dom: '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6 d-flex justify-content-center justify-content-md-end mt-n6 mt-md-0"f>>t<"row"<"col-sm-12 col-md-6"i><"col-sm-12 col-md-6"p>>',
|
||||
displayLength: 7,
|
||||
lengthMenu: [7, 10, 25, 50, 75, 100],
|
||||
language: {
|
||||
paginate: {
|
||||
next: '<i class="bx bx-chevron-right bx-18px"></i>',
|
||||
previous: '<i class="bx bx-chevron-left bx-18px"></i>'
|
||||
}
|
||||
},
|
||||
drawCallback: function (settings) {
|
||||
var api = this.api();
|
||||
var rows = api.rows({ page: 'current' }).nodes();
|
||||
var last = null;
|
||||
|
||||
api
|
||||
.column(groupColumn, { page: 'current' })
|
||||
.data()
|
||||
.each(function (group, i) {
|
||||
if (last !== group) {
|
||||
$(rows)
|
||||
.eq(i)
|
||||
.before('<tr class="group"><td colspan="8">' + group + '</td></tr>');
|
||||
|
||||
last = group;
|
||||
}
|
||||
});
|
||||
},
|
||||
responsive: {
|
||||
details: {
|
||||
display: $.fn.dataTable.Responsive.display.modal({
|
||||
header: function (row) {
|
||||
var data = row.data();
|
||||
return 'Details of ' + data['full_name'];
|
||||
}
|
||||
}),
|
||||
type: 'column',
|
||||
renderer: function (api, rowIdx, columns) {
|
||||
var data = $.map(columns, function (col, i) {
|
||||
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
|
||||
? '<tr data-dt-row="' +
|
||||
col.rowIndex +
|
||||
'" data-dt-column="' +
|
||||
col.columnIndex +
|
||||
'">' +
|
||||
'<td>' +
|
||||
col.title +
|
||||
':' +
|
||||
'</td> ' +
|
||||
'<td>' +
|
||||
col.data +
|
||||
'</td>' +
|
||||
'</tr>'
|
||||
: '';
|
||||
}).join('');
|
||||
|
||||
return data ? $('<table class="table"/><tbody />').append(data) : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Order by the grouping
|
||||
$('.dt-row-grouping tbody').on('click', 'tr.group', function () {
|
||||
var currentOrder = groupingTable.order()[0];
|
||||
if (currentOrder[0] === groupColumn && currentOrder[1] === 'asc') {
|
||||
groupingTable.order([groupColumn, 'desc']).draw();
|
||||
} else {
|
||||
groupingTable.order([groupColumn, 'asc']).draw();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Multilingual DataTable
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
var lang = 'German';
|
||||
if (dt_multilingual_table.length) {
|
||||
var table_language = dt_multilingual_table.DataTable({
|
||||
ajax: assetsPath + 'json/table-datatable.json',
|
||||
columns: [
|
||||
{ data: '' },
|
||||
{ data: 'full_name' },
|
||||
{ data: 'post' },
|
||||
{ data: 'email' },
|
||||
{ data: 'start_date' },
|
||||
{ data: 'salary' },
|
||||
{ data: 'status' },
|
||||
{ data: '' }
|
||||
],
|
||||
columnDefs: [
|
||||
{
|
||||
// For Responsive
|
||||
className: 'control',
|
||||
orderable: false,
|
||||
targets: 0,
|
||||
searchable: false,
|
||||
render: function (data, type, full, meta) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
// Label
|
||||
targets: -2,
|
||||
render: function (data, type, full, meta) {
|
||||
var $status_number = full['status'];
|
||||
var $status = {
|
||||
1: { title: 'Current', class: 'bg-label-primary' },
|
||||
2: { title: 'Professional', class: ' bg-label-success' },
|
||||
3: { title: 'Rejected', class: ' bg-label-danger' },
|
||||
4: { title: 'Resigned', class: ' bg-label-warning' },
|
||||
5: { title: 'Applied', class: ' bg-label-info' }
|
||||
};
|
||||
if (typeof $status[$status_number] === 'undefined') {
|
||||
return data;
|
||||
}
|
||||
return (
|
||||
'<span class="badge ' + $status[$status_number].class + '">' + $status[$status_number].title + '</span>'
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
// Actions
|
||||
targets: -1,
|
||||
title: 'Actions',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
render: function (data, type, full, meta) {
|
||||
return (
|
||||
'<div class="d-inline-block">' +
|
||||
'<a href="javascript:;" class="btn btn-icon dropdown-toggle hide-arrow me-1" data-bs-toggle="dropdown"><i class="bx bx-dots-vertical-rounded bx-sm"></i></a>' +
|
||||
'<div class="dropdown-menu dropdown-menu-end m-0">' +
|
||||
'<a href="javascript:;" class="dropdown-item">Details</a>' +
|
||||
'<a href="javascript:;" class="dropdown-item">Archive</a>' +
|
||||
'<div class="dropdown-divider"></div>' +
|
||||
'<a href="javascript:;" class="dropdown-item text-danger delete-record">Delete</a>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<a href="javascript:;" class="btn btn-icon item-edit"><i class="bx bx-edit bx-sm"></i></a>'
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
language: {
|
||||
url: '//cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/' + lang + '.json',
|
||||
paginate: {
|
||||
next: '<i class="bx bx-chevron-right bx-18px"></i>',
|
||||
previous: '<i class="bx bx-chevron-left bx-18px"></i>'
|
||||
}
|
||||
},
|
||||
order: [[2, 'desc']],
|
||||
displayLength: 7,
|
||||
dom: '<"row"<"col-sm-12 col-md-6 ps-md-4"l><"col-sm-12 col-md-6 d-flex justify-content-center justify-content-md-end mt-n6 mt-md-0"f>>t<"row"<"col-sm-12 col-md-6"i><"col-sm-12 col-md-6"p>>',
|
||||
lengthMenu: [7, 10, 25, 50, 75, 100],
|
||||
responsive: {
|
||||
details: {
|
||||
display: $.fn.dataTable.Responsive.display.modal({
|
||||
header: function (row) {
|
||||
var data = row.data();
|
||||
return 'Details of ' + data['full_name'];
|
||||
}
|
||||
}),
|
||||
type: 'column',
|
||||
renderer: function (api, rowIdx, columns) {
|
||||
var data = $.map(columns, function (col, i) {
|
||||
return col.title !== '' // ? Do not show row in modal popup if title is blank (for check box)
|
||||
? '<tr data-dt-row="' +
|
||||
col.rowIndex +
|
||||
'" data-dt-column="' +
|
||||
col.columnIndex +
|
||||
'">' +
|
||||
'<td>' +
|
||||
col.title +
|
||||
':' +
|
||||
'</td> ' +
|
||||
'<td>' +
|
||||
col.data +
|
||||
'</td>' +
|
||||
'</tr>'
|
||||
: '';
|
||||
}).join('');
|
||||
|
||||
return data ? $('<table class="table"/><tbody />').append(data) : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Filter form control to default size
|
||||
// ? setTimeout used for multilingual table initialization
|
||||
setTimeout(() => {
|
||||
$('.dataTables_filter .form-control').removeClass('form-control-sm');
|
||||
$('.dataTables_length .form-select').removeClass('form-select-sm');
|
||||
}, 300);
|
||||
});
|
3704
public/assets/vendor/libs/animate-css/animate.css
vendored
Normal file
3704
public/assets/vendor/libs/animate-css/animate.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
347
public/assets/vendor/libs/datatables-bs5/datatables-bootstrap5.js
vendored
Normal file
347
public/assets/vendor/libs/datatables-bs5/datatables-bootstrap5.js
vendored
Normal file
File diff suppressed because one or more lines are too long
817
public/assets/vendor/libs/datatables-bs5/datatables.bootstrap5.css
vendored
Normal file
817
public/assets/vendor/libs/datatables-bs5/datatables.bootstrap5.css
vendored
Normal file
@ -0,0 +1,817 @@
|
||||
@charset "UTF-8";
|
||||
:root {
|
||||
--dt-row-selected: 13, 110, 253;
|
||||
--dt-row-selected-text: 255, 255, 255;
|
||||
--dt-row-selected-link: 9, 10, 11;
|
||||
--dt-row-stripe: 0, 0, 0;
|
||||
--dt-row-hover: 0, 0, 0;
|
||||
--dt-column-ordering: 0, 0, 0;
|
||||
--dt-html-background: white;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
--dt-html-background: rgb(33, 37, 41);
|
||||
}
|
||||
|
||||
table.dataTable td.dt-control {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table.dataTable td.dt-control:before {
|
||||
display: inline-block;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
content: "▶";
|
||||
}
|
||||
|
||||
table.dataTable tr.dt-hasChild td.dt-control:before {
|
||||
content: "▼";
|
||||
}
|
||||
|
||||
html.dark table.dataTable td.dt-control:before,
|
||||
:root[data-bs-theme=dark] table.dataTable td.dt-control:before {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
html.dark table.dataTable tr.dt-hasChild td.dt-control:before,
|
||||
:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,
|
||||
table.dataTable thead > tr > td.sorting,
|
||||
table.dataTable thead > tr > td.sorting_asc,
|
||||
table.dataTable thead > tr > td.sorting_desc,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding-right: 26px;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting:before,
|
||||
table.dataTable thead > tr > td.sorting:after,
|
||||
table.dataTable thead > tr > td.sorting_asc:before,
|
||||
table.dataTable thead > tr > td.sorting_asc:after,
|
||||
table.dataTable thead > tr > td.sorting_desc:before,
|
||||
table.dataTable thead > tr > td.sorting_desc:after,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
opacity: 0.125;
|
||||
right: 10px;
|
||||
line-height: 9px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting:before,
|
||||
table.dataTable thead > tr > td.sorting_asc:before,
|
||||
table.dataTable thead > tr > td.sorting_desc:before,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:before {
|
||||
bottom: 50%;
|
||||
content: "▲";
|
||||
content: "▲"/"";
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting:after,
|
||||
table.dataTable thead > tr > td.sorting_asc:after,
|
||||
table.dataTable thead > tr > td.sorting_desc:after,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:after {
|
||||
top: 50%;
|
||||
content: "▼";
|
||||
content: "▼"/"";
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after,
|
||||
table.dataTable thead > tr > td.sorting_asc:before,
|
||||
table.dataTable thead > tr > td.sorting_desc:after {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled:after,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th:active,
|
||||
table.dataTable thead > tr > td:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody > table.dataTable > thead > tr > th:before, div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,
|
||||
div.dataTables_scrollBody > table.dataTable > thead > tr > td:before,
|
||||
div.dataTables_scrollBody > table.dataTable > thead > tr > td:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.dataTables_processing {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
margin-left: -100px;
|
||||
margin-top: -26px;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
div.dataTables_processing > div:last-child {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 15px;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
div.dataTables_processing > div:last-child > div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
background: rgb(13, 110, 253);
|
||||
background: rgb(var(--dt-row-selected));
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
|
||||
div.dataTables_processing > div:last-child > div:nth-child(1) {
|
||||
left: 8px;
|
||||
animation: datatables-loader-1 0.6s infinite;
|
||||
}
|
||||
|
||||
div.dataTables_processing > div:last-child > div:nth-child(2) {
|
||||
left: 8px;
|
||||
animation: datatables-loader-2 0.6s infinite;
|
||||
}
|
||||
|
||||
div.dataTables_processing > div:last-child > div:nth-child(3) {
|
||||
left: 32px;
|
||||
animation: datatables-loader-2 0.6s infinite;
|
||||
}
|
||||
|
||||
div.dataTables_processing > div:last-child > div:nth-child(4) {
|
||||
left: 56px;
|
||||
animation: datatables-loader-3 0.6s infinite;
|
||||
}
|
||||
|
||||
@keyframes datatables-loader-1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes datatables-loader-3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
@keyframes datatables-loader-2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate(24px, 0);
|
||||
}
|
||||
}
|
||||
table.dataTable.nowrap th, table.dataTable.nowrap td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.dataTable th.dt-left,
|
||||
table.dataTable td.dt-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.dataTable th.dt-center,
|
||||
table.dataTable td.dt-center,
|
||||
table.dataTable td.dataTables_empty {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.dataTable th.dt-right,
|
||||
table.dataTable td.dt-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.dataTable th.dt-justify,
|
||||
table.dataTable td.dt-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
table.dataTable th.dt-nowrap,
|
||||
table.dataTable td.dt-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.dataTable thead th,
|
||||
table.dataTable thead td,
|
||||
table.dataTable tfoot th,
|
||||
table.dataTable tfoot td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.dataTable thead th.dt-head-left,
|
||||
table.dataTable thead td.dt-head-left,
|
||||
table.dataTable tfoot th.dt-head-left,
|
||||
table.dataTable tfoot td.dt-head-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.dataTable thead th.dt-head-center,
|
||||
table.dataTable thead td.dt-head-center,
|
||||
table.dataTable tfoot th.dt-head-center,
|
||||
table.dataTable tfoot td.dt-head-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.dataTable thead th.dt-head-right,
|
||||
table.dataTable thead td.dt-head-right,
|
||||
table.dataTable tfoot th.dt-head-right,
|
||||
table.dataTable tfoot td.dt-head-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.dataTable thead th.dt-head-justify,
|
||||
table.dataTable thead td.dt-head-justify,
|
||||
table.dataTable tfoot th.dt-head-justify,
|
||||
table.dataTable tfoot td.dt-head-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
table.dataTable thead th.dt-head-nowrap,
|
||||
table.dataTable thead td.dt-head-nowrap,
|
||||
table.dataTable tfoot th.dt-head-nowrap,
|
||||
table.dataTable tfoot td.dt-head-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.dataTable tbody th.dt-body-left,
|
||||
table.dataTable tbody td.dt-body-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.dataTable tbody th.dt-body-center,
|
||||
table.dataTable tbody td.dt-body-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.dataTable tbody th.dt-body-right,
|
||||
table.dataTable tbody td.dt-body-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.dataTable tbody th.dt-body-justify,
|
||||
table.dataTable tbody td.dt-body-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
table.dataTable tbody th.dt-body-nowrap,
|
||||
table.dataTable tbody td.dt-body-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/*! Bootstrap 5 integration for DataTables
|
||||
*
|
||||
* ©2020 SpryMedia Ltd, all rights reserved.
|
||||
* License: MIT datatables.net/license/mit
|
||||
*/
|
||||
table.dataTable {
|
||||
clear: both;
|
||||
margin-top: 6px !important;
|
||||
margin-bottom: 6px !important;
|
||||
max-width: none !important;
|
||||
border-collapse: separate !important;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
table.dataTable td,
|
||||
table.dataTable th {
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
table.dataTable td.dataTables_empty,
|
||||
table.dataTable th.dataTables_empty {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.dataTable.nowrap th,
|
||||
table.dataTable.nowrap td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.selected > * {
|
||||
box-shadow: inset 0 0 0 9999px rgb(13, 110, 253);
|
||||
box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));
|
||||
color: rgb(255, 255, 255);
|
||||
color: rgb(var(--dt-row-selected-text));
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.selected a {
|
||||
color: rgb(9, 10, 11);
|
||||
color: rgb(var(--dt-row-selected-link));
|
||||
}
|
||||
|
||||
table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);
|
||||
}
|
||||
|
||||
table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1).selected > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);
|
||||
}
|
||||
|
||||
table.dataTable.table-hover > tbody > tr:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);
|
||||
}
|
||||
|
||||
table.dataTable.table-hover > tbody > tr.selected:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_length label {
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_length select {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_filter {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_filter label {
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_filter input {
|
||||
margin-left: 0.5em;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_info {
|
||||
padding-top: 0.85em;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_paginate {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
|
||||
margin: 2px 0;
|
||||
white-space: nowrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dt-row {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.dataTables_scrollHead table.dataTable {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody > table {
|
||||
border-top: none;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody > table > thead .sorting:before,
|
||||
div.dataTables_scrollBody > table > thead .sorting_asc:before,
|
||||
div.dataTables_scrollBody > table > thead .sorting_desc:before,
|
||||
div.dataTables_scrollBody > table > thead .sorting:after,
|
||||
div.dataTables_scrollBody > table > thead .sorting_asc:after,
|
||||
div.dataTables_scrollBody > table > thead .sorting_desc:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody > table > tbody tr:first-child th,
|
||||
div.dataTables_scrollBody > table > tbody tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
div.dataTables_scrollFoot > .dataTables_scrollFootInner {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
div.dataTables_scrollFoot > .dataTables_scrollFootInner > table {
|
||||
margin-top: 0 !important;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dataTables_wrapper div.dataTables_length,
|
||||
div.dataTables_wrapper div.dataTables_filter,
|
||||
div.dataTables_wrapper div.dataTables_info,
|
||||
div.dataTables_wrapper div.dataTables_paginate {
|
||||
text-align: center;
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
|
||||
justify-content: center !important;
|
||||
}
|
||||
}
|
||||
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled):before, table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled):after {
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable {
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable thead tr:first-child th,
|
||||
table.table-bordered.dataTable thead tr:first-child td {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable th,
|
||||
table.table-bordered.dataTable td {
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable th:first-child, table.table-bordered.dataTable th:first-child,
|
||||
table.table-bordered.dataTable td:first-child,
|
||||
table.table-bordered.dataTable td:first-child {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,
|
||||
table.table-bordered.dataTable td:last-child,
|
||||
table.table-bordered.dataTable td:last-child {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable th,
|
||||
table.table-bordered.dataTable td {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
div.dataTables_scrollHead table.table-bordered {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
div.table-responsive > div.dataTables_wrapper > div.row {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
:root[data-bs-theme=dark] {
|
||||
--dt-row-hover: 255, 255, 255;
|
||||
--dt-row-stripe: 255, 255, 255;
|
||||
--dt-column-ordering: 255, 255, 255;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_length select {
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_filter input {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.dataTable .emp_name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper .card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_info {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
table.dataTable thead th.sorting_disabled::before, table.dataTable thead th.sorting_disabled::after {
|
||||
display: none !important;
|
||||
}
|
||||
table.dataTable thead th.sorting:before, table.dataTable thead th.sorting:after {
|
||||
visibility: hidden;
|
||||
}
|
||||
table.dataTable thead th.sorting:hover:before, table.dataTable thead th.sorting:hover:after {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
table.table-bordered.dataTable.dt-complex-header thead tr th, table.table-bordered.dataTable.dt-column-search thead tr th {
|
||||
border-width: 1px;
|
||||
}
|
||||
table.table-bordered.dataTable.dt-complex-header tfoot tr th, table.table-bordered.dataTable.dt-column-search tfoot tr th {
|
||||
border-width: 1px;
|
||||
}
|
||||
table.table-bordered.dataTable tfoot tr th {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
table.table-bordered.dataTable > :not(caption) > * > * {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
html:not([dir=rtl]) table.table-bordered.dataTable tr:first-child th:first-child,
|
||||
html:not([dir=rtl]) table.table-bordered.dataTable td:first-child {
|
||||
border-left-width: 0;
|
||||
}
|
||||
[dir=rtl] table.table-bordered.dataTable tr:first-child th:first-child,
|
||||
[dir=rtl] table.table-bordered.dataTable td:first-child {
|
||||
border-right-width: 0;
|
||||
}
|
||||
html:not([dir=rtl]) table.table-bordered.dataTable tr:first-child th:last-child,
|
||||
html:not([dir=rtl]) table.table-bordered.dataTable td:last-child {
|
||||
border-right-width: 0;
|
||||
}
|
||||
[dir=rtl] table.table-bordered.dataTable tr:first-child th:last-child,
|
||||
[dir=rtl] table.table-bordered.dataTable td:last-child {
|
||||
border-left-width: 0;
|
||||
}
|
||||
table.table-bordered.dataTable > tbody:not(caption) tr:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1399.98px) {
|
||||
table.table-responsive {
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
[dir=rtl] div.dataTables_wrapper .dataTables_filter {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
[dir=rtl] div.dataTables_wrapper .dataTables_filter input {
|
||||
margin-left: 0;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
[dir=rtl] table.table-bordered.dataTable th,
|
||||
[dir=rtl] table.table-bordered.dataTable td {
|
||||
border-right-width: 0;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
[dir=rtl] table.table-bordered.dataTable th:last-child,
|
||||
[dir=rtl] table.table-bordered.dataTable td:last-child {
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
table.dataTable {
|
||||
width: 100% !important;
|
||||
border-collapse: collapse !important;
|
||||
margin-bottom: 1rem !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
[dir=rtl] table.dataTable.table-sm > thead > tr > th {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
[dir=rtl] table.dataTable.table-sm .sorting:before, [dir=rtl] table.dataTable.table-sm .sorting_asc:before, [dir=rtl] table.dataTable.table-sm .sorting_desc:before {
|
||||
right: auto !important;
|
||||
left: 0.85em !important;
|
||||
}
|
||||
[dir=rtl] table.dataTable thead th,
|
||||
[dir=rtl] table.dataTable thead td,
|
||||
[dir=rtl] table.dataTable tfoot th,
|
||||
[dir=rtl] table.dataTable tfoot td {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable .form-check-input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.dataTables_scroll {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
table.dataTable thead th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table.dataTable thead .sorting,
|
||||
table.dataTable thead .sorting_asc,
|
||||
table.dataTable thead .sorting_desc,
|
||||
table.dataTable thead .sorting_asc_disabled,
|
||||
table.dataTable thead .sorting_desc_disabled {
|
||||
padding-right: inherit;
|
||||
}
|
||||
table.dataTable thead .sorting::before, table.dataTable thead .sorting::after,
|
||||
table.dataTable thead .sorting_asc::before,
|
||||
table.dataTable thead .sorting_asc::after,
|
||||
table.dataTable thead .sorting_desc::before,
|
||||
table.dataTable thead .sorting_desc::after,
|
||||
table.dataTable thead .sorting_asc_disabled::before,
|
||||
table.dataTable thead .sorting_asc_disabled::after,
|
||||
table.dataTable thead .sorting_desc_disabled::before,
|
||||
table.dataTable thead .sorting_desc_disabled::after {
|
||||
font-family: boxicons !important;
|
||||
font-weight: 500 !important;
|
||||
font-size: 1.2rem !important;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
content: "" !important;
|
||||
right: 0.7rem !important;
|
||||
}
|
||||
table.dataTable thead .sorting:before,
|
||||
table.dataTable thead .sorting_asc:before,
|
||||
table.dataTable thead .sorting_desc:before,
|
||||
table.dataTable thead .sorting_asc_disabled:before,
|
||||
table.dataTable thead .sorting_desc_disabled:before {
|
||||
top: 0.5rem !important;
|
||||
content: "\ea57" !important;
|
||||
}
|
||||
table.dataTable thead .sorting:after,
|
||||
table.dataTable thead .sorting_asc:after,
|
||||
table.dataTable thead .sorting_desc:after,
|
||||
table.dataTable thead .sorting_asc_disabled:after,
|
||||
table.dataTable thead .sorting_desc_disabled:after {
|
||||
bottom: 0.8rem !important;
|
||||
content: "\ea4a" !important;
|
||||
}
|
||||
[dir=rtl] table.dataTable thead .sorting::before,
|
||||
[dir=rtl] table.dataTable thead .sorting_asc::before,
|
||||
[dir=rtl] table.dataTable thead .sorting_desc::before,
|
||||
[dir=rtl] table.dataTable thead .sorting_asc_disabled::before,
|
||||
[dir=rtl] table.dataTable thead .sorting_desc_disabled::before {
|
||||
right: auto !important;
|
||||
left: 0.58em !important;
|
||||
}
|
||||
[dir=rtl] table.dataTable thead .sorting::after,
|
||||
[dir=rtl] table.dataTable thead .sorting_asc::after,
|
||||
[dir=rtl] table.dataTable thead .sorting_desc::after,
|
||||
[dir=rtl] table.dataTable thead .sorting_asc_disabled::after,
|
||||
[dir=rtl] table.dataTable thead .sorting_desc_disabled::after {
|
||||
right: auto !important;
|
||||
left: 0.58em !important;
|
||||
}
|
||||
|
||||
div.card-datatable.dataTable,
|
||||
div.card-datatable .dataTable {
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 575.98px) {
|
||||
div.dataTables_wrapper .card-header {
|
||||
display: block;
|
||||
}
|
||||
.dtr-bs-modal.modal .modal-body {
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
.dataTable_select div.dataTables_wrapper div.dataTables_info {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 767.98px) {
|
||||
div.dataTables_wrapper div.dataTables_info {
|
||||
padding-bottom: 0.782rem;
|
||||
}
|
||||
}
|
||||
div.dataTables_wrapper div.dataTables_length,
|
||||
div.dataTables_wrapper .dataTables_filter {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item .page-link, div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.next .page-link, div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.previous .page-link, div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.first .page-link, div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-item.last .page-link {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_paginate ul.pagination .page-link div:not(.table-responsive) div.dataTables_wrapper .dataTables_paginate {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
div.dataTables_wrapper div.dataTables_length label,
|
||||
div.dataTables_wrapper div.dataTables_filter label,
|
||||
div.dataTables_wrapper div.dataTables_info,
|
||||
div.dataTables_wrapper div.dataTables_paginate {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
|
||||
justify-content: start !important;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
}
|
||||
div.card-datatable {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
div.card-datatable [class*=col-md-] {
|
||||
padding-right: 1.5rem !important;
|
||||
padding-left: 1.5rem !important;
|
||||
}
|
||||
div.card-datatable:not(.table-responsive) .dataTables_wrapper .row:first-child, div.card-datatable:not(.table-responsive) .dataTables_wrapper .row:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html:not([dir=rtl]) div.card-datatable table.dataTable thead th:first-child,
|
||||
html:not([dir=rtl]) div.card-datatable table.dataTable tfoot th:first-child {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
html:not([dir=rtl]) div.card-datatable table.dataTable thead th:last-child,
|
||||
html:not([dir=rtl]) div.card-datatable table.dataTable tfoot th:last-child {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
html:not([dir=rtl]) div.card-datatable table.dataTable tbody td:first-child {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
[dir=rtl] table.dataTable.table-sm > thead > tr > th {
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
[dir=rtl] table.table-bordered.dataTable tr td,
|
||||
[dir=rtl] table.table-bordered.dataTable tr th,
|
||||
[dir=rtl] table.table-bordered.dataTable tr th:first-child {
|
||||
border-left-width: 0 !important;
|
||||
}
|
||||
[dir=rtl] table.dataTable thead th,
|
||||
[dir=rtl] table.dataTable tbody td,
|
||||
[dir=rtl] table.dataTable tfoot th {
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
[dir=rtl] table.dataTable.table-sm thead th, [dir=rtl] table.dataTable.table-sm tbody td, [dir=rtl] table.dataTable.table-sm tfoot th {
|
||||
padding-right: 1.25rem;
|
||||
}
|
||||
[dir=rtl] div.card-datatable table.dataTable thead th:first-child,
|
||||
[dir=rtl] div.card-datatable table.dataTable tbody td:first-child,
|
||||
[dir=rtl] div.card-datatable table.dataTable tfoot th:first-child {
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
[dir=rtl] div.card-datatable table.dataTable thead th:last-child,
|
||||
[dir=rtl] div.card-datatable table.dataTable tbody td:last-child,
|
||||
[dir=rtl] div.card-datatable table.dataTable tfoot th:last-child {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.light-style div.dataTables_wrapper div.dataTables_info {
|
||||
color: #a7acb2;
|
||||
}
|
||||
.light-style div.dataTables_scrollBody table {
|
||||
border-top-color: #e4e6e8;
|
||||
}
|
||||
.light-style table.dataTable th,
|
||||
.light-style table.dataTable td {
|
||||
border-color: #e4e6e8 !important;
|
||||
}
|
||||
|
||||
.dark-style div.dataTables_wrapper div.dataTables_info {
|
||||
color: #7e7f96;
|
||||
}
|
||||
.dark-style div.dataTables_scrollBody table {
|
||||
border-top-color: #4e4f6c;
|
||||
}
|
||||
.dark-style table.dataTable th,
|
||||
.dark-style table.dataTable td {
|
||||
border-color: #4e4f6c !important;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.dt-print-view h1 {
|
||||
color: #232333;
|
||||
}
|
||||
.dt-print-view .table > :not(caption) > * > * {
|
||||
color: #232333;
|
||||
}
|
||||
}
|
509
public/assets/vendor/libs/datatables-buttons-bs5/buttons.bootstrap5.css
vendored
Normal file
509
public/assets/vendor/libs/datatables-buttons-bs5/buttons.bootstrap5.css
vendored
Normal file
@ -0,0 +1,509 @@
|
||||
@charset "UTF-8";
|
||||
@keyframes dtb-spinner {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes dtb-spinner {
|
||||
100% {
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-ms-keyframes dtb-spinner {
|
||||
100% {
|
||||
-ms-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes dtb-spinner {
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes dtb-spinner {
|
||||
100% {
|
||||
-moz-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
div.dataTables_wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.dt-buttons {
|
||||
position: initial;
|
||||
}
|
||||
|
||||
div.dt-buttons .dt-button {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
div.dt-button-info {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 400px;
|
||||
margin-top: -100px;
|
||||
margin-left: -200px;
|
||||
background-color: white;
|
||||
border-radius: 0.75em;
|
||||
box-shadow: 3px 4px 10px 1px rgba(0, 0, 0, 0.8);
|
||||
text-align: center;
|
||||
z-index: 2003;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.dt-button-info h2 {
|
||||
padding: 2rem 2rem 1rem 2rem;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div.dt-button-info > div {
|
||||
padding: 1em 2em 2em 2em;
|
||||
}
|
||||
|
||||
div.dtb-popover-close {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
z-index: 2003;
|
||||
}
|
||||
|
||||
button.dtb-hide-drop {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
div.dt-button-collection-title {
|
||||
text-align: center;
|
||||
padding: 0.3em 0 0.5em;
|
||||
margin-left: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
div.dt-button-collection-title:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span.dt-button-spacer {
|
||||
display: inline-block;
|
||||
margin: 0.5em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
span.dt-button-spacer.bar {
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.3);
|
||||
vertical-align: middle;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
span.dt-button-spacer.bar:empty {
|
||||
height: 1em;
|
||||
width: 1px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active {
|
||||
padding-right: 3em;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -10px;
|
||||
right: 1em;
|
||||
display: inline-block;
|
||||
content: "✓";
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active.dt-button-split {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active.dt-button-split:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active.dt-button-split > *:first-child {
|
||||
padding-right: 3em;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active.dt-button-split > *:first-child:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -10px;
|
||||
right: 1em;
|
||||
display: inline-block;
|
||||
content: "✓";
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active-a a {
|
||||
padding-right: 3em;
|
||||
}
|
||||
|
||||
div.dt-button-collection .dt-button-active-a a:after {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
display: inline-block;
|
||||
content: "✓";
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
div.dt-button-collection span.dt-button-spacer {
|
||||
width: 100%;
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
div.dt-button-collection span.dt-button-spacer:empty {
|
||||
height: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.dt-button-collection span.dt-button-spacer.bar {
|
||||
border-left: none;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
html.dark div.dt-button-info {
|
||||
background-color: var(--dt-html-background);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
div.dt-buttons div.btn-group {
|
||||
position: initial;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu .dt-button {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu div.dt-button-split {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu div.dt-button-split a:first-child {
|
||||
min-width: auto;
|
||||
flex: 1 0 50px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child {
|
||||
min-width: 33px;
|
||||
flex: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
line-height: 1rem;
|
||||
color: var(--bs-dropdown-link-color);
|
||||
padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:hover {
|
||||
color: var(--bs-dropdown-link-hover-color);
|
||||
background-color: var(--bs-dropdown-link-hover-bg);
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:after {
|
||||
position: relative;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.fixed {
|
||||
position: fixed;
|
||||
display: block;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -75px;
|
||||
border-radius: 5px;
|
||||
background-color: white;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.fixed.two-column {
|
||||
margin-left: -200px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.fixed.three-column {
|
||||
margin-left: -225px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.fixed.four-column {
|
||||
margin-left: -300px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.fixed.columns {
|
||||
margin-left: -409px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
div.dt-buttons div.dropdown-menu.fixed.columns {
|
||||
margin-left: -308px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 640px) {
|
||||
div.dt-buttons div.dropdown-menu.fixed.columns {
|
||||
margin-left: -203px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 460px) {
|
||||
div.dt-buttons div.dropdown-menu.fixed.columns {
|
||||
margin-left: -100px;
|
||||
}
|
||||
}
|
||||
div.dt-buttons div.dropdown-menu.fixed > :last-child {
|
||||
max-height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.two-column > :last-child, div.dt-buttons div.dropdown-menu.three-column > :last-child, div.dt-buttons div.dropdown-menu.four-column > :last-child {
|
||||
display: block !important;
|
||||
-webkit-column-gap: 8px;
|
||||
-moz-column-gap: 8px;
|
||||
-ms-column-gap: 8px;
|
||||
-o-column-gap: 8px;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.two-column > :last-child > *, div.dt-buttons div.dropdown-menu.three-column > :last-child > *, div.dt-buttons div.dropdown-menu.four-column > :last-child > * {
|
||||
-webkit-column-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.two-column {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.two-column > :last-child {
|
||||
padding-bottom: 1px;
|
||||
column-count: 2;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.three-column {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.three-column > :last-child {
|
||||
padding-bottom: 1px;
|
||||
column-count: 3;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.four-column {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.four-column > :last-child {
|
||||
padding-bottom: 1px;
|
||||
column-count: 4;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu .dt-button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.columns {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.columns > :last-child {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 818px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.columns > :last-child .dt-button {
|
||||
min-width: 200px;
|
||||
flex: 0 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.columns.dtb-b3 > :last-child, div.dt-buttons div.dropdown-menu.columns.dtb-b2 > :last-child, div.dt-buttons div.dropdown-menu.columns.dtb-b1 > :last-child {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.columns.dtb-b3 .dt-button {
|
||||
flex: 1 1 32%;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.columns.dtb-b2 .dt-button {
|
||||
flex: 1 1 48%;
|
||||
}
|
||||
|
||||
div.dt-buttons div.dropdown-menu.columns.dtb-b1 .dt-button {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
div.dt-buttons div.dropdown-menu.columns > :last-child {
|
||||
width: 612px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 640px) {
|
||||
div.dt-buttons div.dropdown-menu.columns > :last-child {
|
||||
width: 406px;
|
||||
}
|
||||
div.dt-buttons div.dropdown-menu.columns.dtb-b3 .dt-button {
|
||||
flex: 0 1 32%;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 460px) {
|
||||
div.dt-buttons div.dropdown-menu.columns > :last-child {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
div.dt-buttons span.dt-button-spacer.empty {
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
div.dt-buttons span.dt-button-spacer.bar:empty {
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
div.dt-buttons .btn.processing {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
div.dt-buttons .btn.processing:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: -8px 0 0 -8px;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
content: " ";
|
||||
border: 2px solid #282828;
|
||||
border-radius: 50%;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
animation: dtb-spinner 1500ms infinite linear;
|
||||
-o-animation: dtb-spinner 1500ms infinite linear;
|
||||
-ms-animation: dtb-spinner 1500ms infinite linear;
|
||||
-webkit-animation: dtb-spinner 1500ms infinite linear;
|
||||
-moz-animation: dtb-spinner 1500ms infinite linear;
|
||||
}
|
||||
|
||||
div.dt-button-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dt-buttons {
|
||||
float: none;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
div.dt-buttons a.btn {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
:root[data-bs-theme=dark] div.dropdown-menu.dt-button-collection.fixed {
|
||||
background-color: #212529;
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dt-buttons {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
div.dataTables_wrapper .dt-button-collection {
|
||||
border: 0;
|
||||
border-radius: var(--bs-border-radius);
|
||||
padding: 0.5rem 0;
|
||||
width: auto;
|
||||
}
|
||||
div.dataTables_wrapper .dt-button-collection > div[role=menu] {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dt-button-collection {
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
div.dropdown-menu.dt-button-collection,
|
||||
div.dt-button-collection .dt-button:not(.dt-btn-split-drop) {
|
||||
min-width: 8rem;
|
||||
}
|
||||
|
||||
.dt-down-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.light-style div.dataTables_wrapper .dt-button-collection {
|
||||
background-color: #fff;
|
||||
}
|
||||
.light-style .dataTable a:not([href]):not([tabindex]) {
|
||||
color: #71dd37;
|
||||
}
|
||||
.light-style .dt-button-info {
|
||||
box-shadow: 0 0.1875rem 0.5rem 0 rgba(34, 48, 62, 0.1);
|
||||
}
|
||||
.light-style .dt-button-collection .dropdown-item {
|
||||
padding: 0.543rem 1.25rem;
|
||||
}
|
||||
|
||||
.dark-style div.dataTables_wrapper .dt-button-collection {
|
||||
background-color: #2b2c40;
|
||||
}
|
||||
.dark-style .dataTable a:not([href]):not([tabindex]) {
|
||||
color: #71dd37;
|
||||
}
|
||||
.dark-style .dt-button-info {
|
||||
box-shadow: 0 0.1875rem 0.5rem 0 rgba(20, 20, 29, 0.22);
|
||||
}
|
||||
.dark-style .dt-button-collection .dropdown-item {
|
||||
padding: 0.543rem 1.25rem;
|
||||
}
|
||||
|
||||
.dt-button-info {
|
||||
border-width: 0 !important;
|
||||
border-radius: 0.375rem !important;
|
||||
}
|
||||
.dt-button-info h2 {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
.dt-buttons {
|
||||
position: relative;
|
||||
}
|
||||
[dir=rtl] .dt-buttons .dt-button-collection .dropdown-item {
|
||||
text-align: right;
|
||||
}
|
||||
.dt-buttons.btn-group button {
|
||||
border-color: transparent !important;
|
||||
border-radius: 0.375rem !important;
|
||||
}
|
23
public/assets/vendor/libs/datatables-checkboxes-jquery/datatables.checkboxes.css
vendored
Normal file
23
public/assets/vendor/libs/datatables-checkboxes-jquery/datatables.checkboxes.css
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
table.dataTable.dt-checkboxes-select tbody tr,
|
||||
table.dataTable thead th.dt-checkboxes-select-all,
|
||||
table.dataTable tbody td.dt-checkboxes-cell {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table.dataTable thead th.dt-checkboxes-select-all,
|
||||
table.dataTable tbody td.dt-checkboxes-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper span.select-info,
|
||||
div.dataTables_wrapper span.select-item {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
div.dataTables_wrapper span.select-info,
|
||||
div.dataTables_wrapper span.select-item {
|
||||
margin-left: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
185
public/assets/vendor/libs/datatables-fixedcolumns-bs5/fixedcolumns.bootstrap5.css
vendored
Normal file
185
public/assets/vendor/libs/datatables-fixedcolumns-bs5/fixedcolumns.bootstrap5.css
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
table.dataTable thead tr > .dtfc-fixed-left,
|
||||
table.dataTable thead tr > .dtfc-fixed-right,
|
||||
table.dataTable tfoot tr > .dtfc-fixed-left,
|
||||
table.dataTable tfoot tr > .dtfc-fixed-right {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
table.dataTable tbody tr > .dtfc-fixed-left,
|
||||
table.dataTable tbody tr > .dtfc-fixed-right {
|
||||
z-index: 1;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
div.dtfc-left-top-blocker,
|
||||
div.dtfc-right-top-blocker {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
html.dark table.dataTable thead tr > .dtfc-fixed-left,
|
||||
html.dark table.dataTable thead tr > .dtfc-fixed-right,
|
||||
html.dark table.dataTable tfoot tr > .dtfc-fixed-left,
|
||||
html.dark table.dataTable tfoot tr > .dtfc-fixed-right {
|
||||
background-color: var(--dt-html-background);
|
||||
}
|
||||
|
||||
html.dark table.dataTable tbody tr > .dtfc-fixed-left,
|
||||
html.dark table.dataTable tbody tr > .dtfc-fixed-right {
|
||||
background-color: var(--dt-html-background);
|
||||
}
|
||||
|
||||
html.dark div.dtfc-left-top-blocker,
|
||||
html.dark div.dtfc-right-top-blocker {
|
||||
background-color: var(--dt-html-background);
|
||||
}
|
||||
|
||||
div.dtfc-right-top-blocker,
|
||||
div.dtfc-left-top-blocker {
|
||||
margin-top: 6px;
|
||||
border-bottom: 0px solid #ddd !important;
|
||||
}
|
||||
|
||||
table.dataTable.table-bordered.dtfc-has-left {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
div.dataTables_scroll.dtfc-has-left table.table-bordered {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody {
|
||||
border-left: 1px solid #ddd !important;
|
||||
}
|
||||
|
||||
div.dataTables_scrollFootInner table.table-bordered tr th:first-child,
|
||||
div.dataTables_scrollHeadInner table.table-bordered tr th:first-child {
|
||||
border-left: 1px solid #ddd !important;
|
||||
}
|
||||
|
||||
html[data-bs-theme=dark] table.dataTable thead tr > .dtfc-fixed-left,
|
||||
html[data-bs-theme=dark] table.dataTable thead tr > .dtfc-fixed-right,
|
||||
html[data-bs-theme=dark] table.dataTable tfoot tr > .dtfc-fixed-left,
|
||||
html[data-bs-theme=dark] table.dataTable tfoot tr > .dtfc-fixed-right {
|
||||
background-color: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
html[data-bs-theme=dark] table.dataTable tbody tr > .dtfc-fixed-left,
|
||||
html[data-bs-theme=dark] table.dataTable tbody tr > .dtfc-fixed-right {
|
||||
background-color: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
html[data-bs-theme=dark] div.dtfc-left-top-blocker,
|
||||
html[data-bs-theme=dark] div.dtfc-right-top-blocker {
|
||||
background-color: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
html[data-bs-theme=dark] div.dataTables_scrollBody {
|
||||
border-left-color: var(--bs-border-color) !important;
|
||||
}
|
||||
|
||||
html[data-bs-theme=dark] div.dataTables_scrollFootInner table.table-bordered tr th:first-child,
|
||||
html[data-bs-theme=dark] div.dataTables_scrollHeadInner table.table-bordered tr th:first-child {
|
||||
border-left-color: var(--bs-border-color) !important;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody thead tr,
|
||||
div.DTFC_LeftBodyLiner thead tr {
|
||||
border-top-width: 0;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
div.dataTables_scrollBody {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
html:not([dir=rtl]) div.dataTables_scrollFootInner table.table-bordered tr th:first-child,
|
||||
html:not([dir=rtl]) div.dataTables_scrollHeadInner table.table-bordered tr th:first-child {
|
||||
border-left: 0 !important;
|
||||
}
|
||||
|
||||
[dir=rtl] table.dataTable thead th,
|
||||
[dir=rtl] table.dataTable thead td,
|
||||
[dir=rtl] table.dataTable tfoot th,
|
||||
[dir=rtl] table.dataTable tfoot td {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
.light-style table.DTFC_Cloned tr {
|
||||
border-color: #e4e6e8;
|
||||
}
|
||||
.light-style div.dataTables_scrollFootInner table.table-bordered tr th:first-child,
|
||||
.light-style div.dataTables_scrollHeadInner table.table-bordered tr th:first-child {
|
||||
border-left: 1px solid #e4e6e8;
|
||||
}
|
||||
.light-style table.dataTable thead tr > .dtfc-fixed-left,
|
||||
.light-style table.dataTable thead tr > .dtfc-fixed-right,
|
||||
.light-style table.dataTable tbody tr > .dtfc-fixed-left,
|
||||
.light-style table.dataTable tbody tr > .dtfc-fixed-right,
|
||||
.light-style div.dtfc-right-top-blocker,
|
||||
.light-style div.dtfc-left-top-blocker {
|
||||
background-color: #fff;
|
||||
margin-top: 1px !important;
|
||||
height: 0px !important;
|
||||
}
|
||||
.light-style .dt-fixedcolumns thead {
|
||||
border-top-color: #e4e6e8;
|
||||
}
|
||||
.light-style[dir=rtl] div.dataTables_scrollHead table,
|
||||
.light-style[dir=rtl] div.dataTables_scrollBody table {
|
||||
border-width: 0;
|
||||
}
|
||||
.light-style[dir=rtl] div.DTFC_LeftBodyLiner {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
.light-style[dir=rtl] div.DTFC_RightHeadWrapper table,
|
||||
.light-style[dir=rtl] div.DTFC_RightBodyWrapper table {
|
||||
border: 0;
|
||||
}
|
||||
.light-style[dir=rtl] div.DTFC_RightBodyLiner {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.dark-style table.DTFC_Cloned tr {
|
||||
background-color: #2b2c40;
|
||||
border-color: #4e4f6c;
|
||||
}
|
||||
.dark-style div.dataTables_scrollHead table,
|
||||
.dark-style div.DTFC_RightHeadWrapper table,
|
||||
.dark-style table.dataTable.fixedHeader-floating,
|
||||
.dark-style table.dataTable.fixedHeader-locked {
|
||||
background-color: #2b2c40;
|
||||
}
|
||||
.dark-style div.dataTables_scrollFootInner table.table-bordered tr th:first-child,
|
||||
.dark-style div.dataTables_scrollHeadInner table.table-bordered tr th:first-child {
|
||||
border-left: 1px solid #4e4f6c !important;
|
||||
}
|
||||
.dark-style table.dataTable thead tr > .dtfc-fixed-left,
|
||||
.dark-style table.dataTable thead tr > .dtfc-fixed-right,
|
||||
.dark-style table.dataTable tbody tr > .dtfc-fixed-left,
|
||||
.dark-style table.dataTable tbody tr > .dtfc-fixed-right,
|
||||
.dark-style div.dtfc-right-top-blocker,
|
||||
.dark-style div.dtfc-left-top-blocker {
|
||||
background-color: #2b2c40;
|
||||
margin-top: 1px !important;
|
||||
height: 0px !important;
|
||||
}
|
||||
.dark-style .dt-fixedcolumns thead {
|
||||
border-top-color: #4e4f6c;
|
||||
}
|
||||
.dark-style[dir=rtl] div.dataTables_scrollHead table,
|
||||
.dark-style[dir=rtl] div.dataTables_scrollBody table {
|
||||
border-width: 0;
|
||||
}
|
||||
.dark-style[dir=rtl] div.DTFC_LeftBodyLiner {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
.dark-style[dir=rtl] div.DTFC_RightHeadWrapper table,
|
||||
.dark-style[dir=rtl] div.DTFC_RightBodyWrapper table {
|
||||
border: 0;
|
||||
}
|
||||
.dark-style[dir=rtl] div.DTFC_RightBodyLiner {
|
||||
padding-left: 0 !important;
|
||||
}
|
46
public/assets/vendor/libs/datatables-fixedheader-bs5/fixedheader.bootstrap5.css
vendored
Normal file
46
public/assets/vendor/libs/datatables-fixedheader-bs5/fixedheader.bootstrap5.css
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
table.dataTable.fixedHeader-floating,
|
||||
table.dataTable.fixedHeader-locked {
|
||||
background-color: white;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
table.dataTable.fixedHeader-locked {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
@media print {
|
||||
table.fixedHeader-floating {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
html[data-bs-theme=dark] table.dataTable.fixedHeader-floating,
|
||||
html[data-bs-theme=dark] table.dataTable.fixedHeader-locked {
|
||||
background-color: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
.dt-fixedheader.fixedHeader-floating.table.dataTable {
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.dt-fixedheader.fixedHeader-locked.table.dataTable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.light-style .dtfh-floatingparenthead {
|
||||
border-bottom: 1px solid #e4e6e8;
|
||||
}
|
||||
.light-style .table-bordered.dt-fixedheader.fixedHeader-floating.table.dataTable thead > tr > th,
|
||||
.light-style .table-bordered.dt-fixedheader.fixedHeader-locked.table.dataTable thead > tr > th {
|
||||
border-bottom-width: 1px;
|
||||
border-color: #e4e6e8;
|
||||
}
|
||||
|
||||
.dark-style .dtfh-floatingparenthead {
|
||||
border-bottom: 1px solid #4e4f6c;
|
||||
}
|
||||
.dark-style .table-bordered.dt-fixedheader.fixedHeader-floating.table.dataTable thead > tr > th,
|
||||
.dark-style .table-bordered.dt-fixedheader.fixedHeader-locked.table.dataTable thead > tr > th {
|
||||
border-bottom-width: 1px;
|
||||
border-color: #4e4f6c;
|
||||
}
|
241
public/assets/vendor/libs/datatables-responsive-bs5/responsive.bootstrap5.css
vendored
Normal file
241
public/assets/vendor/libs/datatables-responsive-bs5/responsive.bootstrap5.css
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
@charset "UTF-8";
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.child,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.child,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control:before {
|
||||
margin-right: 0.5em;
|
||||
display: inline-block;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
content: "►";
|
||||
}
|
||||
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control.arrow-right::before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control.arrow-right::before {
|
||||
content: "◄";
|
||||
}
|
||||
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td.dtr-control:before,
|
||||
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th.dtr-control:before {
|
||||
content: "▼";
|
||||
}
|
||||
|
||||
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control,
|
||||
table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control {
|
||||
padding-left: 0.333em;
|
||||
}
|
||||
|
||||
table.dataTable.dtr-column > tbody > tr > td.dtr-control,
|
||||
table.dataTable.dtr-column > tbody > tr > th.dtr-control,
|
||||
table.dataTable.dtr-column > tbody > tr > td.control,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table.dataTable.dtr-column > tbody > tr > td.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > td.control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control:before {
|
||||
display: inline-block;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
content: "►";
|
||||
}
|
||||
|
||||
table.dataTable.dtr-column > tbody > tr > td.dtr-control.arrow-right::before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.dtr-control.arrow-right::before,
|
||||
table.dataTable.dtr-column > tbody > tr > td.control.arrow-right::before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control.arrow-right::before {
|
||||
content: "◄";
|
||||
}
|
||||
|
||||
table.dataTable.dtr-column > tbody > tr.parent td.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent th.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent td.control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent th.control:before {
|
||||
content: "▼";
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.child {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.child:hover {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.child ul.dtr-details {
|
||||
display: inline-block;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.child ul.dtr-details > li {
|
||||
border-bottom: 1px solid #efefef;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.child ul.dtr-details > li:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.child ul.dtr-details > li:last-child {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.child span.dtr-title {
|
||||
display: inline-block;
|
||||
min-width: 75px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.dtr-modal {
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
padding: 10em 1em;
|
||||
}
|
||||
|
||||
div.dtr-modal div.dtr-modal-display {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 50%;
|
||||
height: fit-content;
|
||||
max-height: 75%;
|
||||
overflow: auto;
|
||||
margin: auto;
|
||||
z-index: 102;
|
||||
overflow: auto;
|
||||
background-color: #f5f5f7;
|
||||
border: 1px solid black;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
div.dtr-modal div.dtr-modal-content {
|
||||
position: relative;
|
||||
padding: 2.5em;
|
||||
}
|
||||
|
||||
div.dtr-modal div.dtr-modal-content h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.dtr-modal div.dtr-modal-close {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
div.dtr-modal div.dtr-modal-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 101;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dtr-modal div.dtr-modal-display {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
html.dark table.dataTable > tbody > tr > td.dtr-control:before {
|
||||
color: rgba(255, 255, 255, 0.5) !important;
|
||||
}
|
||||
|
||||
html.dark table.dataTable > tbody > tr.child ul.dtr-details > li {
|
||||
border-bottom-color: #404346;
|
||||
}
|
||||
|
||||
html.dark div.dtr-modal div.dtr-modal-display {
|
||||
background-color: #212529;
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
div.dtr-bs-modal table.table tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.dataTable.table-bordered th.dtr-control.dtr-hidden + *,
|
||||
table.dataTable.table-bordered td.dtr-control.dtr-hidden + * {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
table.dataTable.dtr-column > tbody > tr > td.control,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control {
|
||||
position: relative;
|
||||
}
|
||||
table.dataTable.dtr-column > tbody > tr > td.control:before, table.dataTable.dtr-column > tbody > tr > td.control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control:before,
|
||||
table.dataTable.dtr-column > tbody > tr > th.control:before {
|
||||
position: absolute;
|
||||
line-height: 0.9em;
|
||||
font-weight: 500;
|
||||
height: 0.85em;
|
||||
width: 0.85em;
|
||||
color: #fff;
|
||||
border-radius: 1em;
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
content: "+";
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
table.dataTable.dtr-column > tbody > tr.parent td.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent th.dtr-control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent td.control:before,
|
||||
table.dataTable.dtr-column > tbody > tr.parent th.control:before {
|
||||
content: "+";
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1399.98px) {
|
||||
table.dataTable.table-responsive {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.modal.dtr-bs-modal .modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
.modal.dtr-bs-modal .table tr:last-child > td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.modal.dtr-bs-modal .table .btn {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.modal.dtr-bs-modal .table .emp_name {
|
||||
font-weight: 500;
|
||||
}
|
87
public/assets/vendor/libs/datatables-rowgroup-bs5/rowgroup.bootstrap5.css
vendored
Normal file
87
public/assets/vendor/libs/datatables-rowgroup-bs5/rowgroup.bootstrap5.css
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
table.dataTable tr.dtrg-group th {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.dataTable tr.dtrg-group.dtrg-level-0 th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.dataTable tr.dtrg-group.dtrg-level-1 th,
|
||||
table.dataTable tr.dtrg-group.dtrg-level-2 th,
|
||||
table.dataTable tr.dtrg-group.dtrg-level-3 th,
|
||||
table.dataTable tr.dtrg-group.dtrg-level-4 th,
|
||||
table.dataTable tr.dtrg-group.dtrg-level-5 th {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0.25em;
|
||||
padding-left: 2em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
table.dataTable tr.dtrg-group.dtrg-level-2 th {
|
||||
background-color: rgba(0, 0, 0, 0.01);
|
||||
padding-left: 2.5em;
|
||||
}
|
||||
|
||||
table.dataTable tr.dtrg-group.dtrg-level-3 th {
|
||||
background-color: rgba(0, 0, 0, 0.01);
|
||||
padding-left: 3em;
|
||||
}
|
||||
|
||||
table.dataTable tr.dtrg-group.dtrg-level-4 th {
|
||||
background-color: rgba(0, 0, 0, 0.01);
|
||||
padding-left: 3.5em;
|
||||
}
|
||||
|
||||
table.dataTable tr.dtrg-group.dtrg-level-5 th {
|
||||
background-color: rgba(0, 0, 0, 0.01);
|
||||
padding-left: 4em;
|
||||
}
|
||||
|
||||
html.dark table.dataTable tr.dtrg-group th {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
html.dark table.dataTable tr.dtrg-group.dtrg-level-1 th {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
html.dark table.dataTable tr.dtrg-group.dtrg-level-2 th,
|
||||
html.dark table.dataTable tr.dtrg-group.dtrg-level-3 th,
|
||||
html.dark table.dataTable tr.dtrg-group.dtrg-level-4 th,
|
||||
html.dark table.dataTable tr.dtrg-group.dtrg-level-5 th {
|
||||
background-color: rgba(255, 255, 255, 0.01);
|
||||
}
|
||||
|
||||
table.dataTable.table-striped tr.dtrg-level-0 {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
table.dataTable.table-striped tr.dtrg-level-1 {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
table.dataTable.table-striped tr.dtrg-level-2,
|
||||
table.dataTable.table-striped tr.dtrg-level-3,
|
||||
table.dataTable.table-striped tr.dtrg-level-4,
|
||||
table.dataTable.table-striped tr.dtrg-level-5 {
|
||||
background-color: rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
table.dataTable.table-striped tr.dtrg-level-1 tr.dtrg-level-2 th,
|
||||
table.dataTable.table-striped tr.dtrg-level-3 th,
|
||||
table.dataTable.table-striped tr.dtrg-level-4 th,
|
||||
table.dataTable.table-striped tr.dtrg-level-5 th {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.light-style tr.group,
|
||||
.light-style tr.group:hover {
|
||||
background-color: rgba(34, 48, 62, 0.1) !important;
|
||||
}
|
||||
|
||||
.dark-style tr.group,
|
||||
.dark-style tr.group:hover {
|
||||
background-color: rgba(230, 230, 241, 0.1) !important;
|
||||
}
|
108
public/assets/vendor/libs/datatables-select-bs5/select.bootstrap5.css
vendored
Normal file
108
public/assets/vendor/libs/datatables-select-bs5/select.bootstrap5.css
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
@charset "UTF-8";
|
||||
table.dataTable > tbody > tr > .selected {
|
||||
background-color: rgba(13, 110, 253, 0.9);
|
||||
color: white;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr > td.select-checkbox,
|
||||
table.dataTable > tbody > tr > th.select-checkbox {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr > td.select-checkbox:before, table.dataTable > tbody > tr > td.select-checkbox:after,
|
||||
table.dataTable > tbody > tr > th.select-checkbox:before,
|
||||
table.dataTable > tbody > tr > th.select-checkbox:after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr > td.select-checkbox:before,
|
||||
table.dataTable > tbody > tr > th.select-checkbox:before {
|
||||
content: " ";
|
||||
margin-top: -6px;
|
||||
margin-left: -6px;
|
||||
border: 1px solid black;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.selected > td.select-checkbox:before,
|
||||
table.dataTable > tbody > tr.selected > th.select-checkbox:before {
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
table.dataTable > tbody > tr.selected > td.select-checkbox:after,
|
||||
table.dataTable > tbody > tr.selected > th.select-checkbox:after {
|
||||
content: "✓";
|
||||
font-size: 20px;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.dataTable.compact > tbody > tr > td.select-checkbox:before,
|
||||
table.dataTable.compact > tbody > tr > th.select-checkbox:before {
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
table.dataTable.compact > tbody > tr.selected > td.select-checkbox:after,
|
||||
table.dataTable.compact > tbody > tr.selected > th.select-checkbox:after {
|
||||
margin-top: -16px;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper span.select-info,
|
||||
div.dataTables_wrapper span.select-item {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
html.dark table.dataTable > tbody > tr > td.select-checkbox:before,
|
||||
html.dark table.dataTable > tbody > tr > th.select-checkbox:before,
|
||||
html[data-bs-theme=dark] table.dataTable > tbody > tr > td.select-checkbox:before,
|
||||
html[data-bs-theme=dark] table.dataTable > tbody > tr > th.select-checkbox:before {
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
div.dataTables_wrapper span.select-info,
|
||||
div.dataTables_wrapper span.select-item {
|
||||
margin-left: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
table.dataTable.table-sm tbody td.select-checkbox::before {
|
||||
margin-top: -9px;
|
||||
}
|
||||
|
||||
.light-style table.dataTable tbody > tr.selected,
|
||||
.light-style table.dataTable tbody > tr > .selected {
|
||||
background-color: #f8f9f9;
|
||||
}
|
||||
.light-style table.dataTable tbody > tr.selected > *,
|
||||
.light-style table.dataTable tbody > tr > .selected > * {
|
||||
box-shadow: inset 0 0 0 rgba(34, 48, 62, 0.06);
|
||||
color: #646e78;
|
||||
}
|
||||
.light-style table.dataTable tbody tr.selected,
|
||||
.light-style table.dataTable tbody th.selected,
|
||||
.light-style table.dataTable tbody td.selected {
|
||||
color: #646e78;
|
||||
}
|
||||
|
||||
.dark-style table.dataTable tbody > tr.selected,
|
||||
.dark-style table.dataTable tbody > tr > .selected {
|
||||
background-color: rgba(230, 230, 241, 0.08);
|
||||
}
|
||||
.dark-style table.dataTable tbody > tr.selected > *,
|
||||
.dark-style table.dataTable tbody > tr > .selected > * {
|
||||
box-shadow: inset 0 0 0 rgba(230, 230, 241, 0.06);
|
||||
color: #b2b2c4;
|
||||
}
|
||||
.dark-style table.dataTable tbody tr.selected,
|
||||
.dark-style table.dataTable tbody th.selected,
|
||||
.dark-style table.dataTable tbody td.selected {
|
||||
color: inherit;
|
||||
}
|
1327
public/assets/vendor/libs/sweetalert2/sweetalert2.css
vendored
Normal file
1327
public/assets/vendor/libs/sweetalert2/sweetalert2.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
120
public/assets/vendor/libs/sweetalert2/sweetalert2.js
vendored
Normal file
120
public/assets/vendor/libs/sweetalert2/sweetalert2.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
public/img/avatars/female.jpg
Normal file
BIN
public/img/avatars/female.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 372 KiB |
BIN
public/img/avatars/male.jpg
Normal file
BIN
public/img/avatars/male.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
24
src/App.css
24
src/App.css
@ -43,3 +43,27 @@
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
|
||||
.assign-employee-card {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.assign-employee-card:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.employee-info p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.assign-employee-card {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import { convertShortTime } from '../../utils/dateUtils';
|
||||
const AttendLogs = ({ Id }) => {
|
||||
|
||||
const {logs,loading} = useEmployeeAttendacesLog(Id)
|
||||
console.log(logs)
|
||||
return (
|
||||
<div className="table-responsive">
|
||||
{loading && <p>Loading..</p>}
|
||||
@ -22,7 +21,7 @@ console.log(logs)
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: '20%' }}>Time</th>
|
||||
<th style={{ width: '20%' }}>activityTime</th>
|
||||
<th style={{ width: '20%' }}>Date</th>
|
||||
<th style={{ width: '60%' }}>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -7,7 +7,9 @@ import usePagination from "../../hooks/usePagination";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
|
||||
const Attendance = ({attendance,getRole, handleModalData}) => {
|
||||
const Attendance = ( {attendance, getRole, handleModalData} ) =>
|
||||
{
|
||||
|
||||
const { currentPage, totalPages, currentItems, paginate } = usePagination(attendance, 5);
|
||||
const [loading,setLoading] = useState(false);
|
||||
const navigate = useNavigate()
|
||||
@ -15,7 +17,7 @@ const Attendance = ({attendance,getRole, handleModalData}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="table-responsive text-nowrap">
|
||||
{attendance &&attendance.length > 0 ? (<>
|
||||
{attendance && attendance.length > 0 ? (<>
|
||||
<table className="table ">
|
||||
<thead >
|
||||
<tr>
|
||||
@ -40,7 +42,6 @@ const Attendance = ({attendance,getRole, handleModalData}) => {
|
||||
<a
|
||||
// href="#"
|
||||
onClick={(e) =>navigate(`/employee/${item.employeeId}?for=attendance`)}
|
||||
|
||||
className="text-heading text-truncate cursor-pointer"
|
||||
>
|
||||
<span className="fw-medium">
|
||||
@ -60,7 +61,7 @@ const Attendance = ({attendance,getRole, handleModalData}) => {
|
||||
<td>{item.checkOutTime ? convertShortTime(item.checkOutTime):"--"}</td>
|
||||
|
||||
<td className="mx-24" >
|
||||
<RenderAttendanceStatus attendanceData={item} handleModalData={handleModalData} Tab={1}/>
|
||||
<RenderAttendanceStatus attendanceData={item} handleModalData={handleModalData} Tab={1} currentDate={null}/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
@ -1,68 +1,79 @@
|
||||
import React,{useEffect,useState} from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Avatar from '../common/Avatar';
|
||||
import { convertShortTime } from '../../utils/dateUtils';
|
||||
import RenderAttendanceStatus from './RenderAttendanceStatus';
|
||||
import { useSelector,useDispatch } from 'react-redux';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { fetchAttendanceData } from '../../slices/apiSlice/attedanceLogsSlice';
|
||||
|
||||
const AttendanceLog = ({attendance,handleModalData,projectId}) => {
|
||||
const[attendances,setAttendnaces] = useState()
|
||||
const[selectedDate,setSelectedDate] = useState('')
|
||||
const dispatch = useDispatch()
|
||||
const {data,loading,error} = useSelector((store)=>store.attendance)
|
||||
const AttendanceLog = ({ attendance, handleModalData, projectId }) => {
|
||||
const [attendances, setAttendnaces] = useState([]);
|
||||
const [selectedDate, setSelectedDate] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
const { data, loading, error } = useSelector((store) => store.attendanceLogs);
|
||||
|
||||
const handleDateChange = (e) => {
|
||||
// Set the default selected date to the current date
|
||||
const currentDate = new Date().toISOString().split('T')[0]; // "YYYY-MM-DD"
|
||||
|
||||
const handleDateChange = (e) => {
|
||||
const date = e.target.value;
|
||||
console.log(date)
|
||||
setSelectedDate(date);
|
||||
if (date) {
|
||||
dispatch(fetchAttendanceData({ projectId, date: date }));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// If attendance has check-in time, filter it
|
||||
setAttendnaces(attendance?.filter((record) => record.checkInTime !== null));
|
||||
setSelectedDate(currentDate); // Set default selected date to today
|
||||
}, [attendance]);
|
||||
|
||||
const renderAttendanceData = selectedDate === currentDate ? attendances : data;
|
||||
|
||||
useEffect(()=>{
|
||||
setAttendnaces(attendance?.filter((record)=>record.checkInTime !== null))
|
||||
},[attendance])
|
||||
return (
|
||||
<>
|
||||
<div className="dataTables_length text-start py-2" id="DataTables_Table_0_length">
|
||||
<div class="col-md-3">
|
||||
<input class="form-control form-control-sm" type="date" placeholder='Selecte Date' value={selectedDate} onChange={handleDateChange} id="html5-date-input" />
|
||||
<div className="col-md-3">
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
type="date"
|
||||
placeholder="Select Date"
|
||||
value={selectedDate}
|
||||
onChange={handleDateChange}
|
||||
id="html5-date-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="table-responsive">
|
||||
{attendance && attendance.length > 0 ? (
|
||||
<table className="table mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
{/* <th>Date</th> */}
|
||||
<th><i className='bx bxs-down-arrow-alt text-success' ></i>Check-In</th>
|
||||
<th><i className='bx bxs-up-arrow-alt text-danger' ></i>Check-Out</th>
|
||||
<th>
|
||||
<i className="bx bxs-down-arrow-alt text-success"></i> Check-In
|
||||
</th>
|
||||
<th>
|
||||
<i className="bx bxs-up-arrow-alt text-danger"></i> Check-Out
|
||||
</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading && <td colSpan={5}>Loading...</td>}
|
||||
{error && <td colSpan={5}>{error}</td>}
|
||||
{selectedDate && data.length == 0 && (<td colSpan={5}>No Data Found</td>)}
|
||||
{selectedDate && renderAttendanceData.length === 0 && (
|
||||
<td colSpan={5}>No Data Found</td>
|
||||
)}
|
||||
|
||||
|
||||
{
|
||||
|
||||
(selectedDate ? data : attendances)?.map((attendance, index) => (
|
||||
{renderAttendanceData?.map((attendance, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<div className="d-flex justify-content-start align-items-center">
|
||||
<Avatar
|
||||
firstName={attendance.firstName}
|
||||
lastName={attendance.lastName}
|
||||
></Avatar>
|
||||
<Avatar firstName={attendance.firstName} lastName={attendance.lastName} />
|
||||
<div className="d-flex flex-column">
|
||||
<a
|
||||
href="#"
|
||||
className="text-heading text-truncate"
|
||||
>
|
||||
<a href="#" className="text-heading text-truncate">
|
||||
<span className="fw-medium">
|
||||
{attendance.firstName} {attendance.lastName}
|
||||
</span>
|
||||
@ -70,24 +81,23 @@ useEffect(()=>{
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{/* <td>{attendance.date}</td> */}
|
||||
<td>{convertShortTime(attendance.checkInTime)}</td>
|
||||
<td>{attendance.checkOutTime ? convertShortTime(attendance.checkOutTime):"--"}</td>
|
||||
<td className='text-center ' >
|
||||
{/* <div className='d-flex justify-content-center align-items-center gap-3'> */}
|
||||
<RenderAttendanceStatus attendanceData={attendance} handleModalData={handleModalData} Tab={2} />
|
||||
{/* </div> */}
|
||||
<td>{attendance.checkOutTime ? convertShortTime(attendance.checkOutTime) : '--'}</td>
|
||||
<td className="text-center">
|
||||
<RenderAttendanceStatus
|
||||
attendanceData={attendance}
|
||||
handleModalData={handleModalData}
|
||||
Tab={2}
|
||||
currentDate={currentDate}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
|
||||
}
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
):(
|
||||
<span>No employees Logs</span>
|
||||
)
|
||||
}
|
||||
) : (
|
||||
<span>No employee logs</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -7,6 +7,7 @@ import { usePositionTracker } from "../../hooks/usePositionTracker";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { markAttendance } from "../../slices/apiSlice/attedanceLogsSlice";
|
||||
import showToast from "../../services/toastService";
|
||||
import {checkIfCurrentDate} from "../../utils/dateUtils";
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -15,7 +16,7 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
const CheckCheckOutmodel = ({modeldata,closeModal,handleSubmitForm,}) => {
|
||||
console.log(modeldata)
|
||||
|
||||
const projectId = useSelector((store)=>store.localVariables.projectId)
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const coords = usePositionTracker();
|
||||
@ -31,11 +32,20 @@ const CheckCheckOutmodel = ({modeldata,closeModal,handleSubmitForm,}) => {
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
const onSubmit = ( data ) =>
|
||||
{
|
||||
console.log(data)
|
||||
let record = {...data, date: new Date().toLocaleDateString(),latitude:coords.latitude,longitude:coords.longitude,employeeId:modeldata.employeeId,action:modeldata.action}
|
||||
if(modeldata.forWhichTab === 1){
|
||||
handleSubmitForm(record)
|
||||
}else{
|
||||
} else
|
||||
{
|
||||
console.log("is Date" ,checkIfCurrentDate(modeldata?.currentDate))
|
||||
if ( modeldata?.currentDate && checkIfCurrentDate(modeldata?.currentDate) )
|
||||
{
|
||||
handleSubmitForm(record)
|
||||
} else
|
||||
{
|
||||
let formData = {...data, date: new Date().toLocaleDateString(),latitude:coords.latitude,longitude:coords.longitude,employeeId:modeldata.employeeId,projectId:projectId,action:modeldata.action}
|
||||
dispatch(markAttendance(formData))
|
||||
.unwrap()
|
||||
@ -46,6 +56,8 @@ const CheckCheckOutmodel = ({modeldata,closeModal,handleSubmitForm,}) => {
|
||||
showToast(error, "error");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
closeModal()
|
||||
};
|
||||
|
||||
@ -57,7 +69,6 @@ const CheckCheckOutmodel = ({modeldata,closeModal,handleSubmitForm,}) => {
|
||||
<div className="col-12 col-md-12">
|
||||
{/* <TimePicker label="Time" onChange={(e) => setValue("time", e)} /> */}
|
||||
|
||||
|
||||
<TimePicker
|
||||
label="Choose a time"
|
||||
onChange={(e) => setValue("time", e)}
|
||||
@ -106,9 +117,6 @@ const CheckCheckOutmodel = ({modeldata,closeModal,handleSubmitForm,}) => {
|
||||
export default CheckCheckOutmodel;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const schemaReg = z.object({
|
||||
description:z.string().min(1,{message:"please give reason!"})
|
||||
});
|
||||
@ -120,8 +128,6 @@ export const Regularization = ({modeldata,closeModal,handleSubmitForm})=>{
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
setValue,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schemaReg),
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import useAttendanceStatus from "../../hooks/useAttendanceStatus";
|
||||
|
||||
|
||||
const RenderAttendanceStatus = ({ attendanceData, handleModalData,Tab }) => {
|
||||
const RenderAttendanceStatus = ({ attendanceData, handleModalData,Tab,currentDate}) => {
|
||||
|
||||
const { text, color, disabled, action,checkInTime } = useAttendanceStatus(attendanceData);
|
||||
|
||||
@ -17,7 +17,7 @@ const RenderAttendanceStatus = ({ attendanceData, handleModalData,Tab }) => {
|
||||
action,
|
||||
employeeId: attendanceData?.employeeId,
|
||||
id: attendanceData?.id,
|
||||
|
||||
currentDate
|
||||
});
|
||||
}
|
||||
};
|
||||
|
135
src/components/Employee/DemoTable.jsx
Normal file
135
src/components/Employee/DemoTable.jsx
Normal file
@ -0,0 +1,135 @@
|
||||
import React from 'react'
|
||||
|
||||
const DemoTable = () => {
|
||||
return (
|
||||
<div class="content-wrapper">
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="card">
|
||||
<div class="card-datatable table-responsive">
|
||||
<table class="datatables-basic table border-top">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th>id</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Date</th>
|
||||
<th>Salary</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="offcanvas offcanvas-end" id="add-new-record">
|
||||
<div class="offcanvas-header border-bottom">
|
||||
<h5 class="offcanvas-title" id="exampleModalLabel">New Record</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close text-reset"
|
||||
data-bs-dismiss="offcanvas"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body flex-grow-1">
|
||||
<form class="add-new-record pt-0 row g-2" id="form-add-new-record" onsubmit="return false">
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label" for="basicFullname">Full Name</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span id="basicFullname2" class="input-group-text"><i class="bx bx-user"></i></span>
|
||||
<input
|
||||
type="text"
|
||||
id="basicFullname"
|
||||
class="form-control dt-full-name"
|
||||
name="basicFullname"
|
||||
placeholder="John Doe"
|
||||
aria-label="John Doe"
|
||||
aria-describedby="basicFullname2" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label" for="basicPost">Post</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span id="basicPost2" class="input-group-text"><i class="bx bxs-briefcase"></i></span>
|
||||
<input
|
||||
type="text"
|
||||
id="basicPost"
|
||||
name="basicPost"
|
||||
class="form-control dt-post"
|
||||
placeholder="Web Developer"
|
||||
aria-label="Web Developer"
|
||||
aria-describedby="basicPost2" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label" for="basicEmail">Email</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-envelope"></i></span>
|
||||
<input
|
||||
type="text"
|
||||
id="basicEmail"
|
||||
name="basicEmail"
|
||||
class="form-control dt-email"
|
||||
placeholder="john.doe@example.com"
|
||||
aria-label="john.doe@example.com" />
|
||||
</div>
|
||||
<div class="form-text">You can use letters, numbers & periods</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label" for="basicDate">Joining Date</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span id="basicDate2" class="input-group-text"><i class="bx bx-calendar"></i></span>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control dt-date"
|
||||
id="basicDate"
|
||||
name="basicDate"
|
||||
aria-describedby="basicDate2"
|
||||
placeholder="MM/DD/YYYY"
|
||||
aria-label="MM/DD/YYYY" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label" for="basicSalary">Salary</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span id="basicSalary2" class="input-group-text"><i class="bx bx-dollar"></i></span>
|
||||
<input
|
||||
type="number"
|
||||
id="basicSalary"
|
||||
name="basicSalary"
|
||||
class="form-control dt-salary"
|
||||
placeholder="12000"
|
||||
aria-label="12000"
|
||||
aria-describedby="basicSalary2" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary data-submit me-sm-4 me-1">Submit</button>
|
||||
<button type="reset" class="btn btn-outline-secondary" data-bs-dismiss="offcanvas">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-12" />
|
||||
|
||||
|
||||
|
||||
<hr class="my-12" />
|
||||
|
||||
|
||||
|
||||
<hr class="my-12" />
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content-backdrop fade"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DemoTable
|
@ -1,93 +1,102 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import showToast from "../../services/toastService";
|
||||
import EmployeeRepository from "../../repositories/EmployeeRepository";
|
||||
import { useForm,Controller } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import useMaster from "../../hooks/masterHook/useMaster";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { getCachedData } from "../../slices/apiDataManager";
|
||||
import { formatDate } from "../../utils/dateUtils";
|
||||
import { useEmployees } from "../../hooks/useEmployees";
|
||||
import { useEmployeeProfile } from "../../hooks/useEmployees";
|
||||
import { clearCacheKey, getCachedData } from "../../slices/apiDataManager";
|
||||
import {clearApiCacheKey} from "../../slices/apiCacheSlice";
|
||||
|
||||
const mobileNumberRegex = /^(?:\d{10}|\d{3}[-\s]?\d{3}[-\s]?\d{4})$/;
|
||||
|
||||
const ManageEmployee = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { employeeId } = useParams();
|
||||
const {
|
||||
employee,
|
||||
error,
|
||||
loading: empLoading,
|
||||
} = useEmployeeProfile(employeeId);
|
||||
dispatch(changeMaster("Job Role"));
|
||||
const { data: job_role, loading } = useMaster();
|
||||
const [isloading, setLoading] = useState(false);
|
||||
const navigation = useNavigate();
|
||||
const [currentEmployee, setCurrentEmployee] = useState();
|
||||
console.log(currentEmployee);
|
||||
|
||||
|
||||
|
||||
|
||||
const ManageEmployee = ({ employee }) => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const {employeeId} = useParams()
|
||||
dispatch(changeMaster("Job Role"))
|
||||
const {data:job_role,loading} = useMaster()
|
||||
const {employees,loading:empLoading} = useEmployees(useSelector((store)=>store.localVariables.projectId))
|
||||
|
||||
const[isloading,setLoading] = useState(false)
|
||||
const navigation = useNavigate()
|
||||
|
||||
|
||||
const[currentEmployee,setCurrentEmployee] = useState()
|
||||
|
||||
|
||||
|
||||
|
||||
const userSchema = z.object({
|
||||
const userSchema = z
|
||||
.object({
|
||||
...(employeeId ? { Id: z.number().optional() } : {}),
|
||||
FirstName: z.string().min(1, {message:"First Name is required"}),
|
||||
FirstName: z.string().min(1, { message: "First Name is required" }),
|
||||
MiddleName: z.string().optional(),
|
||||
LastName: z.string().min(1,{message: "Last Name is required"}),
|
||||
LastName: z.string().min(1, { message: "Last Name is required" }),
|
||||
Email: z.string().optional(),
|
||||
CurrentAddress: z.string().min(1, {message:"Current Address is required"}),
|
||||
BirthDate: z.string().min(1,{message: "Birth Date is required"}),
|
||||
JoiningDate: z.string().min(1, {message:"Joining Date is required"}),
|
||||
EmergencyPhoneNumber:z.string().min(1,{message: "Phone Number is required"})
|
||||
.regex(mobileNumberRegex, {message:"Invalid phone number "}),
|
||||
EmergencyContactPerson: z.string().min(1, {message:"Emergency Contact Person is required"}),
|
||||
CurrentAddress: z
|
||||
.string()
|
||||
.min(1, { message: "Current Address is required" }),
|
||||
BirthDate: z.string().min(1, { message: "Birth Date is required" }),
|
||||
JoiningDate: z.string().min(1, { message: "Joining Date is required" }),
|
||||
EmergencyPhoneNumber: z
|
||||
.string()
|
||||
.min(1, { message: "Phone Number is required" })
|
||||
.regex(mobileNumberRegex, { message: "Invalid phone number " }),
|
||||
EmergencyContactPerson: z
|
||||
.string()
|
||||
.min(1, { message: "Emergency Contact Person is required" }),
|
||||
AadharNumber: z.string().optional(),
|
||||
Gender: z.string().min(1, {message:"Gender is required"}).refine(val => val !== "Select Gender", {
|
||||
message: "Please select a gender"
|
||||
Gender: z
|
||||
.string()
|
||||
.min(1, { message: "Gender is required" })
|
||||
.refine((val) => val !== "Select Gender", {
|
||||
message: "Please select a gender",
|
||||
}),
|
||||
PanNumber: z.string().optional(),
|
||||
PeramnentAddress: z.string().min(1,{message: "Permanent Address is required"}),
|
||||
PhoneNumber: z.string().min(1,{message: "Phone Number is required"})
|
||||
.regex(mobileNumberRegex, {message:"Invalid phone number "}),
|
||||
JobRoleId:z.string().min(1,{message:"Role is required"}),
|
||||
Documents: z
|
||||
.array(z.unknown())
|
||||
// .optional()
|
||||
.default([])
|
||||
.refine((value) => value.length > 0, {
|
||||
message: "Documents is required",
|
||||
})
|
||||
.refine((value) => {
|
||||
return value.every((file) => file.size <= 5000000); //this required if want
|
||||
}, "Each file must be less than 5MB")
|
||||
.refine((value) => {
|
||||
if (!value || value.length === 0) return true;
|
||||
return value.every((file) =>
|
||||
["application/pdf", "application/msword", "image/jpeg", "image/png"].includes(file.type)
|
||||
);
|
||||
}, "Only PDF, Word, and image files are allowed"),
|
||||
Photo: z
|
||||
.unknown()
|
||||
.default(null)
|
||||
PeramnentAddress: z
|
||||
.string()
|
||||
.min(1, { message: "Permanent Address is required" }),
|
||||
PhoneNumber: z
|
||||
.string()
|
||||
.min(1, { message: "Phone Number is required" })
|
||||
.regex(mobileNumberRegex, { message: "Invalid phone number " }),
|
||||
JobRoleId: z.string().min(1, { message: "Role is required" }),
|
||||
// Documents: z
|
||||
// .array(z.unknown())
|
||||
// // .optional()
|
||||
// .default([])
|
||||
// .refine((value) => value.length > 0, {
|
||||
// message: "Documents is required",
|
||||
// })
|
||||
// .refine((value) => {
|
||||
// return value.every((file) => file.size <= 5000000); //this required if want
|
||||
// }, "Each file must be less than 5MB")
|
||||
// .refine((value) => {
|
||||
// if (!value || value.length === 0) return true;
|
||||
// return value.every((file) =>
|
||||
// ["application/pdf", "application/msword", "image/jpeg", "image/png"].includes(file.type)
|
||||
// );
|
||||
// }, "Only PDF, Word, and image files are allowed"),
|
||||
// Photo: z
|
||||
// .unknown()
|
||||
// .default(null)
|
||||
|
||||
.refine((value) => value !== undefined && value !== null, {
|
||||
message: "Photo is required",
|
||||
// .refine((value) => value !== undefined && value !== null, {
|
||||
// message: "Photo is required",
|
||||
// })
|
||||
// .refine((value) => value?.size <= 5000000, { //this required if want
|
||||
// message: "The file must be less than 5MB", //size
|
||||
// })
|
||||
// .refine((value) => {
|
||||
// if (!value || value.length === 0) return true;
|
||||
// return value && ["image/jpeg", "image/png", "image/jpg"].includes(value?.type);
|
||||
// }, { message: "Only JPEG and PNG images are allowed" }),
|
||||
})
|
||||
.refine((value) => value?.size <= 5000000, { //this required if want
|
||||
message: "The file must be less than 5MB", //size
|
||||
})
|
||||
.refine((value) => {
|
||||
if (!value || value.length === 0) return true;
|
||||
return value && ["image/jpeg", "image/png", "image/jpg"].includes(value?.type);
|
||||
}, { message: "Only JPEG and PNG images are allowed" }),
|
||||
|
||||
}).superRefine((data, ctx) => {
|
||||
.superRefine((data, ctx) => {
|
||||
if (!data.AadharNumber && !data.PanNumber) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
@ -103,12 +112,16 @@ const ManageEmployee = ({ employee }) => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const {register,control, handleSubmit, formState: { errors },reset ,getValues} = useForm({
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
getValues,
|
||||
} = useForm({
|
||||
resolver: zodResolver(userSchema),
|
||||
defaultValues:{
|
||||
defaultValues: {
|
||||
Id: currentEmployee?.id || "",
|
||||
FirstName: currentEmployee?.firstName || "",
|
||||
MiddleName: currentEmployee?.middleName || "",
|
||||
@ -125,135 +138,60 @@ const ManageEmployee = ({ employee }) => {
|
||||
PeramnentAddress: currentEmployee?.peramnentAddress || "",
|
||||
PhoneNumber: currentEmployee?.phoneNumber || "",
|
||||
JobRoleId: currentEmployee?.jobRoleId || "",
|
||||
Documents: currentEmployee?.documents || [],
|
||||
Photo: currentEmployee?.photo || null
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// const [formData, setFormData] = useState({
|
||||
// Id: employee?.Id || -1,
|
||||
// FirstName: employee?.FirstName || "fname",
|
||||
// MiddleName: employee?.MiddleName || "mname",
|
||||
// LastName: employee?.LastName || "lname",
|
||||
// Email: employee?.Email || "email",
|
||||
// CurrentAddress: employee?.CurrentAddress || "caddress",
|
||||
// BirthDate: employee?.BirthDate || "bdate",
|
||||
// JoiningDate: employee?.JoiningDate || "jdate",
|
||||
// EmergencyPhoneNumber: employee?.EmergencyPhoneNumber || "enumber",
|
||||
// EmergencyContactPerson: employee?.EmergencyContactPerson || "cperson",
|
||||
// AadharNumber: employee?.AadharNumber || "anumber",
|
||||
// Gender: employee?.Gender || "gender",
|
||||
// PanNumber: employee?.PanNumber || "pnumber",
|
||||
// PeramnentAddress: employee?.PeramnentAddress || "paddress",
|
||||
// PhoneNumber: employee?.PhoneNumber || "phnmber",
|
||||
// RoleId: "2c8d0808-c421-11ef-9b93-0242ac110002",
|
||||
// Photo: null,
|
||||
// Documents: [],
|
||||
// });
|
||||
|
||||
// const handleChange = (e) => {
|
||||
// const { name, value } = e.target;
|
||||
// setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
// };
|
||||
|
||||
// const handleFileChange = (e) => {
|
||||
// const { name, files } = e.target;
|
||||
// if (name === "Photo") {
|
||||
// setFormData((prev) => ({ ...prev, Photo: files[0] }));
|
||||
// } else if (name === "Documents") {
|
||||
// setFormData((prev) => ({ ...prev, Documents: Array.from(files) }));
|
||||
// }
|
||||
// };
|
||||
|
||||
// Documents: currentEmployee?.documents || [],
|
||||
// Photo: currentEmployee?.photo || null
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setLoading(true)
|
||||
setLoading(true);
|
||||
|
||||
const formData = getValues();
|
||||
|
||||
const formDataToSend = new FormData();
|
||||
|
||||
if(!employeeId){
|
||||
delete formData["Id"]
|
||||
if (!employeeId) {
|
||||
delete formData["Id"];
|
||||
}
|
||||
|
||||
for (const key in formData) {
|
||||
|
||||
|
||||
if (key === "Documents") {
|
||||
|
||||
formData[key]?.forEach((file, index) => {
|
||||
formDataToSend.append(`Documents`, file);
|
||||
});
|
||||
} else if (key === "Photo" && formData[key]) {
|
||||
|
||||
formDataToSend.append("Photo", formData[key]);
|
||||
} else{
|
||||
|
||||
formDataToSend.append(key, formData[key]);
|
||||
}
|
||||
|
||||
// if (key === "Documents") {
|
||||
// formData[key]?.forEach((file, index) => {
|
||||
// formDataToSend.append(`Documents`, file);
|
||||
// });
|
||||
// } else if (key === "Photo" && formData[key]) {
|
||||
// formDataToSend.append("Photo", formData[key]);
|
||||
// } else{
|
||||
// formDataToSend.append(key, formData[key]);
|
||||
// }
|
||||
}
|
||||
|
||||
EmployeeRepository.manageEmployee(formDataToSend)
|
||||
.then((response) => {
|
||||
showToast("Employee details updated successfully.", "success");
|
||||
navigation("/employees")
|
||||
console.log(response)
|
||||
.then( ( response ) =>
|
||||
{
|
||||
|
||||
showToast("Employee details updated successfully.", "success" );
|
||||
clearCacheKey("employeeListByProject")
|
||||
clearCacheKey("allEmployeeList")
|
||||
navigation("/employees");
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
setLoading(false);
|
||||
});
|
||||
setLoading(false)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// const onSubmitForm = async (e) => {
|
||||
// e.preventDefault();
|
||||
// const formDataToSend = new FormData();
|
||||
|
||||
// for (const key in formData) {
|
||||
// if (key === "Documents") {
|
||||
// formData[key].forEach((file) =>
|
||||
// formDataToSend.append("Documents", file)
|
||||
// );
|
||||
// } else if (key === "Photo" && formData[key]) {
|
||||
// formDataToSend.append("Photo", formData[key]);
|
||||
// } else {
|
||||
// formDataToSend.append(key, formData[key]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// EmployeeRepository.manageEmployee(formDataToSend)
|
||||
// .then((response) => {
|
||||
// showToast("Employee details updated successfully.", "success");
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// showToast(error.message, "error");
|
||||
// });
|
||||
|
||||
// // api
|
||||
// // .post("/api/employee/manage", formDataToSend)
|
||||
// // .then((data) => {
|
||||
// // showToast("Employee details updated successfully.", "success");
|
||||
// // })
|
||||
// // .catch((error) => {
|
||||
// // showToast(error.message, "error");
|
||||
// // });
|
||||
// };
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!employeeId || !employees?.length) return;
|
||||
|
||||
const foundEmployee = employees.find((emp) => emp.id == employeeId);
|
||||
setCurrentEmployee(foundEmployee || null);
|
||||
}, [employeeId, employees]);
|
||||
if (!loading && !error && employee) {
|
||||
setCurrentEmployee(employee);
|
||||
}
|
||||
}, [loading, error, employee]);
|
||||
|
||||
dispatch(changeMaster("Job Role"));
|
||||
|
||||
useEffect(() => {
|
||||
reset(
|
||||
@ -268,23 +206,22 @@ const ManageEmployee = ({ employee }) => {
|
||||
BirthDate: formatDate(currentEmployee.birthDate) || "",
|
||||
JoiningDate: formatDate(currentEmployee.joiningDate) || "",
|
||||
EmergencyPhoneNumber: currentEmployee.emergencyPhoneNumber || "",
|
||||
EmergencyContactPerson: currentEmployee.emergencyContactPerson || "",
|
||||
EmergencyContactPerson:
|
||||
currentEmployee.emergencyContactPerson || "",
|
||||
AadharNumber: currentEmployee.aadharNumber || "",
|
||||
Gender: currentEmployee.gender || "",
|
||||
PanNumber: currentEmployee.panNumber || "",
|
||||
PeramnentAddress: currentEmployee.peramnentAddress || "",
|
||||
PhoneNumber: currentEmployee.phoneNumber || "",
|
||||
JobRoleId: currentEmployee.jobRoleId || "",
|
||||
Documents: currentEmployee.documents || [],
|
||||
Photo: currentEmployee.photo || null,
|
||||
JobRoleId: currentEmployee.jobRoleId?.toString() || "",
|
||||
// Documents: currentEmployee.documents || [],
|
||||
// Photo: currentEmployee.photo || null,
|
||||
}
|
||||
: {} // Empty object resets the form
|
||||
);
|
||||
}, [currentEmployee, reset]);
|
||||
|
||||
|
||||
dispatch(changeMaster("JobRole"));
|
||||
|
||||
console.log(currentEmployee);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -296,56 +233,73 @@ const ManageEmployee = ({ employee }) => {
|
||||
{" "}
|
||||
{employee ? "Update Employee" : "Create Employee"}
|
||||
</h6>
|
||||
|
||||
</div>
|
||||
<div className="card-body">
|
||||
{(!currentEmployee && empLoading) && (<p>Loading Employee Data...</p>)}
|
||||
{!currentEmployee && empLoading && (
|
||||
<p>Loading Employee Data...</p>
|
||||
)}
|
||||
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="row mb-3">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-4">
|
||||
{" "}
|
||||
<div className="form-text text-start">First Name</div>
|
||||
<input
|
||||
type="text"
|
||||
name="FirstName"
|
||||
|
||||
{...register("FirstName")}
|
||||
className="form-control form-control-sm"
|
||||
id="FirstName"
|
||||
placeholder="First Name"
|
||||
/>
|
||||
{errors.FirstName && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.FirstName.message}</div>}
|
||||
{errors.FirstName && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.FirstName.message}
|
||||
</div>
|
||||
)}
|
||||
</div>{" "}
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Middle Name</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
|
||||
{...register("MiddleName")}
|
||||
className="form-control form-control-sm"
|
||||
id="MiddleName"
|
||||
placeholder="Middle Name"
|
||||
/>
|
||||
{errors.MiddleName && <div className="danger-text text-start " style={{fontSize:"12px"}}>{errors.MiddleName.message}</div>}
|
||||
{errors.MiddleName && (
|
||||
<div
|
||||
className="danger-text text-start "
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.MiddleName.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Last Name</div>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
{...register("LastName")}
|
||||
className="form-control form-control-sm"
|
||||
id="LastName"
|
||||
placeholder="Last Name"
|
||||
/>
|
||||
{errors.LastName && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.LastName.message}</div>}
|
||||
{errors.LastName && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.LastName.message}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-6">
|
||||
<div className="form-text text-start">Email</div>
|
||||
|
||||
@ -353,14 +307,20 @@ const ManageEmployee = ({ employee }) => {
|
||||
<input
|
||||
type="email"
|
||||
id="Email"
|
||||
|
||||
{...register("Email")}
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Email"
|
||||
aria-label=""
|
||||
aria-describedby="Email"
|
||||
/>
|
||||
{errors.Email && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.Email.message}</div>}
|
||||
{errors.Email && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.Email.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* </div> */}
|
||||
</div>
|
||||
@ -368,41 +328,50 @@ const ManageEmployee = ({ employee }) => {
|
||||
<div className="form-text text-start">Phone Number</div>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
keyboardType="numeric"
|
||||
id="PhoneNumber"
|
||||
|
||||
{...register("PhoneNumber")}
|
||||
className="form-control form-control-sm"
|
||||
placeholder="000 000 0000"
|
||||
/>
|
||||
{errors.PhoneNumber && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.PhoneNumber.message}</div>}
|
||||
|
||||
{errors.PhoneNumber && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.PhoneNumber.message}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3"></div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Gender</div>
|
||||
|
||||
<div className="input-group input-group-merge ">
|
||||
<select
|
||||
className="form-select form-select-sm "
|
||||
|
||||
{...register("Gender")}
|
||||
id="Gender"
|
||||
aria-label=""
|
||||
>
|
||||
<option disabled value="">Select Gender</option>
|
||||
<option disabled value="">
|
||||
Select Gender
|
||||
</option>
|
||||
<option value="Male">Male </option>
|
||||
<option value="Female">Female</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
{errors.Gender && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.Gender.message}</div>}
|
||||
{errors.Gender && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.Gender.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Birth Date</div>
|
||||
@ -411,14 +380,18 @@ const ManageEmployee = ({ employee }) => {
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
type="date"
|
||||
|
||||
{...register("BirthDate")}
|
||||
id="BirthDate"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
{errors.BirthDate && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.BirthDate.message}</div>}
|
||||
{errors.BirthDate && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.BirthDate.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Joining Date</div>
|
||||
@ -427,30 +400,40 @@ const ManageEmployee = ({ employee }) => {
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
type="date"
|
||||
|
||||
{...register("JoiningDate")}
|
||||
id="JoiningDate"
|
||||
/>
|
||||
</div>
|
||||
{errors.JoiningDate && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.JoiningDate.message}</div>}
|
||||
{errors.JoiningDate && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.JoiningDate.message}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-6">
|
||||
<div className="form-text text-start">Current Address</div>
|
||||
|
||||
<textarea
|
||||
id="CurrentAddress"
|
||||
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Current Address"
|
||||
aria-label="Current Address"
|
||||
aria-describedby="basic-icon-default-message2"
|
||||
|
||||
{...register("CurrentAddress")}
|
||||
></textarea>
|
||||
{errors.CurrentAddress && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.CurrentAddress.message}</div>}
|
||||
|
||||
{errors.CurrentAddress && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.CurrentAddress.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-6">
|
||||
<div className="form-text text-start">Permnant Address</div>
|
||||
@ -461,21 +444,26 @@ const ManageEmployee = ({ employee }) => {
|
||||
placeholder="Permnant Address"
|
||||
aria-label="Permnant Address"
|
||||
aria-describedby="basic-icon-default-message2"
|
||||
|
||||
{...register("PeramnentAddress")}
|
||||
></textarea>
|
||||
{errors.PeramnentAddress && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.PeramnentAddress.message}</div>}
|
||||
|
||||
{errors.PeramnentAddress && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.PeramnentAddress.message}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
{" "}
|
||||
<div className="divider">
|
||||
<div className="divider-text">Other Information</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-4">
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Role</div>
|
||||
<div className="input-group input-group-merge ">
|
||||
<select
|
||||
@ -487,14 +475,19 @@ const ManageEmployee = ({ employee }) => {
|
||||
<option disabled value="">
|
||||
Select Role
|
||||
</option>
|
||||
{job_role?.map((item)=>(
|
||||
{job_role?.map((item) => (
|
||||
<option value={item?.id}>{item?.name} </option>
|
||||
))}
|
||||
|
||||
|
||||
</select>
|
||||
</div>
|
||||
{errors.JobRoleId && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.JobRoleId.message}</div>}
|
||||
{errors.JobRoleId && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.JobRoleId.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">
|
||||
@ -502,15 +495,19 @@ const ManageEmployee = ({ employee }) => {
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
{...register("EmergencyContactPerson")}
|
||||
className="form-control form-control-sm"
|
||||
id="EmergencyContactPerson"
|
||||
|
||||
placeholder="Contact Person"
|
||||
/>
|
||||
{errors.EmergencyContactPerson && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.EmergencyContactPerson.message}</div>}
|
||||
|
||||
{errors.EmergencyContactPerson && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.EmergencyContactPerson.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">
|
||||
@ -518,18 +515,22 @@ const ManageEmployee = ({ employee }) => {
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
{...register("EmergencyPhoneNumber")}
|
||||
className="form-control form-control-sm phone-mask"
|
||||
id="EmergencyPhoneNumber"
|
||||
placeholder="Phone Number"
|
||||
/>
|
||||
{errors.EmergencyPhoneNumber && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.EmergencyPhoneNumber.message}</div>}
|
||||
|
||||
{errors.EmergencyPhoneNumber && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.EmergencyPhoneNumber.message}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-6">
|
||||
<div className="form-text text-start">AADHAR Number</div>
|
||||
|
||||
@ -540,29 +541,32 @@ const ManageEmployee = ({ employee }) => {
|
||||
id="AadharNumber"
|
||||
placeholder="AADHAR Number"
|
||||
/>
|
||||
{errors.root?.AadharNumber && <div className="danger-text text-start">{errors.AadharNumber.root?.message}</div>}
|
||||
|
||||
{errors.root?.AadharNumber && (
|
||||
<div className="danger-text text-start">
|
||||
{errors.AadharNumber.root?.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-6">
|
||||
<div className="form-text text-start">PAN Number</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
|
||||
{...register("PanNumber")}
|
||||
className="form-control form-control-sm"
|
||||
id="PanNumber"
|
||||
placeholder="PAN Number"
|
||||
/>
|
||||
{/* {errors.PanNumber && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.PanNumber.message}</div>} */}
|
||||
|
||||
</div>
|
||||
{(errors.PanNumber || errors.AadharNumber) && (
|
||||
<div className="danger-text text-start">{errors.PanNumber?.message || errors.AadharNumber?.message}</div>
|
||||
<div className="danger-text text-start">
|
||||
{errors.PanNumber?.message ||
|
||||
errors.AadharNumber?.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
</div>
|
||||
{/* <div className="row mb-3">
|
||||
<div className="col-sm-12">
|
||||
<div className="form-text text-start">Upload Photo</div>
|
||||
<Controller
|
||||
@ -581,8 +585,8 @@ const ManageEmployee = ({ employee }) => {
|
||||
/>
|
||||
{errors.Photo && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.Photo.message}</div>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
</div> */}
|
||||
{/* <div className="row mb-3">
|
||||
<div className="col-sm-12">
|
||||
<div className="form-text text-start">Upload Documents</div>
|
||||
|
||||
@ -613,19 +617,17 @@ const ManageEmployee = ({ employee }) => {
|
||||
{errors.Documents && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.Documents.message}</div>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{employeeId && (
|
||||
{employeeId && (
|
||||
<div className="row mb-3 d-none">
|
||||
<div className="col-sm-12">
|
||||
<input type="number" name="id" {...register("Id")} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
) }
|
||||
|
||||
|
||||
<div className="row justify-content-start">
|
||||
<div className="row justify-content-start">
|
||||
<div className="col-sm-12">
|
||||
<button
|
||||
aria-label="manage employee"
|
||||
@ -633,7 +635,11 @@ const ManageEmployee = ({ employee }) => {
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={isloading}
|
||||
>
|
||||
{isloading ? "Please Wait..." : employeeId ? "Update" : "Create"}
|
||||
{isloading
|
||||
? "Please Wait..."
|
||||
: employeeId
|
||||
? "Update"
|
||||
: "Create"}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@ -644,9 +650,8 @@ const ManageEmployee = ({ employee }) => {
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -656,5 +661,3 @@ const ManageEmployee = ({ employee }) => {
|
||||
};
|
||||
|
||||
export default ManageEmployee;
|
||||
|
||||
|
||||
|
@ -5,6 +5,9 @@ import { z } from 'zod';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { RolesRepository } from '../../repositories/MastersRepository';
|
||||
import { useEmployeeRoles } from '../../hooks/useEmployees';
|
||||
import {useDispatch} from 'react-redux';
|
||||
import {changeMaster} from '../../slices/localVariablesSlice';
|
||||
import showToast from '../../services/toastService';
|
||||
|
||||
|
||||
const formSchema = z.object({
|
||||
@ -18,10 +21,11 @@ const formSchema = z.object({
|
||||
|
||||
const ManageRole = ({employeeId,onClosed}) => {
|
||||
|
||||
const disptach = useDispatch()
|
||||
disptach(changeMaster("Role"))
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { employeeRoles,loading } = useEmployeeRoles(employeeId);
|
||||
|
||||
const { data } = useMaster();
|
||||
const {data,loading:roleLoading} = useMaster();
|
||||
|
||||
const buildDefaultRoles = () => {
|
||||
const defaults = {};
|
||||
@ -51,7 +55,8 @@ const ManageRole = ({employeeId,onClosed}) => {
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
useEffect( () =>
|
||||
{
|
||||
if (Object.keys(initialRoles).length > 0) {
|
||||
reset({
|
||||
selectedRole: initialRoles,
|
||||
@ -72,25 +77,29 @@ const ManageRole = ({employeeId,onClosed}) => {
|
||||
}
|
||||
}
|
||||
|
||||
RolesRepository.createEmployeeRoles(result).then((resp)=>{
|
||||
RolesRepository.createEmployeeRoles( result ).then( ( resp ) =>
|
||||
{
|
||||
|
||||
showToast( "Role assigned successfully", "success" )
|
||||
setIsLoading(false)
|
||||
onClosed()
|
||||
}).catch((err)=>{
|
||||
console.log(err)
|
||||
console.log( err )
|
||||
setIsLoading(false)
|
||||
|
||||
showToast(err.message,"error")
|
||||
})
|
||||
setIsLoading(false)
|
||||
};
|
||||
|
||||
|
||||
if(loading){
|
||||
<div className='text-center'>Loading...</div>
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div className={`modal fade `} id="managerole-modal" tabindex="-1" aria-hidden="true">
|
||||
<div className="modal-dialog modal-simple modal-md d-flex align-items-center justify-content-center">
|
||||
<div className="modal-content ">
|
||||
<div className={`modal fade `} id="managerole-modal" tabindex="-1" aria-hidden="true" >
|
||||
<div className="modal-dialog modal-simple modal-md d-flex align-items-center justify-content-center" >
|
||||
<div className="modal-content " >
|
||||
<div className="modal-body" >
|
||||
<button
|
||||
type="button"
|
||||
@ -99,17 +108,20 @@ const ManageRole = ({employeeId,onClosed}) => {
|
||||
aria-label="Close"
|
||||
|
||||
></button>
|
||||
|
||||
<div className="container" >
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} >
|
||||
<div className='text-start my-0'>
|
||||
<p className='lead'>Select Roles</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div className='d-flex flex-wrap justify-content-between align-items-center pb-5 '>
|
||||
{data.map((item) => (
|
||||
<div className='d-flex flex-wrap justify-content-between align-items-center pb-5 ' style={{height: "70vh", overflowY: "scroll"}} >
|
||||
|
||||
{(loading || roleLoading) && <p>Loading...</p>}
|
||||
{data && data.map((item) => (
|
||||
<>
|
||||
<div className="d-flex mx-3 my-2">
|
||||
<div className="form-check ms-2">
|
||||
|
||||
<div className="col-md-6 col-lg-4 mb-4">
|
||||
<div className="form-check ms-2 text-start">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
@ -119,7 +131,7 @@ const ManageRole = ({employeeId,onClosed}) => {
|
||||
})}
|
||||
|
||||
/>
|
||||
<label className="form-check-label" htmlFor={item.id}>{item.role}</label>
|
||||
<label className="form-check-label text-bold" htmlFor={item.id}><small>{item.role || "--"}</small></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -132,7 +144,7 @@ const ManageRole = ({employeeId,onClosed}) => {
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-primary me-3">
|
||||
{isLoading? "Please Wait...":"Submit"}
|
||||
{ isLoading ? "Please Wait":"Submit"}
|
||||
</button>
|
||||
<button
|
||||
type="reset"
|
||||
@ -145,7 +157,7 @@ const ManageRole = ({employeeId,onClosed}) => {
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
@ -1,8 +1,24 @@
|
||||
import getGreetingMessage from "../../utils/greetingHandler";
|
||||
import { clearAllCache } from "../../slices/apiDataManager";
|
||||
import AuthRepository from "../../repositories/AuthRepository";
|
||||
import {useDispatch, } from "react-redux";
|
||||
import {changeMaster} from "../../slices/localVariablesSlice";
|
||||
import useMaster from "../../hooks/masterHook/useMaster";
|
||||
import {useProfile} from "../../hooks/useProfile";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
const Header = () => {
|
||||
const Header = () =>
|
||||
{
|
||||
const {profile} = useProfile()
|
||||
const dispatch = useDispatch( changeMaster( "Job Role" ) )
|
||||
const {data, loading} = useMaster()
|
||||
const navigate = useNavigate()
|
||||
const getRole = (roles,joRoleId) =>
|
||||
{
|
||||
if (!Array.isArray(roles)) return "User";
|
||||
let role = roles.find( role => role.id === joRoleId )
|
||||
return role ? role.name : "User";
|
||||
}
|
||||
const handleLogout = (e) => {
|
||||
e.preventDefault(); // Prevent default anchor behavior (e.g., page reload)
|
||||
logout();
|
||||
@ -32,44 +48,6 @@ const Header = () => {
|
||||
window.location.href = "/auth/login";
|
||||
});
|
||||
|
||||
// api
|
||||
// .post("/api/auth/logout", data)
|
||||
// .then((data) => {
|
||||
// localStorage.removeItem("jwtToken");
|
||||
// localStorage.removeItem("refreshToken");
|
||||
// localStorage.removeItem("user");
|
||||
// window.location.href = "/auth/login";
|
||||
// localStorage.clear();
|
||||
// clearAllCache();
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// localStorage.removeItem("jwtToken");
|
||||
// localStorage.removeItem("refreshToken");
|
||||
// localStorage.removeItem("user");
|
||||
// window.location.href = "/auth/login";
|
||||
// clearAllCache();
|
||||
// });
|
||||
|
||||
// await axiosClient
|
||||
// .post("/api/auth/logout", data, {
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${localStorage.getItem("jwt")}`, // Pass the JWT token
|
||||
// },
|
||||
// })
|
||||
// .then((response) => {
|
||||
// // Clear all local storage items related to the session
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// localStorage.removeItem("jwtToken");
|
||||
// localStorage.removeItem("refreshToken");
|
||||
// localStorage.removeItem("user"); // Remove any user-related info
|
||||
// });
|
||||
|
||||
// // Optionally clear all localStorage (uncomment if needed)
|
||||
// // localStorage.clear();
|
||||
|
||||
// // Redirect to login page or home page
|
||||
// window.location.href = "/auth/login"; // Adjust the route as needed
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error during logout:",
|
||||
@ -77,6 +55,11 @@ const Header = () => {
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleProfilePage = ()=>{
|
||||
navigate(`/employee/${profile?.employeeInfo?.id}?for=account`)
|
||||
}
|
||||
return (
|
||||
<nav
|
||||
className="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
|
||||
@ -95,7 +78,7 @@ const Header = () => {
|
||||
className="navbar-nav-right d-flex align-items-center"
|
||||
id="navbar-collapse"
|
||||
>
|
||||
<marquee> {getGreetingMessage("Ramchandra")}</marquee>
|
||||
<marquee> {getGreetingMessage(profile?.employeeInfo?.firstName)}</marquee>
|
||||
<ul className="navbar-nav flex-row align-items-center ms-auto">
|
||||
<li className="nav-item navbar-dropdown dropdown-user dropdown">
|
||||
<a
|
||||
@ -114,11 +97,11 @@ const Header = () => {
|
||||
</div>
|
||||
</a>
|
||||
<ul className="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<li onClick={handleProfilePage}>
|
||||
<a
|
||||
aria-label="go to profile"
|
||||
className="dropdown-item"
|
||||
href="#"
|
||||
|
||||
>
|
||||
<div className="d-flex">
|
||||
<div className="flex-shrink-0 me-3">
|
||||
@ -132,8 +115,8 @@ const Header = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-grow-1">
|
||||
<span className="fw-medium d-block">Ramchandra</span>
|
||||
<small className="text-muted">Admin</small>
|
||||
<span className="fw-medium d-block">{profile?.employeeInfo?.firstName}</span>
|
||||
<small className="text-muted">{ getRole(data,profile?.employeeInfo?.joRoleId)}</small>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@ -141,11 +124,11 @@ const Header = () => {
|
||||
<li>
|
||||
<div className="dropdown-divider"></div>
|
||||
</li>
|
||||
<li>
|
||||
<li onClick={handleProfilePage} >
|
||||
<a
|
||||
aria-label="go to profile"
|
||||
className="dropdown-item"
|
||||
href="#"
|
||||
className="dropdown-item cusor-pointer"
|
||||
|
||||
>
|
||||
<i className="bx bx-user me-2"></i>
|
||||
<span className="align-middle">My Profile</span>
|
||||
@ -155,7 +138,7 @@ const Header = () => {
|
||||
<a
|
||||
aria-label="go to setting"
|
||||
className="dropdown-item"
|
||||
href="#"
|
||||
|
||||
>
|
||||
<i className="bx bx-cog me-2"></i>
|
||||
<span className="align-middle">Settings</span>
|
||||
@ -165,7 +148,7 @@ const Header = () => {
|
||||
<a
|
||||
aria-label="go to billing"
|
||||
className="dropdown-item"
|
||||
href="#"
|
||||
|
||||
>
|
||||
<span className="d-flex align-items-center align-middle">
|
||||
<i className="flex-shrink-0 bx bx-credit-card me-2"></i>
|
||||
|
@ -1,14 +1,20 @@
|
||||
import React from "react";
|
||||
import { Link, NavLink, useLocation } from "react-router-dom";
|
||||
import { Link, NavLink, useLocation, useNavigate } from "react-router-dom";
|
||||
import menuData from "../../data/menuData.json";
|
||||
import {getCachedProfileData} from "../../slices/apiDataManager";
|
||||
|
||||
const Sidebar = () => {
|
||||
|
||||
const logineUser = getCachedProfileData()
|
||||
const navigate = useNavigate()
|
||||
const handleLogout = (e) => {
|
||||
e.preventDefault(); // Prevent default anchor behavior (e.g., page reload)
|
||||
e.preventDefault();
|
||||
// logout();
|
||||
};
|
||||
|
||||
const handleProfilePage = ()=>{
|
||||
console.log(profile?.employeeInfo?.id)
|
||||
navigate(`/employee/${profile?.employeeInfo?.id}?for=account`)
|
||||
}
|
||||
return (
|
||||
<aside
|
||||
id="layout-menu"
|
||||
@ -30,7 +36,7 @@ const Sidebar = () => {
|
||||
</Link>
|
||||
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
|
||||
className="layout-menu-toggle menu-link text-large ms-auto"
|
||||
>
|
||||
<i className="bx bx-chevron-left bx-sm d-flex align-items-center justify-content-center"></i>
|
||||
@ -67,11 +73,11 @@ const Sidebar = () => {
|
||||
height="28"
|
||||
className="rounded-circle"
|
||||
/>
|
||||
<span className="d-none d-sm-inline mx-1">Ramchandra</span>
|
||||
<span className="d-none d-sm-inline mx-1">{ logineUser?.employeeInfo?.firstName}</span>
|
||||
</a>
|
||||
<ul className="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<a aria-label="go to profile" className="dropdown-item" href="#">
|
||||
<li onClick={handleProfilePage}>
|
||||
<a aria-label="go to profile" className="dropdown-item">
|
||||
<div className="d-flex">
|
||||
<div className="flex-shrink-0 me-3">
|
||||
<div className="avatar avatar-online">
|
||||
@ -84,7 +90,7 @@ const Sidebar = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-grow-1">
|
||||
<span className="fw-medium d-block">Ramchandra</span>
|
||||
<span className="fw-medium d-block">{ logineUser?.employeeInfo?.firstName}</span>
|
||||
<small className="text-muted">Admin</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -93,8 +99,8 @@ const Sidebar = () => {
|
||||
<li>
|
||||
<div className="dropdown-divider"></div>
|
||||
</li>
|
||||
<li>
|
||||
<a aria-label="go to profile" className="dropdown-item" href="#">
|
||||
<li onClick={handleProfilePage}>
|
||||
<a aria-label="go to profile" className="dropdown-item" >
|
||||
<i className="bx bx-user me-2"></i>
|
||||
<span className="align-middle">My Profile</span>
|
||||
</a>
|
||||
|
@ -1,7 +1,10 @@
|
||||
import React from "react";
|
||||
import React,{useState} from "react";
|
||||
import moment from "moment";
|
||||
import { ProjectStatus } from "../../utils/projectStatus";
|
||||
const AboutProject = ({ data }) => {
|
||||
const AboutProject = ( {data} ) =>
|
||||
{
|
||||
const [CurrentProject,setCurrentProject] = useState(data)
|
||||
|
||||
return (
|
||||
<>
|
||||
{data && (
|
||||
|
60
src/components/Project/AssignEmployeeCard.jsx
Normal file
60
src/components/Project/AssignEmployeeCard.jsx
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import RoleBadge from './RoleBadge';
|
||||
import Avatar from '../common/Avatar';
|
||||
|
||||
const AssignEmployeeCard = ({
|
||||
employee,
|
||||
jobRoles,
|
||||
isChecked,
|
||||
onRoleChange,
|
||||
onCheckboxChange,
|
||||
}) => {
|
||||
const [currentJobRole, setCurrentJobRole] = useState(employee.jobRoleId);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentJobRole(employee.jobRoleId);
|
||||
}, [employee.jobRoleId]);
|
||||
|
||||
const handleRoleChange = (newRoleId) => {
|
||||
setCurrentJobRole(newRoleId);
|
||||
onRoleChange(employee.id, newRoleId);
|
||||
};
|
||||
|
||||
const handleCheckboxChange = () => {
|
||||
if (!employee.isActive) {
|
||||
onCheckboxChange(employee.id);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="assign-employee-card d-flex justify-content-between align-items-center px-2 py-1 border-bottom">
|
||||
<div className="d-flex align-items-center flex-grow-1">
|
||||
<div className="avatar me-3">
|
||||
|
||||
<Avatar firstName={employee.firstName} lastName={employee.l} />
|
||||
</div>
|
||||
<div className="employee-info">
|
||||
<p className="mb-0 d-none d-sm-block">{employee.firstName} {employee.lastName} </p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center">
|
||||
<RoleBadge
|
||||
JobRoles={jobRoles}
|
||||
currentJobRole={currentJobRole}
|
||||
onRoleChange={handleRoleChange}
|
||||
/>
|
||||
<input
|
||||
className="form-check-input ms-3"
|
||||
type="checkbox"
|
||||
checked={isChecked}
|
||||
disabled={employee.isActive}
|
||||
onChange={handleCheckboxChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssignEmployeeCard;
|
@ -2,7 +2,7 @@ import React, { useState,useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import useMaster from "../../hooks/masterHook/useMaster";
|
||||
import { jobRoles,employee } from "../../data/masters";
|
||||
import { employee } from "../../data/masters";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { getCachedData } from "../../slices/apiDataManager";
|
||||
@ -17,7 +17,7 @@ const AssignRoleModel = ( {assignData,onClose}) => {
|
||||
const[target,setTraget] = useState("")
|
||||
const dispatch = useDispatch()
|
||||
const {data,loading} = useMaster()
|
||||
const jobRoleData = getCachedData("JobRole")
|
||||
const jobRoleData = getCachedData("Job Role")
|
||||
|
||||
|
||||
const [selectedRole, setSelectedRole] = useState("all");
|
||||
@ -77,7 +77,7 @@ const onSubmit = (data) => {
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
dispatch(changeMaster("JobRole"))
|
||||
dispatch(changeMaster("Job Role"))
|
||||
return ()=> setSelectedRole("all")
|
||||
},[dispatch])
|
||||
|
||||
|
@ -1,44 +1,112 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useForm,Controller } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import {z} from 'zod';
|
||||
|
||||
const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
id: project?.id || "",
|
||||
name: project?.name || "",
|
||||
|
||||
|
||||
const currentDate = new Date().toISOString().split('T')[0];
|
||||
const formatDate = (date) => {
|
||||
if (!date) {
|
||||
return currentDate;
|
||||
}
|
||||
const d = new Date(date);
|
||||
if (isNaN(d.getTime())) {
|
||||
return currentDate;
|
||||
}
|
||||
return d.toISOString().split('T')[0];
|
||||
};
|
||||
const ManageProjectInfo = ( {project,handleSubmitForm, onClose} ) =>
|
||||
{
|
||||
debugger
|
||||
|
||||
const [CurrentProject,setCurrentProject] = useState()
|
||||
const [ isloading, setLoading ] = useState( false )
|
||||
|
||||
|
||||
const projectSchema = z.object( {
|
||||
...(project?.id ? { id: z.number().optional() } : {}),
|
||||
name: z.string().min( 1, {message: "Project Name is required"} ),
|
||||
contactPerson: z.string().min( 1, {message: "Contact Person Name is required"} ),
|
||||
projectAddress: z.string().min( 1, {message: "Address is required"} ),
|
||||
startDate: z.string().min( 1, {message: "Start Date is required"} ).default(currentDate),
|
||||
endDate: z.string().min( 1, {message: "End Date is required"} ).default(currentDate),
|
||||
projectStatusId: z
|
||||
.string()
|
||||
.min(1, { message: "Status is required" })
|
||||
.transform((val) => {
|
||||
const num = Number(val);
|
||||
if (isNaN(num)) {
|
||||
throw new Error("Status must be a valid number");
|
||||
}
|
||||
return num;
|
||||
}),
|
||||
|
||||
} )
|
||||
|
||||
|
||||
const {register, control, handleSubmit, formState: {errors}, reset, getValues} = useForm( {
|
||||
resolver: zodResolver( projectSchema ),
|
||||
defaultValues: {
|
||||
id:project?.id || "",
|
||||
name:project?.name || "",
|
||||
contactPerson:project?.contactPerson || "",
|
||||
projectAddress:project?.projectAddress || "",
|
||||
startDate: formatDate(project?.startDate )|| currentDate,
|
||||
endDate: formatDate(project?.endDate ) || currentDate,
|
||||
projectStatusId: String(project?.projectStatusId || "0"),
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
setCurrentProject(project)
|
||||
reset(
|
||||
project ? {
|
||||
id:project?.id || "",
|
||||
name:project?.name || "",
|
||||
contactPerson: project?.contactPerson || "",
|
||||
projectAddress: project?.projectAddress || "",
|
||||
startDate: project?.startDate || new Date(),
|
||||
endDate: project?.endDate || new Date(),
|
||||
projectStatusId: project?.projectStatusId || "1",
|
||||
});
|
||||
startDate:formatDate(project?.startDate )|| "",
|
||||
endDate: formatDate(project?.endDate ) || "",
|
||||
projectStatusId: String(project.projectStatusId) || "0" ,
|
||||
|
||||
// Handle input change
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({ ...formData, [name]: value });
|
||||
} :{}
|
||||
)
|
||||
},[project,reset,])
|
||||
|
||||
|
||||
|
||||
const onSubmitForm = (updatedProject) => {
|
||||
setLoading( true )
|
||||
handleSubmitForm( updatedProject )
|
||||
};
|
||||
|
||||
// Handle form submission
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
onSubmit(formData); // Pass the updated data to the parent
|
||||
};
|
||||
useEffect( () =>
|
||||
{
|
||||
return ()=>setLoading(false)
|
||||
} )
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-dialog modal-lg modal-simple edit-project-modal" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-body">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
></button>
|
||||
<div className="text-center mb-2">
|
||||
<h5 className="mb-2">
|
||||
{formData.name ? "Edit Project" : "Create Project"}
|
||||
{project?.id ? "Edit Project" : "Create Project"}
|
||||
</h5>
|
||||
</div>
|
||||
<form className="row g-2" onSubmit={handleSubmit}>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="name">
|
||||
Project Name
|
||||
@ -47,11 +115,12 @@ const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
|
||||
className="form-control"
|
||||
placeholder="Project Name"
|
||||
onChange={handleChange}
|
||||
value={formData.name}
|
||||
{...register("name")}
|
||||
/>
|
||||
{errors.name && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.name.message}</div>}
|
||||
</div>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="contactPerson">
|
||||
@ -63,9 +132,10 @@ const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
name="contactPerson"
|
||||
className="form-control"
|
||||
placeholder="Contact Person"
|
||||
onChange={handleChange}
|
||||
value={formData.contactPerson}
|
||||
{...register("contactPerson")}
|
||||
/>
|
||||
{errors.contactPerson && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.contactPerson.message}</div>}
|
||||
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-6">
|
||||
@ -75,11 +145,13 @@ const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
type="date"
|
||||
value={new Date(formData.startDate).toISOString().split("T")[0]}
|
||||
onChange={handleChange}
|
||||
|
||||
name="startDate"
|
||||
{...register("startDate")}
|
||||
id="startDate"
|
||||
/>
|
||||
{errors.startDate && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.startDate.message}</div>}
|
||||
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label" htmlFor="endDate">
|
||||
@ -88,11 +160,13 @@ const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
type="date"
|
||||
value={new Date(formData.endDate).toISOString().split("T")[0]}
|
||||
onChange={handleChange}
|
||||
|
||||
name="endDate"
|
||||
{...register("endDate")}
|
||||
id="endDate"
|
||||
/>
|
||||
{errors.endDate && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.endDate.message}</div>}
|
||||
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label" htmlFor="modalEditUserStatus">
|
||||
@ -103,8 +177,10 @@ const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
name="modalEditUserStatus"
|
||||
className="select2 form-select"
|
||||
aria-label="Default select example"
|
||||
onChange={handleChange}
|
||||
value={formData.projectStatusId}
|
||||
{...register("projectStatusId", {
|
||||
required: "Status is required",
|
||||
valueAsNumber: false
|
||||
})}
|
||||
>
|
||||
<option disabled>Status</option>
|
||||
<option value="1">Active</option>
|
||||
@ -115,6 +191,8 @@ const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
|
||||
<option value="5">Completed</option>
|
||||
</select>
|
||||
{errors.projectStatusId && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.projectStatusId.message}</div>}
|
||||
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-12">
|
||||
@ -127,20 +205,21 @@ const ManageProjectInfo = ({ project, onClose, onSubmit }) => {
|
||||
id="projectAddress"
|
||||
name="projectAddress"
|
||||
className="form-control"
|
||||
onChange={handleChange}
|
||||
value={formData.projectAddress}
|
||||
{...register( "projectAddress" )}
|
||||
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{errors.projectAddress && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.projectAddress.message}</div>}
|
||||
|
||||
</div>
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
Submit
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3" >
|
||||
{isloading ? "Please Wait" : project?.id ? "Update":"Submit"}
|
||||
</button>
|
||||
<button
|
||||
type="reset"
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
>
|
||||
Cancel
|
||||
|
@ -1,253 +1,149 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import EmployeeRepository from "../../repositories/EmployeeRepository";
|
||||
import AssignEmployeeCard from "./AssignEmployeeCard";
|
||||
import {useAllEmployees} from "../../hooks/useEmployees";
|
||||
import useSearch from "../../hooks/useSearch";
|
||||
|
||||
const MapUsers = ({
|
||||
projectId,
|
||||
onClose,
|
||||
empRoles,
|
||||
empJobRoles,
|
||||
onSubmit,
|
||||
clearTrigger,
|
||||
onClearComplete,
|
||||
allocation,
|
||||
}) => {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [searchResults, setSearchResults] = useState([]);
|
||||
const {employeesList, loading, error} = useAllEmployees();
|
||||
const [selectedEmployees, setSelectedEmployees] = useState([]);
|
||||
const [currentEmployee, setCurrentEmployee] = useState(null);
|
||||
const [employeeRoles, setEmployeeRoles] = useState(null);
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (clearTrigger) {
|
||||
setSearchQuery("");
|
||||
setSearchResults([]); // Notify parent that clearing is done
|
||||
setSelectedEmployees([]);
|
||||
setCurrentEmployee(null);
|
||||
}
|
||||
}, [clearTrigger, onClearComplete]);
|
||||
const handleAllocationData = Array.isArray( allocation ) ? allocation : [];
|
||||
|
||||
useEffect(() => {
|
||||
setEmployeeRoles(empRoles);
|
||||
}, [empRoles]);
|
||||
|
||||
const getRole = (id) => {
|
||||
const role = employeeRoles.find((b) => b.id === id);
|
||||
return role;
|
||||
const allocationEmployees = employeesList.map((employee) => {
|
||||
const allocationItem = handleAllocationData.find((alloc) => alloc.employeeId === employee.id);
|
||||
return {
|
||||
...employee,
|
||||
isActive: allocationItem ? allocationItem.isActive : false,
|
||||
jobRoleId: allocationItem ? allocationItem.jobRoleId : employee.jobRoleId,
|
||||
};
|
||||
const searchEmployees = async (query) => {
|
||||
if (!query) {
|
||||
setSearchResults([]);
|
||||
return;
|
||||
} );
|
||||
|
||||
|
||||
|
||||
|
||||
function parseDate(dateStr) {
|
||||
return new Date(dateStr.split('.')[0]);
|
||||
}
|
||||
|
||||
|
||||
const latestAllocations = handleAllocationData.reduce((acc, alloc) => {
|
||||
const existingAlloc = acc[alloc.employeeId];
|
||||
|
||||
if (!existingAlloc) {
|
||||
acc[alloc.employeeId] = alloc;
|
||||
} else {
|
||||
|
||||
const existingDate = parseDate(existingAlloc.reAllocationDate || existingAlloc.allocationDate);
|
||||
const newDate = parseDate(alloc.reAllocationDate || alloc.allocationDate);
|
||||
|
||||
|
||||
if (newDate > existingDate) {
|
||||
acc[alloc.employeeId] = alloc;
|
||||
}
|
||||
try {
|
||||
EmployeeRepository.getEmployeeListByproject(projectid)
|
||||
.then((response) => {
|
||||
setSearchResults(response);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
|
||||
const allocationEmployeesData = employeesList
|
||||
.map((employee) => {
|
||||
const allocationItem = latestAllocations[employee.id];
|
||||
return {
|
||||
...employee,
|
||||
isActive: allocationItem ? allocationItem.isActive : false,
|
||||
};
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setError("Failed to fetch data.");
|
||||
.filter( ( employee ) => employee.isActive === false );
|
||||
|
||||
|
||||
|
||||
const { filteredData, setSearchQuery } = useSearch(allocationEmployeesData, searchText);
|
||||
|
||||
const handleRoleChange = (employeeId, newRoleId) => {
|
||||
setSelectedEmployees((prevSelectedEmployees) =>
|
||||
prevSelectedEmployees.map((emp) =>
|
||||
emp.id === employeeId ? { ...emp, jobRoleId: newRoleId } : emp
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (employeeId) => {
|
||||
setSelectedEmployees((prevSelectedEmployees) => {
|
||||
const updatedEmployees = [...prevSelectedEmployees];
|
||||
const employeeIndex = updatedEmployees.findIndex((emp) => emp.id === employeeId);
|
||||
|
||||
if (employeeIndex !== -1) {
|
||||
const isSelected = !updatedEmployees[employeeIndex].isSelected;
|
||||
updatedEmployees[employeeIndex].isSelected = isSelected;
|
||||
} else {
|
||||
updatedEmployees.push({
|
||||
id: employeeId,
|
||||
isSelected: true,
|
||||
});
|
||||
|
||||
// api
|
||||
// .get(`/api/employee/search/${query}`)
|
||||
// .then((data) => {
|
||||
// setSearchResults(data.data);
|
||||
// //dispatch(cacheApiResponse({ key: "projectslist", data: data }));
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// setError("Failed to fetch data.");
|
||||
// });
|
||||
|
||||
// // const response = await axios.get(`/api/employee/search/${query}`);
|
||||
// let token = localStorage.getItem("jwtToken");
|
||||
|
||||
// const response = await axiosClient.get(`/api/employee/search/${query}`, {
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${token}`,
|
||||
// //"Content-Type": "multipart/form-data",
|
||||
// },
|
||||
// });
|
||||
// setSearchResults(response.data.data);
|
||||
} catch (error) {
|
||||
console.error("Search failed", error);
|
||||
}
|
||||
return updatedEmployees;
|
||||
});
|
||||
};
|
||||
|
||||
const handleRoleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setCurrentEmployee({ ...currentEmployee, [name]: value });
|
||||
};
|
||||
const onSelectAction = (employee) => {
|
||||
setCurrentEmployee(employee);
|
||||
};
|
||||
const handleSelectEmployee = (employee) => {
|
||||
if (!selectedEmployees.some((emp) => emp.id === employee.id)) {
|
||||
setSelectedEmployees([...selectedEmployees, employee]);
|
||||
setCurrentEmployee(null);
|
||||
setSearchQuery("");
|
||||
setSearchResults([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveEmployee = (id) => {
|
||||
setSelectedEmployees(selectedEmployees.filter((emp) => emp.id !== id));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
//formData.projectId = project.id;
|
||||
|
||||
onSubmit(selectedEmployees);
|
||||
const handleSubmit = () => {
|
||||
const selected = selectedEmployees
|
||||
.filter((emp) => emp.isSelected)
|
||||
.map((emp) => ({ empID: emp.id, jobRoleId: emp.jobRoleId }));
|
||||
onSubmit(selected);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-dialog modal-dialog-scrollable mx-sm-auto mx-1 modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-content">
|
||||
{/* <div className="modal-body">
|
||||
<h2>Assign Employees to Project</h2>
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value);
|
||||
searchEmployees(e.target.value);
|
||||
}}
|
||||
placeholder="Search employees..."
|
||||
/>
|
||||
<ul>
|
||||
{searchResults.map((emp) => (
|
||||
<li key={emp.id}>
|
||||
{emp.firstName} {emp.lastName}
|
||||
<button onClick={() => handleSelectEmployee(emp)}>Add</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<h3>Selected Employees:</h3>
|
||||
<ul>
|
||||
{selectedEmployees.map((emp) => (
|
||||
<li key={emp.id}>
|
||||
{emp.firstName} {emp.lastName}
|
||||
<button onClick={() => handleRemoveEmployee(emp.id)}>
|
||||
Remove
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button onClick={handleSubmit}>Assign to Project</button>
|
||||
<button onClick={onClose}>Cancel</button>
|
||||
</div> */}
|
||||
<div className="modal-body">
|
||||
<div className="mb-3">
|
||||
<div className="modal-header">
|
||||
<div className="md-2 mb-1">
|
||||
<div className="input-group">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
value={searchQuery}
|
||||
onChange={(e) => {
|
||||
setSearchQuery(e.target.value);
|
||||
searchEmployees(e.target.value);
|
||||
}}
|
||||
placeholder="Search employees..."
|
||||
type="search"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Search employees.."
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => searchEmployees(searchQuery)}
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="list-group mb-3">
|
||||
{searchResults.map((emp) => (
|
||||
<li
|
||||
</div>
|
||||
<div className="modal-body p-sm-4 p-0">
|
||||
<ul className="list-group border-none mb-2">
|
||||
{loading && !employeesList && <p>Loading...</p>}
|
||||
{filteredData.map((emp) => (
|
||||
<AssignEmployeeCard
|
||||
key={emp.id}
|
||||
className="list-group-item d-flex justify-content-between align-items-center"
|
||||
onClick={() => onSelectAction(emp)}
|
||||
>
|
||||
{emp.firstName} {emp.lastName}
|
||||
{/* <button
|
||||
className="btn btn-success btn-sm"
|
||||
onClick={() => onSelectAction(emp)}
|
||||
>
|
||||
Add
|
||||
</button> */}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{currentEmployee && (
|
||||
<div>
|
||||
<div className="col-12 col-md-12">
|
||||
<span>
|
||||
{currentEmployee.firstName} {currentEmployee.lastName}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="roleId">
|
||||
Select Role
|
||||
</label>
|
||||
<select
|
||||
id="roleId"
|
||||
name="roleId"
|
||||
className="select2 form-select form-select-sm"
|
||||
aria-label="Default select example"
|
||||
onChange={handleRoleChange}
|
||||
value={currentEmployee.roleId}
|
||||
>
|
||||
<option value="0">Select Role</option>
|
||||
{employeeRoles.map((role) => (
|
||||
<option key={role.id} value={role.id}>
|
||||
{role.role}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="col-12 col-md-12">
|
||||
<button
|
||||
className="btn btn-success btn-sm"
|
||||
onClick={() => handleSelectEmployee(currentEmployee)}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
// <div>
|
||||
// <span>
|
||||
// {currentEmployee.firstName} {currentEmployee.lastName}
|
||||
// </span>
|
||||
|
||||
// </div>
|
||||
)}
|
||||
<h6 className="text-start">Selected Employees:</h6>
|
||||
<ul className="list-group">
|
||||
{selectedEmployees.map((emp) => (
|
||||
<li
|
||||
key={emp.id}
|
||||
className="list-group-item d-flex justify-content-between align-items-center"
|
||||
>
|
||||
<div className="row"></div>
|
||||
<div className="col-4">
|
||||
{" "}
|
||||
{emp.firstName} {emp.lastName}
|
||||
</div>
|
||||
<div className="col-4"> {getRole(emp.roleId).role}</div>
|
||||
<div className="col-4">
|
||||
{" "}
|
||||
<button
|
||||
className="btn btn-danger btn-sm"
|
||||
onClick={() => handleRemoveEmployee(emp.id)}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
employee={emp}
|
||||
jobRoles={empJobRoles}
|
||||
isChecked={emp.isSelected}
|
||||
onRoleChange={handleRoleChange}
|
||||
onCheckboxChange={handleCheckboxChange}
|
||||
/>
|
||||
) )}
|
||||
{filteredData.length == 0 && <p>No Data Found</p>}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button className="btn btn-success" onClick={handleSubmit}>
|
||||
Assign to Project
|
||||
</button>
|
||||
<button type="button" className="btn btn-secondary" data-dismiss="modal" aria-label="Close" onClick={onClose}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,65 +1,80 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState,useEffect } from "react";
|
||||
import ManageProjectInfo from "./ManageProjectInfo";
|
||||
import showToast from "../../services/toastService";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import { clearCacheKey } from "../../slices/apiDataManager";
|
||||
import { cacheData,getCachedData } from "../../slices/apiDataManager";
|
||||
import {hasUserPermission} from "../../utils/authUtils";
|
||||
import moment from "moment";
|
||||
|
||||
const ProjectBanner = ({ data }) => {
|
||||
if (data == null) {
|
||||
|
||||
const ProjectBanner = ( {project_data} ) =>
|
||||
{
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const [ CurrentProject, setCurrentProject ] = useState( project_data )
|
||||
if (project_data == null) {
|
||||
return <span>incomplete project information</span>;
|
||||
}
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [project, setProject] = useState(data);
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal(false);
|
||||
|
||||
// Open the modal and set project data (if editing)
|
||||
const openModal = (projectData) => {
|
||||
setIsModalOpen(true);
|
||||
const handleFormSubmit = ( updatedProject ) =>
|
||||
{
|
||||
|
||||
if ( CurrentProject?.id )
|
||||
{
|
||||
ProjectRepository.updateProject(CurrentProject.id,updatedProject).then( ( response ) =>
|
||||
{
|
||||
const updatedProjectData = {
|
||||
...CurrentProject,
|
||||
...response.data,
|
||||
building: CurrentProject.building,
|
||||
};
|
||||
setCurrentProject( updatedProject )
|
||||
|
||||
// Close the modal
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
const modalBackdrop = document.querySelector(".modal-backdrop");
|
||||
if (modalBackdrop) modalBackdrop.remove();
|
||||
};
|
||||
cacheData( `projectinfo-${ CurrentProject.id }`, updatedProjectData );
|
||||
const projects_list = getCachedData("projectslist");
|
||||
if ( projects_list )
|
||||
{
|
||||
const updatedProjectsList = projects_list.map(project =>
|
||||
project.id === CurrentProject.id ? {
|
||||
...project,
|
||||
...response.data,
|
||||
tenant:project.tenant
|
||||
} : project
|
||||
);
|
||||
|
||||
// Handle form submission
|
||||
const handleFormSubmit = (updatedProject) => {
|
||||
console.log("Form submitted:", updatedProject); // Replace this with an API call or state update
|
||||
cacheData("projectslist",updatedProjectsList)
|
||||
}
|
||||
|
||||
ProjectRepository.updateProject(updatedProject)
|
||||
.then((response) => {
|
||||
setProject({
|
||||
...response,
|
||||
});
|
||||
clearCacheKey("projectslist");
|
||||
showToast("Project updated successfully.", "success");
|
||||
closeModal();
|
||||
showToast( "Project updated successfully.", "success" );
|
||||
|
||||
setShowModal(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
showToast( error.message, "error" );
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isModalOpen && (
|
||||
<div
|
||||
className={`modal fade `}
|
||||
id="editproject"
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
className={`modal fade ${showModal ? 'show' : ''}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? 'block' : 'none' }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={project}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleFormSubmit}
|
||||
project={CurrentProject}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
></ManageProjectInfo>
|
||||
</div>
|
||||
)}
|
||||
{/* -------------------- */}
|
||||
<div className="col-12">
|
||||
<div className="card mb-6 pb-0">
|
||||
<div className="user-profile-header d-flex flex-column flex-lg-row text-sm-start text-center mb-2 ">
|
||||
@ -75,40 +90,38 @@ const ProjectBanner = ({ data }) => {
|
||||
<div className="d-flex align-items-md-end align-items-sm-start align-items-center justify-content-md-between justify-content-start mx-5 flex-md-row flex-column gap-4">
|
||||
<div className="user-profile-info">
|
||||
<h4 className="mb-2 mt-lg-1">
|
||||
{project.name ? project.name : "N/A"}
|
||||
{CurrentProject.name ? CurrentProject.name : "N/A"}
|
||||
</h4>
|
||||
<h6 className="mb-1 mt-lg-1">
|
||||
Address:{" "}
|
||||
{project.projectAddress ? project.projectAddress : "N/A"}
|
||||
{CurrentProject.projectAddress ? CurrentProject.projectAddress : "N/A"}
|
||||
</h6>
|
||||
<h6 className="mb-1 mt-lg-1">
|
||||
Contact:{" "}
|
||||
{project.contactPerson ? project.contactPerson : "N/A"}
|
||||
{CurrentProject.contactPerson ? CurrentProject.contactPerson : "N/A"}
|
||||
</h6>
|
||||
<h6 className="mb-1 mt-lg-1">
|
||||
<span>
|
||||
{" "}
|
||||
Start Date:{" "}
|
||||
{project.startDate
|
||||
? new Date(project.startDate)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
{CurrentProject.startDate
|
||||
? moment(CurrentProject.startDate).format("DD-MMM-YYYY")
|
||||
: "N/A"}
|
||||
</span>
|
||||
<span className="ms-5">
|
||||
End Date:{" "}
|
||||
{project.endDate
|
||||
? new Date(project.endDate).toISOString().split("T")[0]
|
||||
{CurrentProject.endDate
|
||||
? moment(CurrentProject.endDate).format("DD-MMM-YYYY")
|
||||
: "N/A"}
|
||||
</span>
|
||||
</h6>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-primary"
|
||||
className={`btn btn-sm btn-primary ${hasUserPermission("53176ebf-c75d-42e5-839f-4508ffac3def") ? "":"d-none"}`}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editproject"
|
||||
onClick={openModal}
|
||||
data-bs-target="#edit-project-modal"
|
||||
onClick={handleShow}
|
||||
>
|
||||
Modify
|
||||
</button>
|
||||
|
@ -1,12 +1,25 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React,{useState,useEffect} from "react";
|
||||
import moment from "moment";
|
||||
import { getDateDifferenceInDays } from "../../utils/dateUtils";
|
||||
import { logInfo, logError } from "../../utils/errorUtil";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {useProjectDetails} from "../../hooks/useProjects";
|
||||
import ManageProjectInfo from "./ManageProjectInfo";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import {cacheData, getCachedData} from "../../slices/apiDataManager";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
const ProjectCard = ({ project }) => {
|
||||
useEffect(() => {
|
||||
// projectCardChart(project);
|
||||
}, []);
|
||||
|
||||
|
||||
const ProjectCard = ( {projectData} ) =>
|
||||
{
|
||||
|
||||
const[projectInfo,setProjectInfo] = useState(projectData)
|
||||
const navigate = useNavigate()
|
||||
const {projects_Details, loading} = useProjectDetails( projectData.id )
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal( false );
|
||||
|
||||
const getProjectStatusName = (statusId) => {
|
||||
switch (statusId) {
|
||||
@ -22,6 +35,7 @@ const ProjectCard = ({ project }) => {
|
||||
return "Completed";
|
||||
}
|
||||
};
|
||||
|
||||
const getProjectStatusColor = (statusId) => {
|
||||
switch (statusId) {
|
||||
case 1:
|
||||
@ -36,13 +50,75 @@ const ProjectCard = ({ project }) => {
|
||||
return "bg-label-dark";
|
||||
}
|
||||
};
|
||||
|
||||
const handleViewProject = (e) => {
|
||||
e.preventDefault(); // Prevent default anchor behavior (e.g., page reload)
|
||||
window.location.href = "/projects/" + project.id;
|
||||
navigate(`/projects/${projectData.id}`)
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleFormSubmit = ( updatedProject ) =>
|
||||
{
|
||||
if ( projectInfo?.id )
|
||||
{
|
||||
ProjectRepository.updateProject(projectInfo.id,updatedProject).then( ( response ) =>
|
||||
{
|
||||
const updatedProjectData = {
|
||||
...projectInfo,
|
||||
...response.data,
|
||||
building:projects_Details.building,
|
||||
};
|
||||
setProjectInfo( updatedProject )
|
||||
if ( getCachedData( `projectinfo-${ projectInfo.id }` ) )
|
||||
{
|
||||
cacheData( `projectinfo-${ projectInfo.id }`, updatedProjectData );
|
||||
|
||||
}
|
||||
const projects_list = getCachedData( "projectslist" );
|
||||
if ( projects_list )
|
||||
{
|
||||
const updatedProjectsList = projects_list.map(project =>
|
||||
project.id == projectInfo.id ? {
|
||||
...project,
|
||||
...response.data,
|
||||
tenant:project.tenant
|
||||
} : project
|
||||
);
|
||||
|
||||
cacheData("projectslist",updatedProjectsList)
|
||||
}
|
||||
|
||||
showToast( "Project updated successfully.", "success" );
|
||||
|
||||
setShowModal(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast( error.message, "error" );
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div
|
||||
className={`modal fade ${showModal ? 'show' : ''}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? 'block' : 'none' }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={projects_Details}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
></ManageProjectInfo>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6 col-lg-4 col-xl-4 order-0 mb-4">
|
||||
<div className="card">
|
||||
<div className="card cursor-pointer">
|
||||
<div className="card-header pb-4">
|
||||
<div className="d-flex align-items-start">
|
||||
<div className="d-flex align-items-center">
|
||||
@ -56,15 +132,15 @@ const ProjectCard = ({ project }) => {
|
||||
<h5 className="mb-0">
|
||||
<a
|
||||
className="stretched-link text-heading"
|
||||
href="#"
|
||||
|
||||
onClick={handleViewProject}
|
||||
>
|
||||
{project.name}
|
||||
{projectInfo.name}
|
||||
</a>
|
||||
</h5>
|
||||
<div className="client-info text-body">
|
||||
<span className="fw-medium">Client: </span>
|
||||
<span>{project.contactPerson}</span>
|
||||
<span>{projectInfo.contactPerson}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -80,9 +156,6 @@ const ProjectCard = ({ project }) => {
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
{/* <a className="dropdown-item" href="javascript:void(0);">
|
||||
View details
|
||||
</a> */}
|
||||
<a
|
||||
aria-label="click to View details"
|
||||
className="dropdown-item"
|
||||
@ -92,67 +165,50 @@ const ProjectCard = ({ project }) => {
|
||||
<span className="align-left">View details</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<li data-bs-toggle="modal"
|
||||
data-bs-target="#edit-project-modal"
|
||||
onClick={handleShow}>
|
||||
<a
|
||||
className="dropdown-item"
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent default link behavior
|
||||
window.location.href = `/project/manage/${project.id}`;
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-pencil me-2"></i>
|
||||
<span className="align-left">Modify</span>
|
||||
<span className="align-left"
|
||||
>Modify</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a className="dropdown-item" href="javascript:void(0);">
|
||||
<a className="dropdown-item" >
|
||||
<i className="bx bx-task me-2"></i>
|
||||
<span className="align-left">Activities</span>
|
||||
</a>
|
||||
</li>
|
||||
{/* <li>
|
||||
<hr className="dropdown-divider"></hr>
|
||||
</li> */}
|
||||
{/* <li>
|
||||
<a
|
||||
className="dropdown-item text-danger"
|
||||
href="javascript:void(0);"
|
||||
>
|
||||
Leave Project
|
||||
</a>
|
||||
</li> */}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="card-body pb-1">
|
||||
<div className="d-flex align-items-center flex-wrap">
|
||||
{/* <div className="bg-lighter px-3 py-2 rounded me-auto mb-4">
|
||||
<p className="mb-1">
|
||||
<span className="fw-medium text-heading">$24.8k</span>/ $18.2k
|
||||
</p>
|
||||
<span className="text-body">Total Budget</span>
|
||||
</div> */}
|
||||
<div className="text-start mb-4">
|
||||
<p className="mb-1">
|
||||
<span className="text-heading fw-medium">Start Date: </span>
|
||||
{project.startDate
|
||||
? moment(project.startDate).format("DD-MMM-YYYY")
|
||||
{projectInfo.startDate
|
||||
? moment(projectInfo.startDate).format("DD-MMM-YYYY")
|
||||
: "NA"}
|
||||
</p>
|
||||
<p className="mb-1">
|
||||
<span className="text-heading fw-medium">Deadline: </span>
|
||||
|
||||
{project.endDate
|
||||
? moment(project.endDate).format("DD-MMM-YYYY")
|
||||
{projectInfo.endDate
|
||||
? moment(projectInfo.endDate).format("DD-MMM-YYYY")
|
||||
: "NA"}
|
||||
</p>
|
||||
<p className="mb-0">{projectInfo.projectAddress}</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<p className="mb-0">{project.projectAddress}</p>
|
||||
</div>
|
||||
<div className="card-body border-top">
|
||||
<div className="d-flex align-items-center mb-4">
|
||||
@ -160,18 +216,18 @@ const ProjectCard = ({ project }) => {
|
||||
<span
|
||||
className={
|
||||
`badge rounded-pill ` +
|
||||
getProjectStatusColor(project.projectStatusId)
|
||||
getProjectStatusColor(projectInfo.projectStatusId)
|
||||
}
|
||||
>
|
||||
{getProjectStatusName(project.projectStatusId)}
|
||||
{getProjectStatusName(projectInfo.projectStatusId)}
|
||||
</span>
|
||||
</p>{" "}
|
||||
<span className="badge bg-label-success ms-auto">
|
||||
{project.startDate &&
|
||||
project.endDate &&
|
||||
{projectInfo.startDate &&
|
||||
projectInfo.endDate &&
|
||||
getDateDifferenceInDays(
|
||||
project.startDate,
|
||||
project.endDate
|
||||
projectInfo.startDate,
|
||||
projectInfo.endDate
|
||||
)}{" "}
|
||||
Days left
|
||||
</span>
|
||||
@ -192,57 +248,10 @@ const ProjectCard = ({ project }) => {
|
||||
</div>
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="d-flex align-items-center">
|
||||
{/* <ul className="list-unstyled d-flex align-items-center avatar-group mb-0 z-2">
|
||||
<li
|
||||
data-bs-toggle="tooltip"
|
||||
data-popup="tooltip-custom"
|
||||
data-bs-placement="top"
|
||||
className="avatar avatar-sm pull-up"
|
||||
aria-label="Vinnie Mostowy"
|
||||
data-bs-original-title="Vinnie Mostowy"
|
||||
>
|
||||
<img
|
||||
className="rounded-circle"
|
||||
src="../../assets/img/avatars/5.png"
|
||||
alt="Avatar"
|
||||
></img>
|
||||
</li>
|
||||
<li
|
||||
data-bs-toggle="tooltip"
|
||||
data-popup="tooltip-custom"
|
||||
data-bs-placement="top"
|
||||
className="avatar avatar-sm pull-up"
|
||||
aria-label="Allen Rieske"
|
||||
data-bs-original-title="Allen Rieske"
|
||||
>
|
||||
<img
|
||||
className="rounded-circle"
|
||||
src="../../assets/img/avatars/00.jpg"
|
||||
alt="Avatar"
|
||||
></img>
|
||||
</li>
|
||||
<li
|
||||
data-bs-toggle="tooltip"
|
||||
data-popup="tooltip-custom"
|
||||
data-bs-placement="top"
|
||||
className="avatar avatar-sm pull-up me-3"
|
||||
aria-label="Julee Rossignol"
|
||||
data-bs-original-title="Julee Rossignol"
|
||||
>
|
||||
<img
|
||||
className="rounded-circle"
|
||||
src="../../assets/img/avatars/6.png"
|
||||
alt="Avatar"
|
||||
></img>
|
||||
</li>
|
||||
<li>
|
||||
<small className="text-muted">280 Employees</small>
|
||||
</li>
|
||||
</ul> */}
|
||||
</div>
|
||||
<div className="ms-auto">
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
|
||||
className="text-muted d-flex align-items-center"
|
||||
>
|
||||
<i className="bx bx-chat me-1"></i> 15
|
||||
@ -252,6 +261,7 @@ const ProjectCard = ({ project }) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
import React from "react";
|
||||
import {hasUserPermission} from "../../utils/authUtils";
|
||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
|
||||
import {INFRASTRUCTURE} from "../../utils/constants";
|
||||
|
||||
const ProjectNav = ( {onPillClick, activePill} ) =>
|
||||
{
|
||||
const HasInfraStructure = useHasUserPermission( INFRASTRUCTURE )
|
||||
|
||||
const ProjectNav = ({ onPillClick, activePill }) => {
|
||||
return (
|
||||
<div className="col-md-12">
|
||||
<div className="nav-align-top">
|
||||
@ -29,7 +35,7 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
||||
<i className="bx bx-group bx-sm me-1_5"></i> Teams
|
||||
</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<li className={`nav-item ${HasInfraStructure ? "":"d-none"} `}>
|
||||
<a
|
||||
className={`nav-link ${activePill === "infra" ? "active" : ""}`}
|
||||
href="#"
|
||||
|
@ -1,6 +1,11 @@
|
||||
import React from "react";
|
||||
import {useEmployeesByProjectAllocated} from "../../hooks/useProjects";
|
||||
|
||||
const ProjectOverview = ({project}) =>
|
||||
{
|
||||
const {projectEmployees} = useEmployeesByProjectAllocated( project.id );
|
||||
let teamSize = projectEmployees.filter( ( emp ) => emp.isActive )
|
||||
|
||||
const ProjectOverview = () => {
|
||||
return (
|
||||
<div className="card mb-6">
|
||||
<div className="card-body">
|
||||
@ -21,7 +26,7 @@ const ProjectOverview = () => {
|
||||
<li className="d-flex align-items-center">
|
||||
<i className="bx bx-user"></i>
|
||||
<span className="fw-medium mx-2">Current team Size:</span>{" "}
|
||||
<span>897</span>
|
||||
<span>{ teamSize?.length}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
57
src/components/Project/RoleBadge.jsx
Normal file
57
src/components/Project/RoleBadge.jsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
|
||||
const RoleBadge = ({ JobRoles, currentJobRole, onRoleChange }) => {
|
||||
const handleRoleSelect = (newRoleId) => {
|
||||
onRoleChange(newRoleId);
|
||||
};
|
||||
|
||||
|
||||
const validJobRoles = Array.isArray(JobRoles) ? JobRoles : [];
|
||||
|
||||
const selectedRole = validJobRoles.find((role) => role.id === currentJobRole);
|
||||
const selectedRoleName = selectedRole ? selectedRole.name : 'Select Job Role';
|
||||
|
||||
if (validJobRoles.length === 0) {
|
||||
return (
|
||||
<div className="badge bg-label-warning d-flex align-items-center lead">
|
||||
<span className="ms-1 px-2 py-1">No Roles Available</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="dropdown py-2 px-3 cursor-pointer">
|
||||
<div
|
||||
className=" dropdown-toggle d-flex align-items-center lead"
|
||||
id="dropdownMenuButton"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span className="ms-1 px-2 py-1">
|
||||
<small>{selectedRoleName}</small>
|
||||
</span>
|
||||
</div>
|
||||
<ul
|
||||
className="dropdown-menu"
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
style={{ maxHeight: '200px', overflowY: 'auto' }}
|
||||
>
|
||||
{validJobRoles.map((role) => (
|
||||
<li key={role.id}>
|
||||
<a
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleRoleSelect(role.id);
|
||||
}}
|
||||
>
|
||||
{role.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RoleBadge;
|
@ -3,25 +3,37 @@ import MapUsers from "./MapUsers";
|
||||
import showToast from "../../services/toastService";
|
||||
import Avatar from "../common/Avatar";
|
||||
import moment from "moment";
|
||||
import { RolesRepository } from "../../repositories/MastersRepository";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
|
||||
const Teams = ({ project }) => {
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {changeMaster} from "../../slices/localVariablesSlice";
|
||||
import useMaster from "../../hooks/masterHook/useMaster"
|
||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission"
|
||||
import {ASSIGN_USER_TO_PROJECT} from "../../utils/constants";
|
||||
|
||||
|
||||
const Teams = ( {project} ) =>
|
||||
{
|
||||
const dispatch = useDispatch()
|
||||
dispatch( changeMaster( 'Job Role' ) )
|
||||
const {data,loading} = useMaster()
|
||||
const [isModalOpen, setIsModelOpen] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [empRoles, setEmpRoles] = useState(null);
|
||||
const [empJobRoles, setEmpJobRoles] = useState(null);
|
||||
const [clearFormTrigger, setClearFormTrigger] = useState(false);
|
||||
const [employees, setEmployees] = useState(null);
|
||||
const [filteredEmployees, setFilteredEmployees] = useState([]);
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const [ filteredEmployees, setFilteredEmployees ] = useState( [] );
|
||||
|
||||
const HasAssignUserPermission = useHasUserPermission( ASSIGN_USER_TO_PROJECT )
|
||||
|
||||
|
||||
|
||||
const fetchEmployees = async () => {
|
||||
try {
|
||||
// if (!empRoles) {
|
||||
ProjectRepository.getProjectAllocation(project.id)
|
||||
.then( ( response ) =>
|
||||
{
|
||||
debugger
|
||||
setEmployees( response.data );
|
||||
.then((response) => {
|
||||
setEmployees(response.data);
|
||||
setFilteredEmployees(response.data.filter((emp) => emp.isActive));
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -33,123 +45,98 @@ const Teams = ({ project }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchEmpRoles = async () => {
|
||||
try {
|
||||
const roles_cache = getCachedData("employeeRolesMaster");
|
||||
if (!roles_cache) {
|
||||
RolesRepository.getRoles()
|
||||
.then((response) => {
|
||||
setEmpRoles(response.data);
|
||||
cacheData("employeeRolesMaster", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
} else {
|
||||
setEmpRoles(roles_cache);
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch activities.");
|
||||
} finally {
|
||||
// setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const submitAllocations = (items) => {
|
||||
ProjectRepository.manageProjectAllocation(items)
|
||||
.then((response) => {
|
||||
showToast("Details updated successfully.", "success");
|
||||
setClearFormTrigger(true); // Set trigger to true
|
||||
setClearFormTrigger(true);
|
||||
fetchEmployees();
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
|
||||
// api
|
||||
// .post("/api/project/allocation", items)
|
||||
// .then((data) => {
|
||||
// showToast("Details updated successfully.", "success");
|
||||
// setClearFormTrigger(true); // Set trigger to true
|
||||
// fetchEmployees();
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// showToast(error.message, "error");
|
||||
// });
|
||||
};
|
||||
|
||||
const removeAllocation = (item) => {
|
||||
submitAllocations([
|
||||
{
|
||||
empID: item.employeeId,
|
||||
roleID: item.roleID,
|
||||
jobRoleId: item.jobRoleId,
|
||||
projectId: project.id,
|
||||
status: false,
|
||||
},
|
||||
]);
|
||||
|
||||
};
|
||||
|
||||
const handleEmpAlicationFormSubmit = (allocaionObj) => {
|
||||
console.log("Form submitted:", allocaionObj); // Replace this with an API call or state update
|
||||
debugger;
|
||||
|
||||
let items = allocaionObj.map((item) => {
|
||||
return {
|
||||
empID: item.id,
|
||||
roleID: item.roleId,
|
||||
empID: item.empID,
|
||||
jobRoleId: item.jobRoleId,
|
||||
projectId: project.id,
|
||||
status: true,
|
||||
};
|
||||
});
|
||||
|
||||
submitAllocations(items);
|
||||
|
||||
submitAllocations(items)
|
||||
};
|
||||
const getRole = (roleId) => {
|
||||
if (!empRoles) return "Unassigned";
|
||||
if (!roleId) return "Unassigned";
|
||||
const role = empRoles.find((b) => b.id == roleId);
|
||||
return role ? role.role : "Unassigned";
|
||||
|
||||
const getRole = ( jobRoleId ) =>
|
||||
{
|
||||
if (loading) return "Loading...";
|
||||
if (!Array.isArray(empJobRoles)) return "Unassigned";
|
||||
if (!jobRoleId) return "Unassigned";
|
||||
|
||||
const role = empJobRoles.find((b) => b.id == jobRoleId);
|
||||
return role ? role.name : "Unassigned";
|
||||
};
|
||||
const openModel = () => {
|
||||
setIsModelOpen(true);
|
||||
|
||||
|
||||
};
|
||||
|
||||
const onModelClose = () => {
|
||||
|
||||
setIsModelOpen(false);
|
||||
const modalElement = document.getElementById('user-model');
|
||||
const modalElement = document.getElementById("user-model");
|
||||
if (modalElement) {
|
||||
modalElement.classList.remove('show');
|
||||
modalElement.style.display = 'none';
|
||||
document.body.classList.remove('modal-open');
|
||||
document.querySelector('.modal-backdrop').remove();
|
||||
modalElement.classList.remove("show");
|
||||
modalElement.style.display = "none";
|
||||
document.body.classList.remove("modal-open");
|
||||
document.querySelector(".modal-backdrop").remove();
|
||||
}
|
||||
const modalBackdropElement = document.querySelector('.modal-backdrop');
|
||||
if (modalBackdropElement) {
|
||||
modalBackdropElement.remove();
|
||||
}
|
||||
document.body.style.overflow = 'auto'
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchEmployees();
|
||||
fetchEmpRoles();
|
||||
}, [] );
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
if ( data )
|
||||
{
|
||||
setEmpJobRoles(data)
|
||||
}
|
||||
},[data])
|
||||
|
||||
const handleFilterEmployee = (e) => {
|
||||
const filterValue = e.target.value;
|
||||
if (filterValue === 'true') {
|
||||
if (filterValue === "true") {
|
||||
setFilteredEmployees(employees.filter((emp) => emp.isActive));
|
||||
} else {
|
||||
setFilteredEmployees(employees.filter((emp) => !emp.isActive));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
isModalOpen && (
|
||||
{isModalOpen && (
|
||||
<div
|
||||
className={`modal fade `}
|
||||
id="user-model"
|
||||
@ -157,21 +144,24 @@ const Teams = ({ project }) => {
|
||||
aria-hidden="true"
|
||||
>
|
||||
<MapUsers
|
||||
projectId={project.id}
|
||||
onClose={onModelClose}
|
||||
empRoles={empRoles}
|
||||
projectId={1}
|
||||
empJobRoles={empJobRoles}
|
||||
onSubmit={handleEmpAlicationFormSubmit}
|
||||
allocation={employees}
|
||||
clearTrigger={clearFormTrigger}
|
||||
onClearComplete={() => setClearFormTrigger(false)}
|
||||
></MapUsers>
|
||||
</div>
|
||||
|
||||
)}
|
||||
<div className="card card-action mb-6">
|
||||
<div className="card-body">
|
||||
<div className="row">
|
||||
<div className="col-12 d-flex justify-content-between mb-1">
|
||||
<div className="dataTables_length text-start py-2 px-2" id="DataTables_Table_0_length">
|
||||
<div
|
||||
className="dataTables_length text-start py-2 px-2"
|
||||
id="DataTables_Table_0_length"
|
||||
>
|
||||
<label>
|
||||
<select
|
||||
name="DataTables_Table_0_length"
|
||||
@ -183,13 +173,13 @@ const Teams = ({ project }) => {
|
||||
defaultValue="true"
|
||||
>
|
||||
<option value="true">Active Employee</option>
|
||||
<option value="false">InActive Employee</option>
|
||||
<option value="false">In-Active Employee</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="link-button link-button-sm m-1"
|
||||
className={`link-button link-button-sm m-1 ${HasAssignUserPermission ? "":"d-none"}`}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#user-model"
|
||||
onClick={() => openModel()}
|
||||
@ -236,7 +226,6 @@ const Teams = ({ project }) => {
|
||||
{item.firstName} {item.lastName}
|
||||
</span>
|
||||
</a>
|
||||
{/* <small>{item.phoneNumber}</small> */}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -255,7 +244,7 @@ const Teams = ({ project }) => {
|
||||
</td>
|
||||
<td>
|
||||
<span className="badge bg-label-primary me-1">
|
||||
{getRole(item.roleID)}
|
||||
{getRole(item.jobRoleId)}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
@ -271,7 +260,7 @@ const Teams = ({ project }) => {
|
||||
<i className="bx bx-trash me-1 text-danger"></i>{" "}
|
||||
</button>
|
||||
)}
|
||||
{!item.isActive && (<span>Not in project</span>)}
|
||||
{!item.isActive && <span>Not in project</span>}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@ -288,3 +277,260 @@ const Teams = ({ project }) => {
|
||||
};
|
||||
|
||||
export default Teams;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// const Teams = ({ project }) => {
|
||||
// const dispatch = useDispatch()
|
||||
// const {data, loading} = useMaster()
|
||||
// const navigate = useNavigate()
|
||||
|
||||
|
||||
// const [isModalOpen, setIsModelOpen] = useState(false);
|
||||
// const [error, setError] = useState("");
|
||||
// const [empJobRoles, setEmpJobRoles] = useState([]);
|
||||
// const [clearFormTrigger, setClearFormTrigger] = useState(false);
|
||||
// const [employees, setEmployees] = useState([]);
|
||||
// const [ filteredEmployees, setFilteredEmployees ] = useState( [] );
|
||||
|
||||
// useEffect( () =>
|
||||
// {
|
||||
// dispatch( changeMaster( 'Job Role' ) )
|
||||
// },[dispatch])
|
||||
|
||||
// const fetchEmployees = useCallback(async () => {
|
||||
// try {
|
||||
// const response = await ProjectRepository.getProjectAllocation(project.id);
|
||||
// setEmployees(response.data);
|
||||
// setFilteredEmployees(response.data.filter((emp) => emp.isActive));
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
// setError("Failed to fetch data.");
|
||||
// }
|
||||
// }, [project.id]);
|
||||
|
||||
// useEffect(() => {
|
||||
// fetchEmployees();
|
||||
// }, [fetchEmployees]);
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// if (data) setEmpJobRoles(data);
|
||||
// }, [ data ] );
|
||||
|
||||
// const submitAllocations = (items) => {
|
||||
// ProjectRepository.manageProjectAllocation(items)
|
||||
// .then((response) => {
|
||||
// showToast("Details updated successfully.", "success");
|
||||
// setClearFormTrigger(true);
|
||||
// fetchEmployees();
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// showToast(error.message, "error");
|
||||
// });
|
||||
// };
|
||||
|
||||
// const removeAllocation = (item) => {
|
||||
// submitAllocations([
|
||||
// {
|
||||
// empID: item.employeeId,
|
||||
// jobRoleId: item.jobRoleId,
|
||||
// projectId: project.id,
|
||||
// status: false,
|
||||
// },
|
||||
// ]);
|
||||
// };
|
||||
|
||||
// const handleEmpAlicationFormSubmit = (allocaionObj) => {
|
||||
// console.log("Form submitted:", allocaionObj);
|
||||
|
||||
// let items = allocaionObj.map((item) => {
|
||||
// return {
|
||||
// empID: item.empID,
|
||||
// jobRoleId: item.jobRoleId,
|
||||
// projectId: project.id,
|
||||
// status: true,
|
||||
// };
|
||||
// });
|
||||
// submitAllocations(items)
|
||||
// };
|
||||
|
||||
// const getRole = ( jobRoleId ) =>
|
||||
// {
|
||||
// if (loading) return "Loading...";
|
||||
// if (!Array.isArray(empJobRoles)) return "Unassigned";
|
||||
// if (!jobRoleId) return "Unassigned";
|
||||
|
||||
// const role = empJobRoles.find((b) => b.id == jobRoleId);
|
||||
// return role ? role.name : "Unassigned";
|
||||
// };
|
||||
|
||||
// const openModel = () => {
|
||||
// setIsModelOpen(true);
|
||||
// };
|
||||
|
||||
// const onModelClose = () => {
|
||||
// setIsModelOpen(false);
|
||||
// const modalElement = document.getElementById("user-model");
|
||||
// if (modalElement) {
|
||||
// modalElement.classList.remove("show");
|
||||
// modalElement.style.display = "none";
|
||||
// document.body.classList.remove("modal-open");
|
||||
// document.querySelector(".modal-backdrop").remove();
|
||||
// }
|
||||
// const modalBackdropElement = document.querySelector('.modal-backdrop');
|
||||
// if (modalBackdropElement) {
|
||||
// modalBackdropElement.remove();
|
||||
// }
|
||||
// document.body.style.overflow = 'auto'
|
||||
|
||||
// };
|
||||
|
||||
|
||||
// const handleFilterEmployee = (e) => {
|
||||
// const filterValue = e.target.value === "true";
|
||||
// setFilteredEmployees(
|
||||
// employees.filter((emp) => emp.isActive === filterValue)
|
||||
// );
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {isModalOpen && (
|
||||
// <div
|
||||
// className={`modal fade `}
|
||||
// id="user-model"
|
||||
// tabIndex="-1"
|
||||
// aria-hidden="true"
|
||||
// >
|
||||
// <MapUsers
|
||||
// projectId={project.id}
|
||||
// onClose={onModelClose}
|
||||
// empJobRoles={empJobRoles}
|
||||
// onSubmit={handleEmpAlicationFormSubmit}
|
||||
// allocation={employees}
|
||||
// clearTrigger={clearFormTrigger}
|
||||
// onClearComplete={() => setClearFormTrigger(false)}
|
||||
// ></MapUsers>
|
||||
// </div>
|
||||
// )}
|
||||
// <div className="card card-action mb-6">
|
||||
// <div className="card-body">
|
||||
// <div className="row">
|
||||
// <div className="col-12 d-flex justify-content-between mb-1">
|
||||
// <div
|
||||
// className="dataTables_length text-start py-2 px-2"
|
||||
// id="DataTables_Table_0_length"
|
||||
// >
|
||||
// <label>
|
||||
// <select
|
||||
// name="DataTables_Table_0_length"
|
||||
// aria-controls="DataTables_Table_0"
|
||||
// className="form-select form-select-sm"
|
||||
// onChange={handleFilterEmployee}
|
||||
// aria-label=""
|
||||
// defaultValue="true"
|
||||
// >
|
||||
// <option value="true">Active Employee</option>
|
||||
// <option value="false">In-Active Employee</option>
|
||||
// </select>
|
||||
// </label>
|
||||
// </div>
|
||||
// <button
|
||||
// type="button"
|
||||
// className="link-button link-button-sm m-1"
|
||||
// data-bs-toggle="modal"
|
||||
// data-bs-target="#user-model"
|
||||
// onClick={() => openModel()}
|
||||
// >
|
||||
// <i className="bx bx-plus-circle me-2"></i>
|
||||
// Assign Users
|
||||
// </button>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="table-responsive text-nowrap">
|
||||
// {employees && employees.length > 0 ? (
|
||||
// <table className="table ">
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Name</th>
|
||||
// <th>Assigned Date</th>
|
||||
// <th>Release Date</th>
|
||||
// <th>Role</th>
|
||||
// <th>Actions</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody className="table-border-bottom-0">
|
||||
// {filteredEmployees &&
|
||||
// filteredEmployees.map((item) => (
|
||||
// <tr key={item.id}>
|
||||
// <td>
|
||||
// <div className="d-flex justify-content-start align-items-center">
|
||||
// <Avatar
|
||||
// firstName={item.firstName}
|
||||
// lastName={item.lastName}
|
||||
// ></Avatar>
|
||||
// <div className="d-flex flex-column">
|
||||
// <a
|
||||
// href="#"
|
||||
// onClick={()=> navigate(`/employee/${item.employeeId}`)}
|
||||
|
||||
// className="text-heading text-truncate"
|
||||
// >
|
||||
// <span className="fw-medium">
|
||||
// {item.firstName} {item.lastName}
|
||||
// </span>
|
||||
// </a>
|
||||
// </div>
|
||||
// </div>
|
||||
// </td>
|
||||
// <td>
|
||||
// {" "}
|
||||
// {moment(item.allocationDate).format(
|
||||
// "DD-MMM-YYYY"
|
||||
// )}{" "}
|
||||
// </td>
|
||||
// <td>
|
||||
// {item.reAllocationDate
|
||||
// ? moment(item.reAllocationDate).format("DD-MMM-YYYY"): "Present"}
|
||||
// </td>
|
||||
// <td>
|
||||
// <span className="badge bg-label-primary me-1">
|
||||
// {getRole(item.jobRoleId)}
|
||||
// </span>
|
||||
// </td>
|
||||
// <td>
|
||||
// {item.isActive && (
|
||||
// <button
|
||||
// aria-label="Delete"
|
||||
// type="button"
|
||||
// title="Remove from project"
|
||||
// className="btn p-0 dropdown-toggle hide-arrow"
|
||||
// onClick={() => removeAllocation(item)}
|
||||
// >
|
||||
// {" "}
|
||||
// <i className="bx bx-trash me-1 text-danger"></i>{" "}
|
||||
// </button>
|
||||
// )}
|
||||
// {!item.isActive && <span>Not in project</span>}
|
||||
// </td>
|
||||
// </tr>
|
||||
// ))}
|
||||
// </tbody>
|
||||
// </table>
|
||||
// ) : (
|
||||
// <span>No employees assigned to the project</span>
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default Teams;
|
||||
|
@ -25,8 +25,8 @@ const Avatar = ({ firstName, lastName }) => {
|
||||
return bgClasses[randomIndex];
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="avatar-wrapper">
|
||||
<>
|
||||
<div className="avatar-wrapper p-1">
|
||||
<div className="avatar me-2">
|
||||
<span
|
||||
className={`avatar-initial rounded-circle ${getRandomBootstrapBgClass()}`}
|
||||
@ -35,7 +35,7 @@ const Avatar = ({ firstName, lastName }) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -8,16 +8,16 @@ const Breadcrumb = ({ data }) => {
|
||||
<ol className="breadcrumb breadcrumb-style1">
|
||||
{data.map((item) =>
|
||||
item.link ? (
|
||||
<li className="breadcrumb-item">
|
||||
<li className="breadcrumb-item cursor-pointer">
|
||||
<a
|
||||
aria-label="pagination link link-underline-primary"
|
||||
aria-label="pagination link link-underline-primary "
|
||||
onClick={()=>navigate(item.link)}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
) : (
|
||||
<li className="breadcrumb-item active"> {item.label}</li>
|
||||
<li className="breadcrumb-item active "> {item.label}</li>
|
||||
)
|
||||
)}
|
||||
</ol>
|
||||
|
@ -1,11 +1,10 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
|
||||
const TimePicker = ({ label, onChange, interval = 10 }) => {
|
||||
const [time, setTime] = useState("");
|
||||
const TimePicker = ({ label, onChange, interval = 10, value }) => {
|
||||
const [time, setTime] = useState(value || "");
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
// Function to get the current time rounded to the nearest interval
|
||||
const getCurrentTime = () => {
|
||||
const now = new Date();
|
||||
const minutes = now.getMinutes();
|
||||
@ -13,11 +12,9 @@ const TimePicker = ({ label, onChange, interval = 10 }) => {
|
||||
now.setMinutes(roundedMinutes);
|
||||
now.setSeconds(0);
|
||||
now.setMilliseconds(0);
|
||||
|
||||
return now;
|
||||
};
|
||||
|
||||
// Function to format time to hh:mm AM/PM
|
||||
const formatTime = (date) => {
|
||||
let hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
@ -58,10 +55,12 @@ const TimePicker = ({ label, onChange, interval = 10 }) => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Set the default time to the nearest interval of current time
|
||||
if (!value) {
|
||||
const defaultTime = formatTime(getCurrentTime());
|
||||
setTime(defaultTime);
|
||||
}, [interval]);
|
||||
if (onChange) onChange(defaultTime);
|
||||
}
|
||||
}, [value, interval, onChange]);
|
||||
|
||||
return (
|
||||
<div className="position-relative w-100" ref={dropdownRef}>
|
||||
|
@ -33,21 +33,16 @@ const CreateJobRole = ({onClose}) => {
|
||||
const onSubmit = (data) => {
|
||||
setIsLoading(true)
|
||||
const result = {
|
||||
|
||||
name: data.role,
|
||||
description: data.description,
|
||||
|
||||
};
|
||||
|
||||
MasterRespository.createJobRole(result).then((resp)=>{
|
||||
|
||||
setIsLoading(false)
|
||||
resetForm()
|
||||
const cachedData = getCachedData("JobRole");
|
||||
const cachedData = getCachedData("Job Role");
|
||||
const updatedData = [...cachedData, resp?.data];
|
||||
|
||||
|
||||
cacheData("JobRole", updatedData);
|
||||
cacheData("Job Role", updatedData);
|
||||
showToast("JobRole Added successfully.", "success");
|
||||
|
||||
onClose()
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useFeatures } from "../../hooks/useMasterRole";
|
||||
|
||||
import { useForm ,Controller} from 'react-hook-form';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { set, z } from 'zod';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { MasterRespository } from "../../repositories/MastersRepository";
|
||||
@ -56,12 +56,12 @@ const onSubmit = (values) => {
|
||||
};
|
||||
|
||||
MasterRespository.createRole(result).then((resp)=>{
|
||||
|
||||
console.log(resp)
|
||||
setIsLoading(false)
|
||||
|
||||
const cachedData = getCachedData("JobRole");
|
||||
const updatedData = [...cachedData, resp?.data];
|
||||
|
||||
const cachedData = getCachedData( "Role" );
|
||||
console.log(cachedData)
|
||||
const updatedData = [...cachedData, resp];
|
||||
|
||||
cacheData("Role", updatedData);
|
||||
showToast("Role Added successfully.", "success");
|
||||
@ -70,7 +70,7 @@ const onSubmit = (values) => {
|
||||
showToast(err.message, "error");
|
||||
setIsLoading(false)
|
||||
})
|
||||
NewRoleAdded(true)
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -106,7 +106,7 @@ const onSubmit = (values) => {
|
||||
|
||||
|
||||
{masterFeatures.map((feature) => (
|
||||
<>
|
||||
<React.Fragment key={feature.id}>
|
||||
<div className="row my-1" key={feature.id} style={{ marginLeft: "0px" }}>
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ const onSubmit = (values) => {
|
||||
|
||||
</div>
|
||||
<hr className="hr my-1 py-1" />
|
||||
</>
|
||||
</React.Fragment>
|
||||
|
||||
))}
|
||||
{errors.selectedPermissions && (
|
||||
|
@ -44,13 +44,13 @@ const EditJobRole = ({data,onClose}) => {
|
||||
MasterRespository.updateJobRole(data?.id,result).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
showToast("JobRole Update successfully.", "success");
|
||||
const cachedData = getCachedData("JobRole");
|
||||
const cachedData = getCachedData("Job Role");
|
||||
if (cachedData) {
|
||||
|
||||
const updatedData = cachedData.map((role) =>
|
||||
role.id === data?.id ? { ...role, ...resp.data } : role
|
||||
);
|
||||
cacheData("JobRole", updatedData);
|
||||
cacheData("Job Role", updatedData);
|
||||
}
|
||||
|
||||
onClose()
|
||||
|
@ -96,20 +96,20 @@ const EditMaster=({master,onClose})=> {
|
||||
description: data.description,
|
||||
featuresPermission: updatedPermissions,
|
||||
};
|
||||
|
||||
MasterRespository.updateRoles(master?.item?.id, updatedRole).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
setIsLoading( false )
|
||||
|
||||
// const cachedData = getCachedData("Role");
|
||||
|
||||
// if (cachedData) {
|
||||
const cachedData = getCachedData("Role");
|
||||
|
||||
// const updatedData = cachedData.map((role) =>
|
||||
// role.id === data?.id ? { ...role, ...resp } : role
|
||||
// );
|
||||
if (cachedData) {
|
||||
|
||||
// cacheData("Role", updatedData);
|
||||
// }
|
||||
const updatedData = cachedData.map((role) =>
|
||||
role.id === resp.data?.id ? { ...role, ...resp.data } : role
|
||||
);
|
||||
|
||||
cacheData("Role", updatedData);
|
||||
}
|
||||
showToast("Role Update successfully.", "success");
|
||||
onClose()
|
||||
}).catch((Err)=>{
|
||||
|
@ -13,13 +13,14 @@ const MasterModal = ({ modaldata ,closeModal}) => {
|
||||
<div
|
||||
className="modal fade"
|
||||
id="master-modal"
|
||||
tabindex="-1"
|
||||
tabIndex="-1"
|
||||
aria-hidden="true"
|
||||
role="dialog"
|
||||
aria-labelledby="modalToggleLabel"
|
||||
>
|
||||
<div
|
||||
className={`modal-dialog mx-sm-auto mx-1 ${
|
||||
modaldata?.type === "delete" ? "modal-md" : "modal-lg"
|
||||
modaldata?.modalType === "delete" ? "modal-md" : "modal-lg"
|
||||
} modal-simple ` }
|
||||
>
|
||||
<div className="modal-content">
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const List = [{id:1, name: "Role"},{id:2, name: "Job Role"}, {id:3, name: "Status"},{id:4,name:"Module"}]
|
||||
export const mastersList = [{id:1, name: "Role"},{id:2, name: "Job Role"}, {id:3, name: "Status"},{id:4,name:"Module"}]
|
||||
|
||||
export const dailyTask = [
|
||||
{
|
||||
|
@ -5,36 +5,6 @@ import { useSelector } from "react-redux";
|
||||
|
||||
|
||||
|
||||
const useMasterData = (key, fetchFunction) => {
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const cachedData = getCachedData(key);
|
||||
if (cachedData) {
|
||||
setData(cachedData);
|
||||
} else {
|
||||
const response = await fetchFunction();
|
||||
setData(response);
|
||||
cacheData(key, response);
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [key, fetchFunction]);
|
||||
|
||||
return { data, loading, error };
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@ -52,7 +22,6 @@ const useMaster = () => {
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (!selectedMaster) return;
|
||||
@ -68,6 +37,7 @@ const useMaster = () => {
|
||||
switch (selectedMaster) {
|
||||
case "Role":
|
||||
response = await MasterRespository.getRoles();
|
||||
response = response.data;
|
||||
break;
|
||||
case "Job Role":
|
||||
response = await MasterRespository.getJobRole();
|
||||
@ -94,7 +64,11 @@ const useMaster = () => {
|
||||
}
|
||||
};
|
||||
|
||||
if ( selectedMaster )
|
||||
{
|
||||
|
||||
fetchData();
|
||||
}
|
||||
|
||||
}, [selectedMaster]);
|
||||
|
||||
|
@ -5,7 +5,7 @@ import AttendanceRepository from "../repositories/AttendanceRepository";
|
||||
export const useAttendace =(projectId)=>{
|
||||
|
||||
const [attendance, setAttendance] = useState([]);
|
||||
const[loading,setLoading] = useState(true)
|
||||
const[loading,setLoading] = useState(false)
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const fetchData = () => {
|
||||
@ -24,7 +24,7 @@ export const useAttendace =(projectId)=>{
|
||||
})
|
||||
} else {
|
||||
setAttendance(Attendance_cache.data);
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
@ -51,7 +51,6 @@ export const useEmployeeAttendacesLog = (id) => {
|
||||
setLoading(true)
|
||||
AttendanceRepository.getAttendanceLogs(id).then((response)=>{
|
||||
setLogs(response.data)
|
||||
console.log("logs",response)
|
||||
cacheData("AttendanceLogs", { data: response.data, id })
|
||||
setLoading(false)
|
||||
}).catch((error)=>{
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { timeElapsed } from '../utils/dateUtils';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { THRESH_HOLD } from '../utils/constants';
|
||||
import {THRESH_HOLD} from '../utils/constants';
|
||||
|
||||
|
||||
export const ACTIONS = {
|
||||
CHECK_IN: 0,
|
||||
CHECK_OUT: 1,
|
||||
|
@ -2,19 +2,60 @@ import { useEffect, useState } from "react";
|
||||
import { cacheData, getCachedData } from "../slices/apiDataManager";
|
||||
import { RolesRepository } from "../repositories/MastersRepository";
|
||||
import EmployeeRepository from "../repositories/EmployeeRepository";
|
||||
import ProjectRepository from "../repositories/ProjectRepository";
|
||||
|
||||
|
||||
export const useAllEmployees = () =>
|
||||
{
|
||||
const [employeesList, setEmployeeList] = useState([]);
|
||||
const [ loading, setLoading ] = useState( false )
|
||||
const [ error, setError ] = useState()
|
||||
|
||||
const fetchData = async () =>
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
let EmployeeList_cached = getCachedData( "AllEmployees" )
|
||||
if ( ! EmployeeList_cached )
|
||||
{
|
||||
setLoading(true)
|
||||
const response = await EmployeeRepository.getAllEmployeeList();
|
||||
cacheData( "AllEmployees", response.data )
|
||||
setEmployeeList(response.data)
|
||||
} else
|
||||
{
|
||||
setEmployeeList( EmployeeList_cached )
|
||||
setLoading(false)
|
||||
}
|
||||
} catch ( error )
|
||||
{
|
||||
console.error(error);
|
||||
setError( "Failed to fetch data." );
|
||||
setLoading(false)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
fetchData()
|
||||
}, [] )
|
||||
return {employeesList,loading,error}
|
||||
}
|
||||
|
||||
export const useEmployees =(selectedProject)=>{
|
||||
|
||||
const [employees, setEmployeeList] = useState([]);
|
||||
const[loading,setLoading] = useState(false)
|
||||
const[loading,setLoading] = useState(true)
|
||||
const [projects, setProjects] = useState([]);
|
||||
|
||||
const fetchData = async (projectid) => {
|
||||
try {
|
||||
let EmployeeByProject_Cache = getCachedData("employeeListByProject")
|
||||
if(!EmployeeByProject_Cache || !EmployeeByProject_Cache.projectId === projectid) {
|
||||
setLoading(true)
|
||||
|
||||
EmployeeRepository.getEmployeeListByproject(projectid)
|
||||
.then((response) => {
|
||||
setEmployeeList(response);
|
||||
@ -23,11 +64,12 @@ export const useEmployees =(selectedProject)=>{
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
setLoading(false)
|
||||
|
||||
}else{
|
||||
setEmployeeList(EmployeeByProject_Cache.data)
|
||||
setLoading(false);
|
||||
|
||||
}
|
||||
setLoading(false)
|
||||
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
@ -43,13 +85,13 @@ export const useEmployees =(selectedProject)=>{
|
||||
},[selectedProject])
|
||||
|
||||
|
||||
return {employees,loading,projects}
|
||||
return {employees,loading,projects,reCallAllEmployee}
|
||||
}
|
||||
|
||||
export const useEmployeeRoles = (employeeId)=>{
|
||||
const [loading,setLoading] = useState(true)
|
||||
const[error, setError] =useState()
|
||||
const [employeeRoles,setEmployeeRoles] = useState()
|
||||
const [employeeRoles,setEmployeeRoles] = useState([])
|
||||
const fetchData = async (employeeid) => {
|
||||
|
||||
try {
|
||||
@ -78,28 +120,26 @@ return {employeeRoles,loading,error}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const useEmployeesByProject=(projectId)=>{
|
||||
const [loading,setLoading] = useState(true)
|
||||
const [loading,setLoading] = useState(false)
|
||||
const[error, setError] =useState()
|
||||
const [employees,setEmployees] = useState()
|
||||
|
||||
const [employees,setEmployees] = useState([])
|
||||
|
||||
const fetchData = async () => {
|
||||
const Employees_cache = getCachedData("employeeListByProject");
|
||||
if(!Employees_cache || Employees_cache.projectId !== projectId){
|
||||
|
||||
|
||||
EmployeeRepository.getEmployeeListByproject(projectId)
|
||||
.then((response) => {
|
||||
setEmployees(response);
|
||||
cacheData("employeeListByProject", { data: response, projectId })
|
||||
setEmployees(true)
|
||||
ProjectRepository.getEmployeesByProject(projectId)
|
||||
.then( ( response ) =>
|
||||
{
|
||||
setEmployees(response.data);
|
||||
cacheData("employeeListByProject", { data: response.data, projectId })
|
||||
setLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false)
|
||||
|
||||
});
|
||||
|
||||
}else{
|
||||
@ -108,11 +148,116 @@ export const useEmployeesByProject=(projectId)=>{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
fetchData(projectId);
|
||||
},[projectId])
|
||||
|
||||
|
||||
return {employees,loading,error}
|
||||
return {employees,loading,error,recallProjectEmplloyee:fetchData}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const useEmployeesAllOrByProjectId = (projectId) => {
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const fetchData = async () =>
|
||||
{
|
||||
|
||||
if (projectId) {
|
||||
|
||||
const Employees_cache = getCachedData("employeeListByProject");
|
||||
|
||||
if (!Employees_cache || Employees_cache.projectId !== projectId) {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await ProjectRepository.getEmployeesByProject(projectId);
|
||||
setEmployees(response.data);
|
||||
cacheData("employeeListByProject", { data: response.data, projectId });
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
setEmployees(Employees_cache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
|
||||
const employeesCache = getCachedData("allEmployeeList");
|
||||
|
||||
if (!employeesCache) {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await EmployeeRepository.getAllEmployeeList();
|
||||
setEmployees(response.data);
|
||||
cacheData("allEmployeeList", { data: response.data });
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
setEmployees(employeesCache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(); // Fetch data when the component mounts or projectId changes
|
||||
}, [projectId]); // Re-fetch when projectId changes
|
||||
|
||||
return {
|
||||
employees,
|
||||
loading,
|
||||
error,
|
||||
recallEmployeeData: fetchData,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const useEmployeeProfile =(employeeId)=>{
|
||||
const [loading,setLoading] = useState(true)
|
||||
const[error, setError] =useState()
|
||||
const [employee,setEmployees] = useState()
|
||||
|
||||
const fetchData = async () => {
|
||||
const Employee_cache = getCachedData("employeeProfile");
|
||||
if(!Employee_cache || Employee_cache.employeeId !== employeeId){
|
||||
|
||||
|
||||
EmployeeRepository.getEmployeeProfile(employeeId)
|
||||
.then((response) => {
|
||||
setEmployees(response.data);
|
||||
cacheData("employeeProfile", { data: response.data, employeeId })
|
||||
setLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false)
|
||||
});
|
||||
|
||||
}else{
|
||||
setEmployees(Employee_cache.data)
|
||||
setLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
fetchData(employeeId);
|
||||
},[employeeId])
|
||||
|
||||
return {employee,loading,error}
|
||||
|
||||
}
|
11
src/hooks/useHasUserPermission.js
Normal file
11
src/hooks/useHasUserPermission.js
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
import { useProfile } from "./useProfile"
|
||||
export const useHasUserPermission = (permission) => {
|
||||
const { profile } = useProfile();
|
||||
|
||||
if (profile && permission && typeof permission === "string") {
|
||||
return profile?.featurePermissions.includes(permission);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
42
src/hooks/useProfile.js
Normal file
42
src/hooks/useProfile.js
Normal file
@ -0,0 +1,42 @@
|
||||
import {useState,useEffect} from "react";
|
||||
import AuthRepository from "../repositories/AuthRepository";
|
||||
import {cacheProfileData, getCachedProfileData} from "../slices/apiDataManager";
|
||||
|
||||
export const useProfile =()=>{
|
||||
const [profile, setProfile] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect( () => {
|
||||
|
||||
const fetchData = async () => {
|
||||
const profile_cache = getCachedProfileData()
|
||||
|
||||
// if (!profile_cache) {
|
||||
setLoading(true)
|
||||
AuthRepository.profile()
|
||||
.then((response) => {
|
||||
setProfile(response)
|
||||
cacheProfileData(response)
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false)
|
||||
console.error(error);
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
|
||||
// } else {
|
||||
// setProfile(profile_cache);
|
||||
// }
|
||||
};
|
||||
fetchData()
|
||||
},[])
|
||||
|
||||
return { profile,loading,error}
|
||||
|
||||
}
|
@ -17,8 +17,6 @@ export const useProjects =()=>{
|
||||
|
||||
if (!projects_cache) {
|
||||
setLoading(true)
|
||||
|
||||
|
||||
ProjectRepository.getProjectList()
|
||||
.then((response) => {
|
||||
setProjects(response);
|
||||
@ -37,12 +35,86 @@ export const useProjects =()=>{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
fetchData()
|
||||
},[])
|
||||
|
||||
return { projects,loading,error}
|
||||
return { projects,loading,error,refetch:fetchData}
|
||||
|
||||
}
|
||||
|
||||
export const useEmployeesByProjectAllocated = ( selectedProject ) =>
|
||||
{
|
||||
const [projectEmployees, setEmployeeList] = useState([]);
|
||||
const[loading,setLoading] = useState(true)
|
||||
const [projects, setProjects] = useState([]);
|
||||
|
||||
const fetchData = async (projectid) => {
|
||||
try {
|
||||
let EmployeeByProject_Cache = getCachedData("empListByProjectAllocated")
|
||||
if(!EmployeeByProject_Cache || !EmployeeByProject_Cache.projectId === projectid) {
|
||||
|
||||
let response = await ProjectRepository.getProjectAllocation(projectid)
|
||||
setEmployeeList(response.data);
|
||||
cacheData("empListByProjectAllocated",{data:response.data,projectId:projectid});
|
||||
setLoading(false)
|
||||
}else{
|
||||
setEmployeeList(EmployeeByProject_Cache.data)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(selectedProject){
|
||||
fetchData(selectedProject);
|
||||
}
|
||||
},[selectedProject])
|
||||
|
||||
|
||||
return {projectEmployees,loading,projects}
|
||||
}
|
||||
|
||||
export const useProjectDetails =(projectId)=>{
|
||||
|
||||
const [projects_Details, setProject_Details] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true)
|
||||
const project_cache = getCachedData(`projectinfo-${projectId}`);
|
||||
if (!project_cache) {
|
||||
ProjectRepository.getProjectByprojectId(projectId)
|
||||
.then( ( response ) =>
|
||||
{
|
||||
setProject_Details(response);
|
||||
cacheData( `projectinfo-${ projectId }`, response );
|
||||
setLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setError( "Failed to fetch data." );
|
||||
setLoading(false)
|
||||
});
|
||||
} else {
|
||||
setProject_Details( project_cache );
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
};;
|
||||
|
||||
useEffect(()=>{
|
||||
fetchData()
|
||||
},[projectId])
|
||||
|
||||
return { projects_Details,loading,error,refetch:fetchData}
|
||||
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { useState } from "react";
|
||||
|
||||
const useSearch = (data, query) => {
|
||||
const [searchQuery, setSearchQuery] = useState(query);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
const filteredData = () => {
|
||||
if (!searchQuery) {
|
||||
return data;
|
||||
}
|
||||
@ -14,10 +14,10 @@ const useSearch = (data, query) => {
|
||||
item.lastName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
item.phoneNumber.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
}, [data, searchQuery]);
|
||||
};
|
||||
|
||||
return {
|
||||
filteredData,
|
||||
filteredData: filteredData(),
|
||||
setSearchQuery,
|
||||
};
|
||||
};
|
||||
|
@ -81,3 +81,22 @@ button:focus-visible {
|
||||
.danger-text {
|
||||
color: #f08080;
|
||||
}
|
||||
|
||||
/* for breadcrumb It Important*/
|
||||
.breadcrumb-style1 .breadcrumb-item + .breadcrumb-item::before {
|
||||
content: "";
|
||||
font-size: 11.8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.li-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.name-column,
|
||||
.role-column,
|
||||
.checkbox-column {
|
||||
flex: 1;
|
||||
}
|
||||
|
@ -10,12 +10,12 @@ const HomeLayout = () => {
|
||||
Main();
|
||||
}, []);
|
||||
return (
|
||||
<div className="layout-wrapper layout-content-navbar">
|
||||
<div className="layout-container">
|
||||
<div className="layout-wrapper layout-content-navbar" >
|
||||
<div className="layout-container" >
|
||||
<Sidebar />
|
||||
<div className="layout-page ">
|
||||
<Header />
|
||||
<div className="content-wrapper">
|
||||
<div className="content-wrapper" >
|
||||
<Outlet />
|
||||
<Footer />
|
||||
</div>
|
||||
|
@ -1,67 +1,24 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import { cacheData, getCachedData, getCachedProfileData } from "../../slices/apiDataManager";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import AttendanceLog from "../../components/Activities/AttendcesLogs";
|
||||
import Attendance from "../../components/Activities/Attendance";
|
||||
import AttendanceModel from "../../components/Activities/AttendanceModel";
|
||||
import AttendanceRepository from "../../repositories/AttendanceRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import Regularization from "../../components/Activities/Regularization";
|
||||
import { useAttendace } from "../../hooks/useAttendance";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setProjectId } from "../../slices/localVariablesSlice";
|
||||
import {markCurrentAttendance} from "../../slices/apiSlice/attendanceAllSlice";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
|
||||
|
||||
// const attendance1 = [
|
||||
|
||||
// {
|
||||
// "id": 0,
|
||||
// "employeeId": 4,
|
||||
// "firstName": "Umesh",
|
||||
// "lastName": "Desai",
|
||||
// "employeeAvatar": null,
|
||||
// "checkInTime": null,
|
||||
// "checkOutTime": null,
|
||||
// "activity": 0
|
||||
// },
|
||||
|
||||
// {
|
||||
// "id": 0,
|
||||
// "employeeId": 7,
|
||||
// "firstName": "ramesh",
|
||||
// "lastName": "sadsad",
|
||||
// "employeeAvatar": null,
|
||||
// "checkInTime": null,
|
||||
// "checkOutTime": null,
|
||||
// "activity": 0
|
||||
// },
|
||||
// {
|
||||
// "id": 0,
|
||||
// "employeeId": 1,
|
||||
// "firstName": "Administrator",
|
||||
// "lastName": "",
|
||||
// "employeeAvatar": null,
|
||||
// "checkInTime": "2025-03-08T08:55:00",
|
||||
// "checkOutTime":"2025-03-08T08:55:00",
|
||||
// "activity": 2
|
||||
// },
|
||||
// {
|
||||
// "id": 0,
|
||||
// "employeeId": 2,
|
||||
// "firstName": "ssda",
|
||||
// "lastName": "sadsad",
|
||||
// "employeeAvatar": null,
|
||||
// "checkInTime": null,
|
||||
// "checkOutTime": null,
|
||||
// "activity": 0
|
||||
// }
|
||||
// ]
|
||||
|
||||
const AttendancePage = () => {
|
||||
const AttendancePage = () =>
|
||||
{
|
||||
const loginUser = getCachedProfileData()
|
||||
const selectedProject = useSelector((store)=>store.localVariables.projectId)
|
||||
const {projects,loading:projectLoading} = useProjects()
|
||||
const {attendance} = useAttendace(selectedProject)
|
||||
const {attendance,loading:attLoading} = useAttendace(selectedProject)
|
||||
const[attendances,setAttendances] = useState()
|
||||
const [empRoles, setEmpRoles] = useState(null);
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
@ -74,6 +31,7 @@ const AttendancePage = () => {
|
||||
date: new Date().toLocaleDateString(),
|
||||
});
|
||||
|
||||
|
||||
const getRole = (roleId) => {
|
||||
if (!empRoles) return "Unassigned";
|
||||
if (!roleId) return "Unassigned";
|
||||
@ -81,8 +39,6 @@ const AttendancePage = () => {
|
||||
return role ? role.role : "Unassigned";
|
||||
};
|
||||
|
||||
|
||||
|
||||
const openModel = () => {
|
||||
setIsCreateModalOpen(true);
|
||||
};
|
||||
@ -106,43 +62,23 @@ const AttendancePage = () => {
|
||||
};
|
||||
|
||||
|
||||
const handleSubmit = (formData) => {
|
||||
|
||||
let newRecordAttendance = {
|
||||
id: null,
|
||||
comment:formData.description,
|
||||
employeeID: formData.employeeId ,
|
||||
projectId:selectedProject,
|
||||
date:new Date().toISOString(),
|
||||
markTime:formData.time,
|
||||
latitude: formData.latitude.toString(),
|
||||
longitude: formData.longitude.toString(),
|
||||
action:formData.action,
|
||||
image:null
|
||||
|
||||
}
|
||||
console.log(newRecordAttendance)
|
||||
|
||||
AttendanceRepository.markAttendance(newRecordAttendance).then((response) => {
|
||||
console.log(response)
|
||||
const handleSubmit = ( formData ) =>{
|
||||
|
||||
dispatch( markCurrentAttendance( formData ) ).then( ( action ) =>
|
||||
{
|
||||
const updatedAttendance = attendances.map(item =>
|
||||
item.employeeId === response.data.employeeId
|
||||
? { ...item, ...response.data }
|
||||
item.employeeId === action.payload.employeeId
|
||||
? { ...item, ...action.payload }
|
||||
: item
|
||||
);
|
||||
cacheData("Attendance", { data: updatedAttendance, projectId: selectedProject })
|
||||
setAttendances(updatedAttendance)
|
||||
showToast("Attedance Marked Successfully","success")
|
||||
console.log(updatedAttendance)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
.catch( ( error ) =>
|
||||
{
|
||||
showToast(error.message,"error")
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -155,6 +91,7 @@ const AttendancePage = () => {
|
||||
setAttendances(attendance)
|
||||
},[attendance])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCreateModalOpen && modelConfig && (
|
||||
@ -182,7 +119,8 @@ const AttendancePage = () => {
|
||||
className="dataTables_length text-start py-2 px-2"
|
||||
id="DataTables_Table_0_length"
|
||||
>
|
||||
<label>
|
||||
{
|
||||
((loginUser && loginUser?.projects.length > 1) ) && (<label>
|
||||
<select
|
||||
name="DataTables_Table_0_length"
|
||||
aria-controls="DataTables_Table_0"
|
||||
@ -191,12 +129,14 @@ const AttendancePage = () => {
|
||||
onChange={(e)=>dispatch(setProjectId(e.target.value))}
|
||||
aria-label=""
|
||||
>
|
||||
{!projectLoading && projects?.map((project)=>(
|
||||
{!projectLoading && projects?.filter(project =>
|
||||
loginUser?.projects?.map(Number).includes(project.id)).map((project)=>(
|
||||
<option value={project.id}>{project.name}</option>
|
||||
))}
|
||||
{projectLoading && <option disabled>Loading...</option> }
|
||||
{projectLoading && <option value="Loading..." disabled>Loading...</option> }
|
||||
</select>
|
||||
</label>
|
||||
</label>)
|
||||
}
|
||||
</div>
|
||||
</ul>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
@ -224,6 +164,7 @@ const AttendancePage = () => {
|
||||
Logs
|
||||
</button>
|
||||
</li>
|
||||
{hasUserPermission("52c9cf54-1eb2-44d2-81bb-524cf29c0a94") && (
|
||||
<li class="nav-item">
|
||||
<button
|
||||
type="button"
|
||||
@ -237,12 +178,16 @@ const AttendancePage = () => {
|
||||
</button>
|
||||
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<div class="tab-content attedanceTabs py-2">
|
||||
{ projects && projects.length > 0 ? (
|
||||
{projectLoading && (<span>Loading..</span>)}
|
||||
{(!projectLoading && !attendances) && <span>Not Found</span>}
|
||||
{ (projects && projects.length > 0 ) && (
|
||||
<>
|
||||
<div class="tab-pane fade show active py-0" id="navs-top-home" role="tabpanel">
|
||||
<Attendance attendance={attendances} handleModalData={handleModalData} getRole={getRole}/>
|
||||
<div className="tab-pane fade show active py-0" id="navs-top-home" role="tabpanel">
|
||||
|
||||
<Attendance attendance={attendances} handleModalData={handleModalData} getRole={getRole} />
|
||||
</div>
|
||||
<div class="tab-pane fade" id="navs-top-profile" role="tabpanel">
|
||||
|
||||
@ -252,20 +197,14 @@ const AttendancePage = () => {
|
||||
projectId={selectedProject}
|
||||
/>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="navs-top-messages" role="tabpanel">
|
||||
<div className="tab-pane fade" id="navs-top-messages" role="tabpanel">
|
||||
<Regularization
|
||||
attendance={attendances}
|
||||
handleRequest ={handleSubmit}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
):(
|
||||
<>
|
||||
{projectLoading && (<span>Loading..</span>)}
|
||||
{!projectLoading && (<span>Not Found</span>)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,19 +1,40 @@
|
||||
import { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import {Link} from "react-router-dom";
|
||||
import { AuthWrapper } from "./AuthWrapper"
|
||||
import "./page-auth.css";
|
||||
import { AuthWrapper } from "./AuthWrapper";
|
||||
import AuthRepository from "../../repositories/AuthRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
|
||||
const ForgotPasswordPage = () => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [ email, setEmail ] = useState( "" );
|
||||
const[loding,setLoading] = useState(false)
|
||||
|
||||
const handleChange = (e) => {
|
||||
setEmail(e.target.value);
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = async ( e ) =>
|
||||
{
|
||||
setLoading(true)
|
||||
e.preventDefault();
|
||||
// Add logic to handle the form submission (e.g., send reset link)
|
||||
console.log("Email submitted:", email);
|
||||
try
|
||||
{
|
||||
const response = await AuthRepository.forgotPassword({email})
|
||||
if ( response.data && response.success )
|
||||
{
|
||||
showToast( response.message, "success" )
|
||||
} else
|
||||
{
|
||||
showToast( response.message, "warning" )
|
||||
}
|
||||
setLoading( false )
|
||||
setEmail("")
|
||||
} catch ( error )
|
||||
{
|
||||
showToast( "User Not Found", "error" )
|
||||
setLoading(false)
|
||||
}
|
||||
};
|
||||
return (
|
||||
<AuthWrapper>
|
||||
@ -38,7 +59,7 @@ const ForgotPasswordPage = () => {
|
||||
/>
|
||||
</div>
|
||||
<button aria-label="Click me" className="btn btn-primary d-grid w-100">
|
||||
Send Reset Link
|
||||
{loding ? "Please Wait...":"Send Reset Link"}
|
||||
</button>
|
||||
</form>
|
||||
<div className="text-center">
|
||||
|
@ -4,9 +4,11 @@ import { AuthWrapper } from "./AuthWrapper";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import "./page-auth.css";
|
||||
import AuthRepository from "../../repositories/AuthRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
const LoginPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
password: "",
|
||||
@ -25,6 +27,7 @@ const LoginPage = () => {
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
let data = {
|
||||
@ -32,44 +35,18 @@ const LoginPage = () => {
|
||||
password: formData.password,
|
||||
};
|
||||
|
||||
AuthRepository.login(data)
|
||||
.then((response) => {
|
||||
//console.log("Login Success:", response);
|
||||
localStorage.setItem("jwtToken", response.token);
|
||||
localStorage.setItem("refreshToken", response.refreshToken);
|
||||
|
||||
// Redirect to dashboard
|
||||
const response = await AuthRepository.login(data);
|
||||
localStorage.setItem("jwtToken", response.data.token);
|
||||
localStorage.setItem("refreshToken", response.data.refreshToken);
|
||||
setLoading(false);
|
||||
navigate("/dashboard");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Invalid credentials. Please try again.");
|
||||
});
|
||||
|
||||
// api
|
||||
// .postPublic("/api/auth/login", data)
|
||||
// .then((data) => {
|
||||
// console.log("Login Success:", data);
|
||||
// localStorage.setItem("jwtToken", data.token);
|
||||
// localStorage.setItem("refreshToken", data.refreshToken);
|
||||
|
||||
// // Redirect to dashboard
|
||||
// navigate("/dashboard");
|
||||
// })
|
||||
// .catch((error) =>
|
||||
// console.log("Invalid credentials. Please try again.")
|
||||
// );
|
||||
|
||||
// const response = await axiosClient.post("/api/auth/login", data);
|
||||
|
||||
// //console.log(response.data);
|
||||
// // Store tokens in localStorage or cookies
|
||||
// localStorage.setItem("jwtToken", response.data.token);
|
||||
// localStorage.setItem("refreshToken", response.data.refreshToken);
|
||||
|
||||
// // Redirect to dashboard
|
||||
// navigate("/employees");
|
||||
} catch (err) {
|
||||
console.log("Unable to proceed. Please try again.");
|
||||
setLoading(false);
|
||||
|
||||
if (err.status === 401) {
|
||||
showToast(err.response.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
@ -144,7 +121,7 @@ const LoginPage = () => {
|
||||
className="btn btn-primary d-grid w-100"
|
||||
type="submit"
|
||||
>
|
||||
Sign in
|
||||
{loading ? "Please Wait" : " Sign in"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -5,6 +5,8 @@ import { AuthWrapper } from "./AuthWrapper";
|
||||
import { useForm,Controller } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import showToast from "../../services/toastService";
|
||||
import AuthRepository from "../../repositories/AuthRepository";
|
||||
|
||||
const mobileNumberRegex = /^(?:\d{10}|\d{3}[-\s]?\d{3}[-\s]?\d{4})$/;
|
||||
|
||||
@ -18,15 +20,25 @@ const registerSchema = z.object({
|
||||
terms: z.boolean().refine((val) => val === true, {
|
||||
message: "Please accept the terms and conditions.",
|
||||
}),
|
||||
})
|
||||
} )
|
||||
|
||||
const RegisterPage = () => {
|
||||
|
||||
const {register,handleSubmit,formState: { errors }} = useForm({
|
||||
resolver:zodResolver(registerSchema)
|
||||
})
|
||||
|
||||
const onSubmit=(data)=>{
|
||||
console.log(data)
|
||||
const onSubmit= async(data)=>{
|
||||
try
|
||||
{
|
||||
// const response = await AuthRepository.register( data );
|
||||
|
||||
showToast("Your Registration SuccessFully !")
|
||||
} catch ( error )
|
||||
{
|
||||
console.log(error)
|
||||
showToast(error.message,"error")
|
||||
}
|
||||
}
|
||||
return (
|
||||
<AuthWrapper>
|
||||
|
139
src/pages/authentication/ResetPasswordPage.jsx
Normal file
139
src/pages/authentication/ResetPasswordPage.jsx
Normal file
@ -0,0 +1,139 @@
|
||||
import { useState } from "react";
|
||||
import { Link, useSearchParams } from "react-router-dom";
|
||||
import "./page-auth.css";
|
||||
import { AuthWrapper } from "./AuthWrapper";
|
||||
import showToast from "../../services/toastService";
|
||||
import AuthRepository from "../../repositories/AuthRepository";
|
||||
import { z } from 'zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {clearAllCache} from "../../slices/apiDataManager";
|
||||
|
||||
const resetPasswordSchema = z.object( {
|
||||
email:z.string().email(),
|
||||
password: z
|
||||
.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
|
||||
.regex(/[a-z]/, 'Password must contain at least one lowercase letter')
|
||||
.regex(/\d/, 'Password must contain at least one number')
|
||||
.regex(/[!@#$%^&*()_+{}\[\]:;<>,.?~\\/-]/, 'Password must contain at least one special character'),
|
||||
|
||||
confirmPassword: z.string().min(8, 'Password must be at least 8 characters'),
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
message: 'Passwords do not match',
|
||||
path: ['confirmPassword'],
|
||||
} );
|
||||
|
||||
const ResetPasswordPage = () =>
|
||||
{
|
||||
const [ searchParams ] = useSearchParams();
|
||||
const [loading,setLoading] = useState(false)
|
||||
|
||||
const token = searchParams.get('token');
|
||||
const navigate = useNavigate()
|
||||
|
||||
const {register,handleSubmit,formState: { errors }} = useForm({
|
||||
resolver:zodResolver(resetPasswordSchema)
|
||||
})
|
||||
|
||||
const onSubmitResetPassword = async(data) => {
|
||||
try
|
||||
{
|
||||
setLoading(true)
|
||||
const {email, password, confirmPassword} = data;
|
||||
|
||||
let reqObject = {
|
||||
email,
|
||||
token: token,
|
||||
newPassword: password
|
||||
}
|
||||
let response = await AuthRepository.resetPassword( reqObject );
|
||||
showToast( "Password Reseted", "success" )
|
||||
clearAllCache()
|
||||
setLoading(false)
|
||||
navigate("/auth/login",{replace:true})
|
||||
} catch ( error )
|
||||
{
|
||||
setLoading(false)
|
||||
showToast("Token is expries or Invalid ","error")
|
||||
}
|
||||
};
|
||||
return (
|
||||
<AuthWrapper>
|
||||
<h4 className="mb-2">Reset Password? 🔒</h4>
|
||||
<p className="mb-4">Enter your email and new password to update.</p>
|
||||
<form id="formAuthentication" className="mb-3" onSubmit={handleSubmit(onSubmitResetPassword)}>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="email" className="form-label">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="email"
|
||||
{...register("email")}
|
||||
placeholder="Enter your email"
|
||||
autoFocus
|
||||
/>
|
||||
{errors.email && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.email.message}</div>}
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label htmlFor="email" className="form-label">
|
||||
New Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
autoComplete="true"
|
||||
id="password"
|
||||
className="form-control"
|
||||
name="password"
|
||||
{...register('password')}
|
||||
placeholder="············"
|
||||
aria-describedby="password"
|
||||
/>
|
||||
{errors.password && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.password.message}</div>}
|
||||
</div>
|
||||
<div className=" mb-3">
|
||||
<label htmlFor="email" className="form-label">
|
||||
Repeat New Password
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
autoComplete="true"
|
||||
id="password"
|
||||
className="form-control"
|
||||
name="confirmPassword"
|
||||
{...register('confirmPassword')}
|
||||
placeholder="············"
|
||||
aria-describedby="password"
|
||||
/>
|
||||
{errors.confirmPassword && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.confirmPassword.message}</div>}
|
||||
</div>
|
||||
<div className="mb-3 text-start ">
|
||||
<p className="p-0 m-0"style={{fontSize:'9px'}}>Password must be at least 8 characters</p>
|
||||
<p className="p-0 m-0" style={{fontSize:'9px'}}>Password must contain at least one uppercase letter</p>
|
||||
<p className="p-0 m-0" style={{fontSize:'9px'}}>Password must contain at least one number</p>
|
||||
<p className="p-0 m-0" style={{fontSize:'9px'}}>Password must contain at least one special character</p>
|
||||
</div>
|
||||
<button aria-label="Click me" className="btn btn-primary d-grid w-100">
|
||||
{loading ? "Please Wait...":"Update Password"}
|
||||
</button>
|
||||
</form>
|
||||
<div className="text-center">
|
||||
<Link
|
||||
aria-label="Go to Login Page"
|
||||
to="/auth/login"
|
||||
className="d-flex align-items-center justify-content-center"
|
||||
>
|
||||
<i className="bx bx-chevron-left scaleX-n1-rtl bx-sm"></i>
|
||||
Back to login
|
||||
</Link>
|
||||
</div>
|
||||
</AuthWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResetPasswordPage;
|
@ -3,77 +3,95 @@ import moment from "moment";
|
||||
import { Link, NavLink, useNavigate } from "react-router-dom";
|
||||
import Avatar from "../../components/common/Avatar";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import EmployeeRepository from "../../repositories/EmployeeRepository";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import ManageEmp from "../../components/Employee/ManageRole";
|
||||
import {useEmployeesAllOrByProjectId,useAllEmployees} from "../../hooks/useEmployees";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {setProjectId} from "../../slices/localVariablesSlice";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import {hasUserPermission} from "../../utils/authUtils";
|
||||
|
||||
const EmployeeList = () => {
|
||||
const [employees, setEmployeeList] = useState([]);
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [selectedProject, setSelectedProject] = useState(0);
|
||||
const EmployeeList = () =>
|
||||
{
|
||||
|
||||
|
||||
const {profile:loginUser}= useProfile()
|
||||
const [selectedProject, setSelectedProject] = useState("");
|
||||
const [ projectsList, setProjectsList ] = useState( [] );
|
||||
const {projects, loading: projectLoading} = useProjects()
|
||||
|
||||
const {employees, loading,setLoading, error} = useEmployeesAllOrByProjectId( selectedProject );
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [employeeList,setEmployeeList] = useState([])
|
||||
const [modelConfig, setModelConfig] = useState();
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const [data, setData] = useState([]);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage] = useState(7);
|
||||
const [itemsPerPage] = useState(5);
|
||||
const [isCreateModalOpen, setIsCreateModalOpen ] = useState( false );
|
||||
const [searchText,setSearchText] = useState("")
|
||||
const [filteredData, setFilteredData] = useState([]);
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const value = e.target.value.toLowerCase();
|
||||
setSearchText(value);
|
||||
|
||||
if (!employeeList.length) return;
|
||||
|
||||
const results = employeeList.filter((item) =>
|
||||
Object.values(item).some((field) =>
|
||||
field && field.toString().toLowerCase().includes(value)
|
||||
)
|
||||
);
|
||||
|
||||
setFilteredData(results);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if ( loginUser && projects )
|
||||
{
|
||||
const filteredProjects = projects.filter((project) =>
|
||||
loginUser?.projects?.map(Number).includes(project.id)
|
||||
);
|
||||
setProjectsList(filteredProjects);
|
||||
}
|
||||
}, [ loginUser, projects ] );
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPage( 1 )
|
||||
|
||||
if (!loading && Array.isArray(employees)) {
|
||||
setEmployeeList(employees);
|
||||
setFilteredData(employees);
|
||||
}
|
||||
|
||||
}, [loading, employees, selectedProject]);
|
||||
|
||||
|
||||
const displayData = searchText ? filteredData :employeeList
|
||||
|
||||
|
||||
const indexOfLastItem = currentPage * itemsPerPage;
|
||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
||||
const currentItems = Array.isArray(displayData)
|
||||
? displayData.slice(indexOfFirstItem, indexOfLastItem)
|
||||
: [];
|
||||
|
||||
const paginate = (pageNumber) => setCurrentPage(pageNumber);
|
||||
const totalPages = Array.isArray(displayData)
|
||||
? Math.ceil(displayData.length / itemsPerPage)
|
||||
: 0;
|
||||
|
||||
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
const openModal = () => {
|
||||
setIsCreateModalOpen(true);
|
||||
};
|
||||
|
||||
const projects_cache = getCachedData("projectslist");
|
||||
if (!projects_cache) {
|
||||
ProjectRepository.getProjectList().then((response) => {
|
||||
setProjects(response);
|
||||
cacheData("projectslist", response);
|
||||
});
|
||||
} else {
|
||||
if (!projects.length) setProjects(projects_cache);
|
||||
}
|
||||
|
||||
const fetchData = async (projectid) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
EmployeeRepository.getEmployeeListByproject(projectid)
|
||||
.then((response) => {
|
||||
setEmployeeList(response);
|
||||
setData(response);
|
||||
cacheData("employeelist", response);
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleManageEmployee = (id) => {
|
||||
if (id != null) {
|
||||
window.location.href = "/employee/manage/" + id;
|
||||
} else {
|
||||
window.location.href = "/employee/manage";
|
||||
}
|
||||
};
|
||||
|
||||
const handleProjectChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setSelectedProject(value);
|
||||
fetchData(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(selectedProject);
|
||||
}, []);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsCreateModalOpen(false);
|
||||
|
||||
@ -94,14 +112,9 @@ const EmployeeList = () => {
|
||||
if (modelConfig !== null) {
|
||||
openModal();
|
||||
}
|
||||
}, [modelConfig, isCreateModalOpen]);
|
||||
}, [ modelConfig, isCreateModalOpen ] );
|
||||
|
||||
const indexOfLastItem = currentPage * itemsPerPage;
|
||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
||||
const currentItems = employees.slice(indexOfFirstItem, indexOfLastItem);
|
||||
|
||||
const paginate = (pageNumber) => setCurrentPage(pageNumber);
|
||||
const totalPages = Math.ceil(employees.length / itemsPerPage);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -130,19 +143,33 @@ const EmployeeList = () => {
|
||||
className="dataTables_length text-start"
|
||||
id="DataTables_Table_0_length"
|
||||
>
|
||||
|
||||
<label>
|
||||
<select
|
||||
onChange={handleProjectChange}
|
||||
id="project-select"
|
||||
onChange={(e)=>setSelectedProject(e.target.value)}
|
||||
name="DataTables_Table_0_length"
|
||||
aria-controls="DataTables_Table_0"
|
||||
className="form-select form-select-sm"
|
||||
value={selectedProject || ""}
|
||||
>
|
||||
<option value="0">All Projects</option>
|
||||
{projects.map((item) => (
|
||||
<option value={item.id}>{item.name}</option>
|
||||
{projectLoading ? (
|
||||
<option value="Loading" >
|
||||
Loading...
|
||||
</option>
|
||||
) : (
|
||||
<>
|
||||
<option value="">All Employees</option>
|
||||
{Array.isArray(projectsList) && projectsList.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -155,6 +182,8 @@ const EmployeeList = () => {
|
||||
<label>
|
||||
<input
|
||||
type="search"
|
||||
value={searchText}
|
||||
onChange={handleSearch}
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Search User"
|
||||
aria-controls="DataTables_Table_0"
|
||||
@ -213,12 +242,10 @@ const EmployeeList = () => {
|
||||
</li>
|
||||
</ul>
|
||||
<button
|
||||
className="btn btn-sm add-new btn-primary"
|
||||
className={`btn btn-sm add-new btn-primary `}
|
||||
// ${hasUserPermission("81ab8a87-8ccd-4015-a917-0627cee6a100")?"":"d-none"}
|
||||
tabIndex="0"
|
||||
// aria-controls="DataTables_Table_0"
|
||||
type="button"
|
||||
// data-bs-toggle="offcanvas"
|
||||
// data-bs-target="#offcanvasAddUser"
|
||||
>
|
||||
<span>
|
||||
<Link
|
||||
@ -280,17 +307,7 @@ const EmployeeList = () => {
|
||||
>
|
||||
Role
|
||||
</th>
|
||||
<th
|
||||
className="sorting d-none d-sm-table-cell"
|
||||
tabIndex="0"
|
||||
aria-controls="DataTables_Table_0"
|
||||
rowSpan="1"
|
||||
colSpan="1"
|
||||
// style={{ width: "148px" }}
|
||||
aria-label="Role: activate to sort column ascending"
|
||||
>
|
||||
Current Project
|
||||
</th>
|
||||
|
||||
<th
|
||||
className="sorting d-none d-md-table-cell"
|
||||
tabIndex="0"
|
||||
@ -325,7 +342,16 @@ const EmployeeList = () => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currentItems.map((item) => (
|
||||
{loading && <tr>
|
||||
<td colSpan={8}>
|
||||
<p>Loading...</p>
|
||||
</td>
|
||||
</tr>}
|
||||
{( !loading && employeeList?.length === 0 ) && <td colSpan={8}>Not Data Found </td>}
|
||||
{( !loading && employeeList && currentItems.length === 0 && employeeList.length !==0 ) && <td colSpan={8}><small className="muted">'{searchText}' employee not found</small> </td>}
|
||||
|
||||
|
||||
{(currentItems && !loading) && currentItems.map((item) => (
|
||||
<tr className="odd" key={item.id}>
|
||||
<td className="sorting_1" colSpan={2}>
|
||||
<div className="d-flex justify-content-start align-items-center user-name">
|
||||
@ -335,12 +361,9 @@ const EmployeeList = () => {
|
||||
></Avatar>
|
||||
<div className="d-flex flex-column">
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent default link behavior
|
||||
window.location.href = "/employee/" + item.id;
|
||||
}}
|
||||
className="text-heading text-truncate"
|
||||
|
||||
onClick={()=>navigate(`/employee/${item.id}?for=account`)}
|
||||
className="text-heading text-truncate cursor-pointer"
|
||||
>
|
||||
<span className="fw-medium">
|
||||
{item.firstName} {item.lastName}
|
||||
@ -355,17 +378,12 @@ const EmployeeList = () => {
|
||||
{item.phoneNumber}
|
||||
</span>
|
||||
</td>
|
||||
<td className=" d-none d-sm-table-cell">
|
||||
<td className=" d-none d-sm-table-cell text-start">
|
||||
<span className="text-truncate">
|
||||
<i className="bx bxs-wrench text-success me-2"></i>
|
||||
</span>
|
||||
</td>
|
||||
<td className=" d-none d-sm-table-cell">
|
||||
<span className="text-heading d-none d-sm-table-cell">
|
||||
<i className="bx bx-home text-primary me-2"></i>
|
||||
Assigned Project
|
||||
<i className="bx bxs-wrench text-success me-2"></i>{item.jobRole || "Not Assign Yet"}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td className=" d-none d-md-table-cell">
|
||||
{moment(item.birthDate).format("DD-MMM-YYYY")}
|
||||
</td>
|
||||
@ -378,9 +396,8 @@ const EmployeeList = () => {
|
||||
</span>
|
||||
</td>
|
||||
<td className="d-flex justify-content-end justify-content-sm-center">
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="d-flex align-items-center ">
|
||||
<a
|
||||
href="javascript:;"
|
||||
className="btn btn-icon dropdown-toggle hide-arrow"
|
||||
data-bs-toggle="dropdown"
|
||||
>
|
||||
@ -390,11 +407,8 @@ const EmployeeList = () => {
|
||||
<div className="dropdown-menu dropdown-menu-end m-0">
|
||||
{" "}
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent default link behavior
|
||||
window.location.href = "/employee/" + item.id;
|
||||
}}
|
||||
|
||||
onClick={()=> navigate(`/employee/${item.id}`)}
|
||||
className="dropdown-item"
|
||||
>
|
||||
View
|
||||
@ -403,13 +417,14 @@ const EmployeeList = () => {
|
||||
|
||||
> */}
|
||||
<Link
|
||||
className="dropdown-item"
|
||||
className={`dropdown-item `}
|
||||
// ${hasUserPermission("81ab8a87-8ccd-4015-a917-0627cee6a100")?"":"d-none"}
|
||||
to={`/employee/manage/${item.id}`}
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
{/* </a> */}
|
||||
<a href="javascript:;" className="dropdown-item">
|
||||
<a className="dropdown-item">
|
||||
Suspend
|
||||
</a>
|
||||
<a
|
||||
@ -421,13 +436,14 @@ const EmployeeList = () => {
|
||||
handleConfigData(item.id);
|
||||
}}
|
||||
>
|
||||
ManageRole
|
||||
Manage Role
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
) )}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@ -449,7 +465,7 @@ const EmployeeList = () => {
|
||||
«
|
||||
</button>
|
||||
</li>
|
||||
{[...Array(totalPages)].map((_, index) => (
|
||||
{[...Array(totalPages)]?.map((_, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={`page-item ${
|
||||
|
@ -5,35 +5,20 @@ import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import EmployeeNav from "../../components/Employee/EmployeeNav";
|
||||
import { useSearchParams,useParams } from "react-router-dom";
|
||||
import { getCachedData } from "../../slices/apiDataManager";
|
||||
import { useEmployees, useEmployeesByProject } from "../../hooks/useEmployees";
|
||||
import { useEmployeeProfile, useEmployees, useEmployeesByProject } from "../../hooks/useEmployees";
|
||||
import { useSelector } from "react-redux";
|
||||
import { base64ToFile } from "../../utils/dateUtils";
|
||||
import EmployeeRepository from "../../repositories/EmployeeRepository";
|
||||
|
||||
|
||||
const EmployeeProfile = () => {
|
||||
// const employeeAPI = (url = "http://localhost:5032/api/File/manage1") => {
|
||||
|
||||
// return {
|
||||
// fetchAll: () => axios.get(url),
|
||||
// create: (newRecord) => axios.post(url, newRecord),
|
||||
// update: (id, updatedRecord) => axios.put(url + id, updatedRecord),
|
||||
// delete: (id) => axios.delete(id),
|
||||
// };
|
||||
// };
|
||||
// const addOrEdit = (formData, onSuccess) => {
|
||||
// employeeAPI()
|
||||
// .create(formData)
|
||||
// .then((res) => {
|
||||
// onSuccess();
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.log(error);
|
||||
// });
|
||||
// };
|
||||
const projectID = useSelector((store)=>store.localVariables.projectId)
|
||||
const {employees} = useEmployeesByProject(projectID)
|
||||
const { employeeId,loading } = useParams();
|
||||
const {employeeId} = useParams();
|
||||
// const {employee,loading} = useEmployeeProfile(employeeId)
|
||||
const [loading,setLoading] = useState(true)
|
||||
|
||||
const [SearchParams] = useSearchParams()
|
||||
const tab = SearchParams.get("for")
|
||||
const tab = SearchParams.get( "for" )
|
||||
const [activePill, setActivePill] = useState(tab);
|
||||
const[currentEmployee,setCurrentEmployee] = useState()
|
||||
|
||||
@ -42,9 +27,32 @@ const EmployeeProfile = () => {
|
||||
setActivePill(pillKey);
|
||||
};
|
||||
|
||||
const fetchEmployeeProfile = async( employeeID ) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
const resp = await EmployeeRepository.getEmployeeProfile( employeeID )
|
||||
setCurrentEmployee( resp.data )
|
||||
setLoading(false)
|
||||
|
||||
} catch ( err )
|
||||
{
|
||||
console.log( "Faild to fetch employee data,", err )
|
||||
setLoading(false)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if ( employeeId )
|
||||
{
|
||||
fetchEmployeeProfile(employeeId)
|
||||
}
|
||||
}, [employeeId]);
|
||||
|
||||
|
||||
const renderContent = () => {
|
||||
// if (loading) return <Loader></Loader>;
|
||||
if (loading) return <div>Loading</div>;
|
||||
switch (activePill) {
|
||||
case "account": {
|
||||
return (
|
||||
@ -81,13 +89,12 @@ const EmployeeProfile = () => {
|
||||
};
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(employees && employees?.length > 0){
|
||||
setCurrentEmployee(employees?.find((emp)=>emp.id == employeeId))
|
||||
}
|
||||
},[employees])
|
||||
|
||||
console.log(currentEmployee)
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="container-xxl flex-grow-1 container-p-y">
|
||||
<Breadcrumb
|
||||
@ -98,22 +105,16 @@ console.log(currentEmployee)
|
||||
]}
|
||||
></Breadcrumb>
|
||||
|
||||
{/* <div>
|
||||
<EmpProfile employee={null} addOrEdit={addOrEdit}></EmpProfile>
|
||||
</div> */}
|
||||
{ currentEmployee && (
|
||||
|
||||
<div className="row">
|
||||
<div className="col-12 col-md-8 col-lg-4 order-1 order-lg-1">
|
||||
<div className="row">
|
||||
|
||||
<div className="col-12 col-md-8 col-lg-4 order-1 order-lg-1">
|
||||
<div className="row">
|
||||
<div className="col-12 mb-4">
|
||||
<div className="card">
|
||||
<div className="card-body">
|
||||
<div className="d-flex flex-row flex-lg-column">
|
||||
<div className="d-flex flex-column justify-content-center align-items-center text-center " >
|
||||
<div className="d-flex flex-column justify-content-center align-items-center text-center">
|
||||
<img
|
||||
src="../../../public/img/avatars/00.png"
|
||||
src={`../../../public/img/avatars/${currentEmployee.gender}.jpg`}
|
||||
alt="user-avatar"
|
||||
className="d-block rounded"
|
||||
height="100"
|
||||
@ -122,56 +123,60 @@ console.log(currentEmployee)
|
||||
id="uploadedAvatar"
|
||||
/>
|
||||
<div className="py-2">
|
||||
<p className="h6">{currentEmployee?.firstName + " "+ currentEmployee?.lastName}</p>
|
||||
<p className="h6">{`${currentEmployee?.firstName} ${currentEmployee?.lastName}`}</p>
|
||||
</div>
|
||||
<hr className="my-2 " />
|
||||
<hr className="my-2" />
|
||||
</div>
|
||||
<div className="w-100 d-flex flex-row flex-sm-column justify-content-sm-start justify-content-around">
|
||||
|
||||
<div className="text-wrap">
|
||||
<small className="card-text text-uppercase text-muted small">
|
||||
Contacts
|
||||
</small>
|
||||
<small className="card-text text-uppercase text-muted small">Contacts</small>
|
||||
<ul className="list-unstyled my-3 py-1">
|
||||
<li className="d-flex align-items-center mb-4">
|
||||
<i className="bx bx-user"></i>
|
||||
<span className="fw-medium mx-2">Contact:</span>{" "}
|
||||
<span className="text-muted"><em>NA</em></span>
|
||||
</li>
|
||||
|
||||
<li className="d-flex align-items-center mb-4">
|
||||
<i className="bx bx-phone"></i>
|
||||
<span className="fw-medium mx-2">Contact Number:</span>{" "}
|
||||
<span className={`${currentEmployee.emergencyPhoneNumber ? "":"text-muted"}`}>{currentEmployee.emergencyPhoneNumber || <em>NA</em>}</span>
|
||||
<span className="fw-medium mx-2">Contact Number:</span>
|
||||
<span className={`${currentEmployee?.emergencyPhoneNumber ? "" : "text-muted"}`}>
|
||||
{currentEmployee?.emergencyPhoneNumber || <em>NA</em>}
|
||||
</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-4 ">
|
||||
<li className="d-flex align-items-center mb-4 text-start">
|
||||
<i className="bx bx-envelope"></i>
|
||||
<span className="fw-medium mx-2">Email:</span>
|
||||
<span lassName={`text-break text-wrap ${currentEmployee.email ? "":"text-muted"}`}>{currentEmployee?.email || <em className="muted">NA</em>}</span>
|
||||
<span className={`text-break text-wrap ${currentEmployee?.email ? "" : "text-muted"}`}>
|
||||
{currentEmployee?.email || <em className="muted">NA</em>}
|
||||
</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-4">
|
||||
<i className="bx bx-user"></i>
|
||||
<span className="fw-medium mx-2">Contact Person:</span>
|
||||
<span className="">
|
||||
{currentEmployee?.emergencyContactPerson}
|
||||
</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center text-wrap ">
|
||||
<i className="bx bx-flag"></i>
|
||||
<span className="fw-medium mx-2">Address:</span>{" "}
|
||||
<span className="fw-medium mx-2">Address:</span>
|
||||
|
||||
</li>
|
||||
<li className="d-flex align-items-start test-start mb-4">
|
||||
<span className={`${currentEmployee?.peramnentAddress ? "":"text-muted"}`}>{currentEmployee?.peramnentAddress || <em>NA</em>}</span>
|
||||
<li className="d-flex align-items-start test-start mb-2">
|
||||
<span className={`${currentEmployee?.permanentAddress ? "" : "ms-4"}`}>
|
||||
{currentEmployee?.peramnentAddress}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-lg-8 order-2 order-lg-2 mb-4">
|
||||
<div className="row">
|
||||
<EmployeeNav
|
||||
onPillClick={handlePillClick}
|
||||
activePill={activePill}
|
||||
/>
|
||||
<EmployeeNav onPillClick={handlePillClick} activePill={activePill} />
|
||||
</div>
|
||||
<div className="card">
|
||||
<div className="row row-bordered g-0">
|
||||
@ -179,39 +184,9 @@ console.log(currentEmployee)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
{loading && (<p>Loading...</p>)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployeeProfile;
|
||||
{/* <div>
|
||||
<small className="card-text text-uppercase text-muted small">
|
||||
Profile
|
||||
</small>
|
||||
<ul className="list-unstyled my-3 py-1">
|
||||
<li className="d-flex align-items-center mb-4">
|
||||
<i className="bx bx-check"></i>
|
||||
<span className="fw-medium mx-2">Start Date:</span>{" "}
|
||||
<span>
|
||||
|
||||
</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-4">
|
||||
<i class="bx bx-stop-circle"></i>{" "}
|
||||
<span className="fw-medium mx-2">End Date:</span>{" "}
|
||||
<span>
|
||||
|
||||
</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-2">
|
||||
<i class="bx bx-trophy"></i>
|
||||
<span className="fw-medium mx-2">Status:</span>{" "}
|
||||
<span>active</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div> */}
|
@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import MasterModal from "../../components/master/MasterModal";
|
||||
import { List} from "../../data/masters";
|
||||
import { mastersList} from "../../data/masters";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import useMaster from "../../hooks/masterHook/useMaster"
|
||||
@ -32,14 +32,14 @@ const MasterPage = () => {
|
||||
const modalElement = document.getElementById('master-modal');
|
||||
if (modalElement) {
|
||||
modalElement.classList.remove('show');
|
||||
modalElement.style.display = 'none'; // Hide modal visually
|
||||
document.body.classList.remove('modal-open'); // Unlock body scroll
|
||||
modalElement.style.display = 'none';
|
||||
document.body.classList.remove('modal-open');
|
||||
|
||||
|
||||
const backdropElement = document.querySelector('.modal-backdrop');
|
||||
if (backdropElement) {
|
||||
backdropElement.classList.remove('modal-backdrop'); // Remove backdrop class
|
||||
backdropElement.style.display = 'none'; // Hide the backdrop element
|
||||
backdropElement.classList.remove('modal-backdrop');
|
||||
backdropElement.style.display = 'none';
|
||||
}
|
||||
}
|
||||
const modalBackdropElement = document.querySelector('.modal-backdrop');
|
||||
@ -85,9 +85,15 @@ const MasterPage = () => {
|
||||
openModal();
|
||||
}
|
||||
|
||||
}, [modalConfig,isCreateModalOpen]);
|
||||
}, [ modalConfig, isCreateModalOpen ] );
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setIsCreateModalOpen(false)
|
||||
closeModal();
|
||||
};
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -121,13 +127,15 @@ const MasterPage = () => {
|
||||
>
|
||||
<label>
|
||||
<select
|
||||
select onChange={(e) => dispatch(changeMaster(e.target.value))} value={selectedMaster}
|
||||
onChange={(e) => dispatch(changeMaster(e.target.value))}
|
||||
name="DataTables_Table_0_length"
|
||||
aria-controls="DataTables_Table_0"
|
||||
className="form-select form-select-sm"
|
||||
value={selectedMaster}
|
||||
>
|
||||
<option value="select" disabled selected>Select Master</option>
|
||||
{List.map((item) => (
|
||||
<option value="" disabled >Select Master</option>
|
||||
{mastersList.map( ( item ) => (
|
||||
|
||||
<option key={item.id} value={item.name}>{item.name}</option>
|
||||
))}
|
||||
</select>
|
||||
@ -157,7 +165,8 @@ const MasterPage = () => {
|
||||
<div className="input-group">
|
||||
|
||||
<button
|
||||
className="btn btn-sm add-new btn-primary"
|
||||
className={`btn btn-sm add-new btn-primary `}
|
||||
// ${hasUserPermission('660131a4-788c-4739-a082-cbbf7879cbf2') ? "":"d-none"}
|
||||
tabIndex="0"
|
||||
aria-controls="DataTables_Table_0"
|
||||
type="button"
|
||||
|
@ -1,26 +1,34 @@
|
||||
import React, { useEffect,useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import {MASTER_MANAGE} from "../../utils/constants";
|
||||
|
||||
|
||||
const MasterTable = ({ data, columns, loading, handleModalData }) => {
|
||||
const MasterTable = ( {data, columns, loading, handleModalData} ) =>
|
||||
{
|
||||
const hasMasterPermission = useHasUserPermission(MASTER_MANAGE) // for master manage permission id
|
||||
const selectedMaster = useSelector((store)=>store.localVariables.selectedMaster)
|
||||
const hiddenColumns = ["id", "featurePermission"];
|
||||
const hiddenColumns = ["id", "featurePermission","tenant","tenantId"];
|
||||
|
||||
const safeData = Array.isArray(data) ? data : [];
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage] = useState(10);
|
||||
const indexOfLastItem = currentPage * itemsPerPage;
|
||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
||||
const currentItems = data.slice(indexOfFirstItem, indexOfLastItem);
|
||||
const currentItems = safeData.slice( indexOfFirstItem, indexOfLastItem );
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
}, [ safeData ] )
|
||||
|
||||
const paginate = (pageNumber) => setCurrentPage(pageNumber);
|
||||
const totalPages = Math.ceil(data.length / itemsPerPage);
|
||||
const totalPages = Math.ceil(safeData.length / itemsPerPage);
|
||||
|
||||
const updatedColumns = columns
|
||||
.filter((col) => !hiddenColumns.includes(col.key))
|
||||
.map((col) => ({
|
||||
...col,
|
||||
label: col.key === "role" || "module" || "status" ? "Name" : col.label,
|
||||
label:
|
||||
col.key === "role" || col.key === "module" || col.key === "status" ? "Name" : col.label,
|
||||
}));
|
||||
|
||||
return (
|
||||
@ -28,19 +36,20 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
|
||||
{loading ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<table className="datatables-users table border-top dataTable no-footer dtr-column"
|
||||
<table
|
||||
className="datatables-users table border-top dataTable no-footer dtr-column"
|
||||
id="DataTables_Table_0"
|
||||
aria-describedby="DataTables_Table_0_info"
|
||||
style={{ width: "100%" }}>
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
{/* {updatedColumns.map((col) => (
|
||||
<th key={col.key}>{col.label}</th>
|
||||
))} */}
|
||||
{/* Example of using updatedColumns */}
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Actions</th>
|
||||
<th className={` ${hasMasterPermission? "":"d-none"}`}>Actions</th>
|
||||
{/* ${hasUserPermission('660131a4-788c-4739-a082-cbbf7879cbf2') ? "":"d-none"} */}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -49,10 +58,14 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
|
||||
<tr key={index}>
|
||||
<td></td>
|
||||
{updatedColumns.map((col) => (
|
||||
<td className="text-start mx-2" key={col.key}>{item[col.key] || " --- "}</td>
|
||||
<td className="text-start mx-2" key={col.key}>
|
||||
{item[col.key] !== undefined && item[col.key] !== null
|
||||
? item[col.key]
|
||||
: " --- "}
|
||||
</td>
|
||||
))}
|
||||
<td>
|
||||
|
||||
<td >
|
||||
{/* className={` ${hasUserPermission('660131a4-788c-4739-a082-cbbf7879cbf2') ? "":"d-none"}`}> */}
|
||||
<button
|
||||
aria-label="Modify"
|
||||
type="button"
|
||||
@ -72,6 +85,7 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
|
||||
data-bs-target="#master-modal"
|
||||
onClick={() => handleModalData("delete", item)}
|
||||
>
|
||||
|
||||
<i className="bx bx-trash me-1 text-danger"></i>
|
||||
</button>
|
||||
</td>
|
||||
@ -87,27 +101,29 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
|
||||
)}
|
||||
|
||||
{/* Pagination */}
|
||||
{
|
||||
!loading && (
|
||||
<nav aria-label="Page " >
|
||||
{!loading && (
|
||||
<nav aria-label="Page ">
|
||||
<ul className="pagination pagination-sm justify-content-end py-1">
|
||||
<li className={`page-item ${currentPage === 1 ? 'disabled' : ''}`}>
|
||||
<button className="page-link btn-xs" onClick={() => paginate(currentPage - 1)}>«</button>
|
||||
<li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}>
|
||||
<button className="page-link btn-xs" onClick={() => paginate(currentPage - 1)}>
|
||||
«
|
||||
</button>
|
||||
</li>
|
||||
{[...Array(totalPages)].map((_, index) => (
|
||||
<li key={index} className={`page-item ${currentPage === index + 1 ? 'active' : ''}`}>
|
||||
<button className="page-link " onClick={() => paginate(index + 1)}>
|
||||
<li key={index} className={`page-item ${currentPage === index + 1 ? "active" : ""}`}>
|
||||
<button className="page-link" onClick={() => paginate(index + 1)}>
|
||||
{index + 1}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
<li className={`page-item ${currentPage === totalPages ? 'disabled' : ''}`}>
|
||||
<button className="page-link " onClick={() => paginate(currentPage + 1)}>»</button>
|
||||
<li className={`page-item ${currentPage === totalPages ? "disabled" : ""}`}>
|
||||
<button className="page-link" onClick={() => paginate(currentPage + 1)}>
|
||||
»
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -11,25 +11,25 @@ import ProjectInfra from "../../components/Project/ProjectInfra";
|
||||
import Loader from "../../components/common/Loader";
|
||||
import WorkPlan from "../../components/Project/WorkPlan";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import { ActivityeRepository } from "../../repositories/MastersRepository";
|
||||
|
||||
import "./ProjectDetails.css";
|
||||
import {useEmployeesByProjectAllocated} from "../../hooks/useProjects";
|
||||
|
||||
const ProjectDetails = ({ props }) => {
|
||||
|
||||
const ProjectDetails = () => {
|
||||
let { projectId } = useParams();
|
||||
|
||||
const [project, setProject] = useState(null);
|
||||
const [projectDetails, setProjectDetails] = useState(null);
|
||||
const [ projectDetails, setProjectDetails ] = useState( null );
|
||||
const [activities, setActivities] = useState(null);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchActivities = async () => {
|
||||
try {
|
||||
|
||||
const activities_cache = getCachedData("activitiesMaster");
|
||||
|
||||
if (!activities_cache) {
|
||||
@ -41,68 +41,41 @@ const ProjectDetails = ({ props }) => {
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
// api
|
||||
// .get("/api/task/activities")
|
||||
// .then((data) => {
|
||||
// setActivities(data);
|
||||
// dispatch(cacheApiResponse({ key: "activitiesMaster", data: data }));
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// setError("Failed to fetch data.");
|
||||
// });
|
||||
|
||||
} else {
|
||||
setActivities(activities_cache);
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch activities.");
|
||||
} finally {
|
||||
// setLoading(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
|
||||
const project_cache = getCachedData(`projectinfo-${projectId}`);
|
||||
if (!project_cache) {
|
||||
ProjectRepository.getProjectByprojectId(projectId)
|
||||
.then((response) => {
|
||||
.then( ( response ) =>
|
||||
{
|
||||
setProjectDetails(response);
|
||||
setProject(response);
|
||||
cacheData(`projectinfo-${projectId}`, response);
|
||||
cacheData( `projectinfo-${ projectId }`, response );
|
||||
setLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setError("Failed to fetch data.");
|
||||
setError( "Failed to fetch data." );
|
||||
setLoading(false)
|
||||
});
|
||||
} else {
|
||||
setProjectDetails(project_cache);
|
||||
setProjectDetails( project_cache );
|
||||
setProject( project_cache );
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
// api
|
||||
// .get(`/api/project/details/${projectId}`)
|
||||
// .then((data) => {
|
||||
// setProjectDetails(data);
|
||||
// setProject(data);
|
||||
// dispatch(
|
||||
// cacheApiResponse({ key: `projectinfo-${projectId}`, data: data })
|
||||
// );
|
||||
// setLoading(false);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// setError("Failed to fetch data.");
|
||||
// });
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const [activePill, setActivePill] = useState("profile");
|
||||
|
||||
// Event handler to set the active pill
|
||||
|
||||
const handlePillClick = (pillKey) => {
|
||||
setActivePill(pillKey);
|
||||
};
|
||||
@ -111,7 +84,7 @@ const ProjectDetails = ({ props }) => {
|
||||
fetchData();
|
||||
};
|
||||
|
||||
// Dynamically render content based on the active pill
|
||||
|
||||
const renderContent = () => {
|
||||
if (loading) return <Loader></Loader>;
|
||||
switch (activePill) {
|
||||
@ -125,7 +98,7 @@ const ProjectDetails = ({ props }) => {
|
||||
</div>
|
||||
<div className="col-xl-4 col-lg-5 col-md-5">
|
||||
{/* Profile Overview */}
|
||||
<ProjectOverview></ProjectOverview>
|
||||
<ProjectOverview project={ project} />
|
||||
{/* Profile Overview */}
|
||||
</div>
|
||||
</div>
|
||||
@ -167,14 +140,12 @@ const ProjectDetails = ({ props }) => {
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-lg-12 col-xl-12">
|
||||
{/* Activity Timeline */}
|
||||
<ActivityTimeline></ActivityTimeline>
|
||||
{/* Activity Timeline */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
//return <ComponentC />;
|
||||
|
||||
default:
|
||||
return <div>Select a pill to display content here.</div>;
|
||||
}
|
||||
@ -197,19 +168,18 @@ const ProjectDetails = ({ props }) => {
|
||||
]}
|
||||
></Breadcrumb>
|
||||
|
||||
{/* Header */}
|
||||
<div className="row">
|
||||
<ProjectBanner data={project}></ProjectBanner>
|
||||
{loading && <p>Loading....</p>}
|
||||
{!loading && <ProjectBanner project_data={project} ></ProjectBanner>}
|
||||
</div>
|
||||
{/* Header */}
|
||||
{/* Navbar pills */}
|
||||
|
||||
<div className="row">
|
||||
<ProjectNav
|
||||
onPillClick={handlePillClick}
|
||||
activePill={activePill}
|
||||
></ProjectNav>
|
||||
</div>
|
||||
{/* Navbar pills */}
|
||||
|
||||
{renderContent()}
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,122 +1,99 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import ProjectCard from "../../components/Project/ProjectCard";
|
||||
import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
|
||||
import showToast from "../../services/toastService";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
|
||||
import {
|
||||
cacheData,
|
||||
clearCacheKey,
|
||||
getCachedData,
|
||||
} from "../../slices/apiDataManager";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import showToast from "../../services/toastService";
|
||||
import {
|
||||
getCachedData, cacheData
|
||||
} from "../../slices/apiDataManager";
|
||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission"
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import {MANAGE_PROJECT} from "../../utils/constants";
|
||||
|
||||
const ProjectList = () =>
|
||||
{
|
||||
const {profile: loginUser} = useProfile();
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT)
|
||||
const { projects, loading, error, refetch } = useProjects();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [projectList, setProjectList] = useState([]);
|
||||
const dispatch = useDispatch();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage] = useState(5);
|
||||
|
||||
|
||||
const ProjectList = () => {
|
||||
const { projects,loading,error} = useProjects()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
// -------hook-----
|
||||
// const [projects, setProjects] = useState([]);
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
//const projects_cache = getCachedData("projectslist");
|
||||
|
||||
const openModal = () => {
|
||||
console.log("clicked")
|
||||
setIsCreateModalOpen(true);
|
||||
};
|
||||
|
||||
// Close the modal
|
||||
const closeModal = () => {
|
||||
setIsCreateModalOpen(false);
|
||||
const modalBackdrop = document.querySelector(".modal-backdrop");
|
||||
if (modalBackdrop) modalBackdrop.remove();
|
||||
};
|
||||
|
||||
// const fetchData = async () => {
|
||||
// const projects_cache = getCachedData("projectslist");
|
||||
// if (!projects_cache) {
|
||||
// ProjectRepository.getProjectList()
|
||||
// .then((response) => {
|
||||
// setProjects(response);
|
||||
// cacheData("projectslist", response);
|
||||
// setLoading(false);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// setError("Failed to fetch data.");
|
||||
// });
|
||||
// } else {
|
||||
// if (!projects.length) setProjects(projects_cache);
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleFormSubmit = (updatedProject) => {
|
||||
delete updatedProject.id;
|
||||
//console.log("Form submitted:", updatedProject);
|
||||
ProjectRepository.manageProject(updatedProject)
|
||||
.then((response) => {
|
||||
clearCacheKey("projectslist");
|
||||
fetchData();
|
||||
showToast("Project updated successfully.", "success");
|
||||
closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
|
||||
// api
|
||||
// .post("/api/project", updatedProject)
|
||||
// .then((data) => {
|
||||
// clearCacheKey("projectslist");
|
||||
// fetchData();
|
||||
// showToast("Project updated successfully.", "success");
|
||||
// closeModal();
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// showToast(error.message, "error");
|
||||
// });
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// fetchData();
|
||||
// }, []);
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal( false );
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (isCreateModalOpen) {
|
||||
const modalElement = document.getElementById("create-project-model");
|
||||
const modal = new window.bootstrap.Modal(modalElement);
|
||||
modal.show();
|
||||
if (projects && projects.length > 0) {
|
||||
setProjectList(
|
||||
projects?.filter((project) =>
|
||||
loginUser?.projects?.map(Number).includes(project.id)
|
||||
)
|
||||
);
|
||||
}
|
||||
dispatch(changeMaster("JobRole"))
|
||||
}, [isCreateModalOpen]);
|
||||
}, [projects, loginUser?.projects, loading]);
|
||||
|
||||
const handleSubmitForm = (newProject) => {
|
||||
ProjectRepository.manageProject(newProject)
|
||||
.then((response) => {
|
||||
const cachedProjects_list = getCachedData("projectslist") || [];
|
||||
const updated_Projects_list = [...cachedProjects_list, response];
|
||||
cacheData("projectslist", updated_Projects_list);
|
||||
setProjectList(updated_Projects_list);
|
||||
showToast("Project Created successfully.", "success");
|
||||
setShowModal(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
closeModal();
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
};
|
||||
|
||||
const handleReFresh = () => {
|
||||
if (!projects || projects.length === 0) {
|
||||
refetch();
|
||||
}
|
||||
setRefresh((prev) => !prev);
|
||||
};
|
||||
|
||||
const indexOfLastItem = currentPage * itemsPerPage;
|
||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
||||
const currentItems = Array.isArray(projectList)
|
||||
? projectList.slice(indexOfFirstItem, indexOfLastItem)
|
||||
: [];
|
||||
|
||||
const paginate = (pageNumber) => setCurrentPage(pageNumber);
|
||||
const totalPages = Array.isArray(projectList)
|
||||
? Math.ceil(projectList.length / itemsPerPage)
|
||||
: 0;
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCreateModalOpen && (
|
||||
<div
|
||||
className={`modal fade `}
|
||||
id="create-project-model"
|
||||
className={`modal fade ${showModal ? 'show' : ''}`}
|
||||
tabIndex="-1"
|
||||
aria-hidden="true"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? 'block' : 'none' }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={null}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleFormSubmit}
|
||||
handleSubmitForm={handleSubmitForm}
|
||||
onClose={handleClose}
|
||||
></ManageProjectInfo>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="container-xxl flex-grow-1 container-p-y">
|
||||
<Breadcrumb
|
||||
data={[
|
||||
@ -126,26 +103,101 @@ const ProjectList = () => {
|
||||
></Breadcrumb>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4 text-end">
|
||||
<div
|
||||
className={`col-md-12 col-lg-12 col-xl-12 order-0 mb-4 ${
|
||||
!error && !projects ? "text-center" : "text-end"
|
||||
}`}
|
||||
>
|
||||
{" "}
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-primary"
|
||||
className={`btn btn-sm btn-primary ${
|
||||
HasManageProjectPermission
|
||||
? ""
|
||||
: "d-none"
|
||||
}`}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#create-project-model"
|
||||
onClick={openModal}
|
||||
onClick={handleShow}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Create New Project
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{((error && !loading) || !projects) && (
|
||||
<p className="text-center text-body-secondary">
|
||||
There was an error loading the projects. Please try again.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{(!projects || projects.length === 0 || projectList.length == 0) &&
|
||||
!loading &&
|
||||
error && (
|
||||
<div className="text-center">
|
||||
<button
|
||||
className="btn btn-xs btn-label-secondary"
|
||||
onClick={handleReFresh}
|
||||
>
|
||||
Retry Fetching Projects
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="row">
|
||||
{loading && (<p class="text-center">Loading...</p>)}
|
||||
{projects?.map((item) => (
|
||||
<ProjectCard project={item} key={item.id}></ProjectCard>
|
||||
{loading && <p className="text-center">Loading...</p>}
|
||||
{currentItems &&
|
||||
currentItems?.map((item) => (
|
||||
<ProjectCard projectData={item} key={item.id}></ProjectCard>
|
||||
))}
|
||||
</div>
|
||||
{/* Pagination */}
|
||||
{!loading && (
|
||||
<nav aria-label="Page ">
|
||||
<ul className="pagination pagination-sm justify-content-end py-1">
|
||||
<li
|
||||
className={`page-item ${
|
||||
currentPage === 1 ? "disabled" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link btn-xs"
|
||||
onClick={() => paginate(currentPage - 1)}
|
||||
>
|
||||
«
|
||||
</button>
|
||||
</li>
|
||||
{[...Array(totalPages)]?.map((_, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={`page-item ${
|
||||
currentPage === index + 1 ? "active" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link "
|
||||
onClick={() => paginate(index + 1)}
|
||||
>
|
||||
{index + 1}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
<li
|
||||
className={`page-item ${
|
||||
currentPage === totalPages ? "disabled" : ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
className="page-link "
|
||||
onClick={() => paginate(currentPage + 1)}
|
||||
>
|
||||
»
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -2,8 +2,14 @@ import { api } from "../utils/axiosClient";
|
||||
|
||||
const AuthRepository = {
|
||||
login: (data) => api.post("/api/auth/login", data),
|
||||
refreshToken: (data) => api.post("/api/auth/refresh-token", data),
|
||||
logout: (data) => api.post("/api/auth/logout", data),
|
||||
refreshToken: ( data ) => api.post( "/api/auth/refresh-token", data ),
|
||||
|
||||
logout: ( data ) => api.post( "/api/auth/logout", data ),
|
||||
profile: () => api.get( `/api/user/profile` ),
|
||||
register: ( data ) => api.post( 'api/auth/register', data ),
|
||||
resetPassword: ( data ) => api.post( '/api/auth/reset-password', data ),
|
||||
forgotPassword: (data) => api.post( '/api/auth/forgot-password', data),
|
||||
sendMail:(data)=>api.post("/api/auth/sendmail",data)
|
||||
};
|
||||
|
||||
export default AuthRepository;
|
||||
|
@ -1,12 +1,18 @@
|
||||
import { api } from "../utils/axiosClient";
|
||||
|
||||
const EmployeeRepository = {
|
||||
getAllEmployeeList:()=>api.get(`api/employee/list`),
|
||||
getEmployeeListByproject: (projectid) =>
|
||||
api.get(`/api/employee/get/${projectid}`),
|
||||
searchEmployees: (query) => api.get(`/api/employee/search/${query}`),
|
||||
manageEmployee: (data) => api.post("/api/employee/manage", data,{"Content-Type": "multipart/form-data",}),
|
||||
api.get(`/api/employee/list/${projectid}`),
|
||||
searchEmployees: (query) =>
|
||||
api.get(`/api/employee/search/${query}`),
|
||||
manageEmployee: (data) =>
|
||||
api.post("/api/employee/manage", data, {
|
||||
"Content-Type": "multipart/form-data",
|
||||
}),
|
||||
updateEmployee: (id, data) => api.put(`/users/${id}`, data),
|
||||
deleteEmployee: (id) => api.delete(`/users/${id}`),
|
||||
deleteEmployee: ( id ) => api.delete( `/users/${ id }` ),
|
||||
getEmployeeProfile:(id)=>api.get(`/api/Employee/profile/get/${id}`)
|
||||
};
|
||||
|
||||
export default EmployeeRepository;
|
||||
|
@ -6,16 +6,18 @@ const ProjectRepository = {
|
||||
api.get(`/api/project/details/${projetid}`),
|
||||
|
||||
getProjectAllocation: (projetid) =>
|
||||
api.get(`api/project/allocation/${projetid}`),
|
||||
api.get( `api/project/allocation/${ projetid }` ),
|
||||
|
||||
getEmployeesByProject:(projectId)=>api.get(`/api/Project/employees/get/${projectId}`),
|
||||
|
||||
manageProject: (data) => api.post("/api/project", data),
|
||||
updateProject: (data) => api.post("/api/project/update", data),
|
||||
// updateProject: (data) => api.post("/api/project/update", data),
|
||||
|
||||
manageProjectAllocation: (data) => api.post("/api/project/allocation", data),
|
||||
manageProjectInfra: (data) => api.post("/api/project/manage-infra", data),
|
||||
manageProjectTasks: (data) => api.post("/api/project/manage-infra", data),
|
||||
|
||||
updateProject: (id, data) => api.put(`/projects/${id}`, data),
|
||||
updateProject: (id, data) => api.put(`/api/project/update/${id}`, data),
|
||||
deleteProject: (id) => api.delete(`/projects/${id}`),
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,7 @@ import HomeLayout from "../Layouts/HomeLayout";
|
||||
import LoginPage from "../pages/Authentication/LoginPage";
|
||||
import RegisterPage from "../pages/Authentication/RegisterPage";
|
||||
import ForgotPasswordPage from "../pages/Authentication/ForgotPasswordPage";
|
||||
import ResetPasswordPage from "../pages/Authentication/ResetPasswordPage";
|
||||
|
||||
import Dashboard from "../components/Dashboard/Dashboard";
|
||||
import ProjectList from "../pages/project/ProjectList";
|
||||
@ -28,13 +29,9 @@ import DailyTask from "../pages/Activities/DailyTask";
|
||||
import AttendancePage from "../pages/Activities/AttendancePage";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const AppRoutes = () => {
|
||||
return (
|
||||
<Router>
|
||||
|
||||
<Routes>
|
||||
{/* Authentication Routes */}
|
||||
<Route element={<AuthLayout />}>
|
||||
@ -44,9 +41,10 @@ const AppRoutes = () => {
|
||||
path="/auth/forgot-password"
|
||||
element={<ForgotPasswordPage />}
|
||||
/>
|
||||
|
||||
|
||||
<Route path="/reset-password" element={<ResetPasswordPage />} />
|
||||
</Route>
|
||||
|
||||
|
||||
{/* Protected Routes */}
|
||||
<Route element={<ProtectedRoute />}>
|
||||
<Route element={<HomeLayout />}>
|
||||
@ -54,7 +52,10 @@ const AppRoutes = () => {
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route path="/projects" element={<ProjectList />} />
|
||||
<Route path="/projects/:projectId" element={<ProjectDetails />} />
|
||||
<Route path="/project/manage/:projectId" element={<ManageProject />}/>
|
||||
<Route
|
||||
path="/project/manage/:projectId"
|
||||
element={<ManageProject />}
|
||||
/>
|
||||
<Route path="/employees" element={<EmployeeList />} />
|
||||
<Route path="/employee/:employeeId" element={<EmployeeProfile />} />
|
||||
|
||||
@ -84,7 +85,6 @@ const AppRoutes = () => {
|
||||
{/* Fallback Route */}
|
||||
<Route path="*" element={<ErrorPage />} />
|
||||
</Routes>
|
||||
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
@ -6,9 +6,8 @@ import AuthRepository from "../repositories/AuthRepository";
|
||||
const ProtectedRoute = () => {
|
||||
// const isAuthenticated = localStorage.getItem("jwtToken"); // Example authentication check
|
||||
// // const isAuthenticated = true;
|
||||
|
||||
// isTokenValid();
|
||||
// return isAuthenticated ? <Outlet /> : <Navigate to="/auth/login" />;
|
||||
// return isAuthenticated ? <Outlet /> : <Navigate to="/auth/login" />
|
||||
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(null);
|
||||
|
||||
|
@ -4,6 +4,8 @@ import {
|
||||
clearApiCacheKey,
|
||||
flushApiCache,
|
||||
} from "../slices/apiCacheSlice";
|
||||
import {setLoginUserPermmisions} from "./globalVariablesSlice";
|
||||
|
||||
|
||||
// Cache data
|
||||
export const cacheData = (key, data) => {
|
||||
@ -24,3 +26,14 @@ export const clearCacheKey = (key) => {
|
||||
export const clearAllCache = () => {
|
||||
store.dispatch(flushApiCache());
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const cacheProfileData = ( data) => {
|
||||
store.dispatch(setLoginUserPermmisions(data));
|
||||
};
|
||||
|
||||
// Get cached data
|
||||
export const getCachedProfileData = () => {
|
||||
return store.getState().globalVariables.loginUser;
|
||||
};
|
@ -3,7 +3,7 @@ import AttendanceRepository from '../../repositories/AttendanceRepository';
|
||||
|
||||
// Fetch attendance data
|
||||
export const fetchAttendanceData = createAsyncThunk(
|
||||
'attendance/fetchAttendanceData',
|
||||
'attendanceLogs/fetchAttendanceData', // Updated action type prefix
|
||||
async ({ projectId, date }, thunkAPI) => {
|
||||
try {
|
||||
const response = await AttendanceRepository.getAttendanceFilteredByDate(projectId, date);
|
||||
@ -14,22 +14,22 @@ export const fetchAttendanceData = createAsyncThunk(
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// This method for marking attendance if a date filter is applied
|
||||
export const markAttendance = createAsyncThunk(
|
||||
'attendance/markAttendance',
|
||||
'attendanceLogs/markAttendance', // Updated action type prefix
|
||||
async (formData, thunkAPI) => {
|
||||
try {
|
||||
let newRecordAttendance = {
|
||||
id: formData.id || null,
|
||||
comment: formData.description,
|
||||
employeeID: formData.employeeId,
|
||||
projectID: formData.projectId ,
|
||||
projectID: formData.projectId,
|
||||
date: new Date().toISOString(),
|
||||
markTime: formData.time,
|
||||
latitude: formData.latitude.toString(),
|
||||
longitude: formData.longitude.toString(),
|
||||
action: formData.action,
|
||||
image: null
|
||||
image: null,
|
||||
};
|
||||
|
||||
const response = await AttendanceRepository.markAttendance(newRecordAttendance);
|
||||
@ -40,9 +40,9 @@ export const markAttendance = createAsyncThunk(
|
||||
}
|
||||
);
|
||||
|
||||
// Attendance Slice
|
||||
const attendanceSlice = createSlice({
|
||||
name: 'attendance',
|
||||
// Attendance Logs Slice
|
||||
const attendanceLogsSlice = createSlice({
|
||||
name: 'attendanceLogs', // Updated slice name
|
||||
initialState: {
|
||||
data: [],
|
||||
loading: false,
|
||||
@ -55,7 +55,7 @@ const attendanceSlice = createSlice({
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
// Fetch attendance
|
||||
// Fetch attendance data
|
||||
.addCase(fetchAttendanceData.pending, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
@ -82,5 +82,5 @@ const attendanceSlice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
export const { setAttendanceData } = attendanceSlice.actions;
|
||||
export default attendanceSlice.reducer;
|
||||
export const { setAttendanceData } = attendanceLogsSlice.actions;
|
||||
export default attendanceLogsSlice.reducer;
|
||||
|
32
src/slices/apiSlice/attendanceAllSlice.js
Normal file
32
src/slices/apiSlice/attendanceAllSlice.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import AttendanceRepository from '../../repositories/AttendanceRepository';
|
||||
|
||||
export const markCurrentAttendance = createAsyncThunk(
|
||||
'attendanceCurrentDate/markAttendance',
|
||||
async (formData, { getState, dispatch, rejectWithValue }) => {
|
||||
const { projectId } = getState().localVariables
|
||||
try {
|
||||
// Create the new attendance record
|
||||
const newRecordAttendance = {
|
||||
id: null,
|
||||
comment: formData.description,
|
||||
employeeID: formData.employeeId,
|
||||
projectId: projectId,
|
||||
date: new Date().toISOString(),
|
||||
markTime: formData.time,
|
||||
latitude: formData.latitude.toString(),
|
||||
longitude: formData.longitude.toString(),
|
||||
action: formData.action,
|
||||
image: null,
|
||||
};
|
||||
|
||||
const response = await AttendanceRepository.markAttendance(newRecordAttendance);
|
||||
const markedAttendance = response.data
|
||||
return markedAttendance;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error marking attendance:', error);
|
||||
return rejectWithValue(error.message); // Reject with error message
|
||||
}
|
||||
}
|
||||
);
|
@ -2,14 +2,20 @@ import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const globalVariablesSlice = createSlice({
|
||||
name: "globalVariables",
|
||||
initialState: {},
|
||||
initialState: {
|
||||
loginUser:null
|
||||
},
|
||||
reducers: {
|
||||
setGlobalVariable: (state, action) => {
|
||||
const { key, value } = action.payload;
|
||||
state[key] = value;
|
||||
},
|
||||
setLoginUserPermmisions: ( state, action ) =>
|
||||
{
|
||||
state.loginUser = action.payload
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const { setGlobalVariable } = globalVariablesSlice.actions;
|
||||
export const { setGlobalVariable,setLoginUserPermmisions } = globalVariablesSlice.actions;
|
||||
export default globalVariablesSlice.reducer;
|
||||
|
@ -9,6 +9,6 @@ export const store = configureStore({
|
||||
apiCache: apiCacheReducer,
|
||||
globalVariables: globalVariablesReducer,
|
||||
localVariables:localVariableRducer,
|
||||
attendance: attendanceReducer,
|
||||
attendanceLogs: attendanceReducer,
|
||||
},
|
||||
});
|
||||
|
12
src/utils/authUtils.js
Normal file
12
src/utils/authUtils.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { useProfile } from "../hooks/useProfile";
|
||||
|
||||
export const hasUserPermission = (permission) => {
|
||||
const { profile } = useProfile();
|
||||
if (profile) {
|
||||
if (!permission || typeof permission !== "string") {
|
||||
return false;
|
||||
}
|
||||
return profile?.featurePermissions.includes(permission);
|
||||
}
|
||||
return false;
|
||||
};
|
@ -83,11 +83,11 @@ axiosClient.interceptors.response.use(
|
||||
refreshToken: refreshToken,
|
||||
});
|
||||
|
||||
const { token } = response.data;
|
||||
const { token } = response.data.data;
|
||||
|
||||
// Save the new access token
|
||||
localStorage.setItem("jwtToken", token);
|
||||
localStorage.setItem("refreshToken", response.data.refreshToken);
|
||||
localStorage.setItem("refreshToken", response.data.data.refreshToken);
|
||||
|
||||
// Update the original request with the new token
|
||||
originalRequest.headers["Authorization"] = `Bearer ${token}`;
|
||||
|
@ -1,2 +1,10 @@
|
||||
export const THRESH_HOLD = 12; // hours
|
||||
export const DURATION_TIME=10; // minutes
|
||||
export const DURATION_TIME = 10; // minutes
|
||||
|
||||
export const MASTER_MANAGE = "660131a4-788c-4739-a082-cbbf7879cbf2";
|
||||
|
||||
export const ASSIGN_USER_TO_PROJECT = "81ab8a87-8ccd-4015-a917-0627cee6a100";
|
||||
|
||||
export const INFRASTRUCTURE = "9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c";
|
||||
|
||||
export const MANAGE_PROJECT = "53176ebf-c75d-42e5-839f-4508ffac3def"
|
@ -54,25 +54,15 @@ export const convertShortTime=(dateString)=> {
|
||||
return timeDifferenceInHours >= timeElapsedInHours;
|
||||
}
|
||||
|
||||
export const base64ToFile = (base64String, fileName) => {
|
||||
// Ensure the string is valid and clean
|
||||
const cleanedBase64 = base64String.replace(/^data:.*,/, '').replace(/\s/g, '');
|
||||
|
||||
// Decode Base64 to binary data
|
||||
const byteString = atob(cleanedBase64);
|
||||
|
||||
// Get MIME type from Base64 string
|
||||
const mimeTypeMatch = base64String.match(/^data:(.*);base64,/);
|
||||
const mimeType = mimeTypeMatch ? mimeTypeMatch[1] : 'application/octet-stream';
|
||||
export const checkIfCurrentDate = (dateString) => {
|
||||
const currentDate = new Date();
|
||||
const inputDate = new Date(dateString);
|
||||
|
||||
// Convert binary data to Uint8Array
|
||||
const byteArray = new Uint8Array(byteString.length);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
byteArray[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return new File([byteArray], fileName, { type: mimeType });
|
||||
};
|
||||
|
||||
// Example usage
|
||||
currentDate.setHours(0, 0, 0, 0);
|
||||
inputDate.setHours(0, 0, 0, 0);
|
||||
|
||||
return currentDate.getTime() === inputDate.getTime();
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user