added taskreport, commentTask feature

This commit is contained in:
Pramod Mahajan 2025-04-06 18:03:10 +05:30
parent ddfbed1020
commit 10d6f96ea7
19 changed files with 1788 additions and 108 deletions

View File

@ -49,6 +49,7 @@
<!-- Timer Picker -->
<!-- Flatpickr CSS -->
<link rel="stylesheet" href="/assets/vendor/libs/flatpickr/flatpickr.css" />
@ -84,7 +85,7 @@
<script src="/assets/vendor/libs/select2/select2.js"></script>
<script src="/assets/vendor/libs/apex-charts/apexcharts.js"></script>
<script src="/assets/vendor/libs/jquery-timepicker/jquery-timepicker.js" ></script>
<script src="/assets/vendor/libs/flatpickr/flatpickr.js" ></script>
<!-- Main JS -->
<script src="/assets/js/main.js"></script>
@ -102,17 +103,7 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.min.js"></script> -->
<!-- Flatpickr JS -->
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
// Initialize flatpickr for 12-hour time format with AM/PM
flatpickr("#timePicker", {
enableTime: true,
noCalendar: true,
time_24hr: false, // Disable 24-hour format
dateFormat: "h:i K", // 12-hour format with AM/PM
});
</script>
</body>
</html>

View File

@ -0,0 +1,906 @@
.flatpickr-calendar {
position: absolute;
visibility: hidden;
overflow: hidden;
box-sizing: border-box;
padding: 0;
padding-bottom: 2px;
max-height: 0;
border: 0;
text-align: center;
opacity: 0;
animation: none;
outline: 0;
touch-action: manipulation;
line-height: 1.375;
font-size: 0.9375rem;
border-radius: 0.5rem;
}
.flatpickr-calendar.open, .flatpickr-calendar.inline {
visibility: visible;
overflow: visible;
max-height: 640px;
opacity: 1;
}
.flatpickr-calendar.open {
display: inline-block;
}
.flatpickr-calendar.animate.open {
animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.flatpickr-calendar:not(.inline):not(.open) {
display: none !important;
}
.flatpickr-calendar.inline {
position: relative;
top: 2px;
display: block;
}
.flatpickr-calendar.static {
position: absolute;
top: calc(100% + 2px);
}
.flatpickr-calendar.static.open {
z-index: 999;
display: block;
}
.flatpickr-calendar.hasWeeks {
width: auto;
}
html:not([dir=rtl]) .flatpickr-calendar.hasWeeks .flatpickr-days {
border-bottom-left-radius: 0 !important;
}
[dir=rtl] .flatpickr-calendar.hasWeeks .flatpickr-days {
border-bottom-right-radius: 0 !important;
}
.flatpickr-calendar.hasTime {
padding-bottom: 0;
}
.flatpickr-calendar.hasTime .flatpickr-time {
height: 40px;
}
.flatpickr-calendar.noCalendar.hasTime .flatpickr-time {
height: auto;
}
.flatpickr-calendar input[type=number] {
-moz-appearance: textfield;
}
.flatpickr-calendar input[type=number]::-webkit-inner-spin-button,
.flatpickr-calendar input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.flatpickr-wrapper {
position: relative;
display: inline-block;
}
.flatpickr-month {
position: relative;
overflow: hidden;
height: 3rem;
text-align: center;
line-height: 1;
user-select: none;
}
.flatpickr-prev-month,
.flatpickr-next-month {
position: absolute;
top: 0.75rem;
z-index: 3;
padding: 0 0.41rem;
height: 1.875rem;
width: 1.875rem;
text-decoration: none;
cursor: pointer;
border-radius: 0.375rem;
display: flex;
align-items: center;
justify-content: center;
}
.flatpickr-prev-month svg,
.flatpickr-next-month svg {
stroke-width: 2;
vertical-align: middle;
}
.flatpickr-prev-month i,
.flatpickr-next-month i {
position: relative;
}
.flatpickr-prev-month.flatpickr-prev-month {
left: 1rem;
}
[dir=rtl] .flatpickr-prev-month {
right: 1rem;
left: auto;
transform: scaleX(-1);
}
.flatpickr-next-month.flatpickr-prev-month {
right: 0;
left: 0;
}
.flatpickr-next-month.flatpickr-next-month {
right: 1rem;
}
[dir=rtl] .flatpickr-next-month {
right: auto;
left: 1rem;
transform: scaleX(-1);
}
.flatpickr-prev-month:hover,
.flatpickr-next-month:hover {
opacity: 1;
}
.flatpickr-prev-month svg,
.flatpickr-next-month svg {
width: 0.6rem;
}
.flatpickr-prev-month svg path,
.flatpickr-next-month svg path {
transition: fill 0.1s;
fill: inherit;
}
.numInputWrapper {
position: relative;
height: auto;
}
.numInputWrapper :hover {
background: transparent;
}
.numInputWrapper input,
.numInputWrapper span {
display: inline-block;
}
.numInputWrapper input {
width: 100%;
}
.numInputWrapper span {
position: absolute;
right: 0;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 14px;
height: 50%;
line-height: 1;
opacity: 0;
cursor: pointer;
}
[dir=rtl] .numInputWrapper span {
right: auto;
left: 0;
}
.numInputWrapper span:hover {
background: rgba(0, 0, 0, 0.1);
}
.numInputWrapper span:active {
background: rgba(0, 0, 0, 0.2);
}
.numInputWrapper span:after {
content: "";
display: block;
width: 0;
height: 0;
}
.numInputWrapper span.arrowUp {
top: 0;
}
.numInputWrapper span.arrowUp:after {
border-right: 4px solid transparent;
border-bottom: 4px solid rgba(72, 72, 72, 0.6);
border-left: 4px solid transparent;
}
.numInputWrapper span.arrowDown {
top: 50%;
}
.numInputWrapper span.arrowDown:after {
border-top: 4px solid rgba(72, 72, 72, 0.6);
border-right: 4px solid transparent;
border-left: 4px solid transparent;
}
.numInputWrapper span svg {
width: inherit;
height: auto;
}
.numInputWrapper span svg path {
fill: rgba(255, 255, 255, 0.5);
}
.numInputWrapper:hover {
background: rgba(0, 0, 0, 0.05);
}
.numInputWrapper:hover span {
opacity: 1;
}
.flatpickr-current-month {
position: absolute;
left: 12.5%;
display: flex;
align-items: center;
justify-content: center;
gap: 0.25rem;
width: 75%;
height: 2.5rem;
text-align: center;
font-weight: 300;
line-height: 1;
padding: 0.9rem 0 0 0;
transform: translate3d(0px, 0px, 0px);
}
.flatpickr-current-month .flatpickr-monthDropdown-months,
.flatpickr-current-month input.cur-year {
outline: none;
vertical-align: middle !important;
font-weight: 400;
font-size: inherit;
font-family: inherit;
line-height: inherit;
color: inherit;
display: inline-block;
box-sizing: border-box;
background: transparent;
border: 0;
border-radius: 0;
}
.flatpickr-current-month .flatpickr-monthDropdown-months:not(:first-child),
.flatpickr-current-month input.cur-year:not(:first-child) {
padding: 0 0 0 0.5ch;
}
.flatpickr-current-month .numInputWrapper {
display: inline-block;
width: 6ch;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
appearance: menulist;
cursor: pointer;
height: 2.25rem;
position: relative;
width: auto;
font-size: 0.9375rem;
}
.flatpickr-current-month input.cur-year {
margin: 0;
height: 1.2rem;
cursor: default;
}
[dir=rtl] .flatpickr-current-month input.cur-year {
padding-right: 0.5ch;
padding-left: 0;
}
.flatpickr-current-month input.cur-year:focus {
outline: 0;
}
.flatpickr-current-month input.cur-year[disabled], .flatpickr-current-month input.cur-year[disabled]:hover {
background: transparent;
pointer-events: none;
}
.flatpickr-current-month input.cur-year[disabled] {
opacity: 0.5;
}
.flatpickr-weekdaycontainer {
display: flex;
width: 100%;
padding: 0.25rem 0.6rem;
}
.flatpickr-weekdays {
display: flex;
overflow: hidden;
align-items: center;
max-width: 17.5rem;
width: 100%;
height: 2.25rem;
text-align: center;
margin-bottom: 0.125rem;
}
span.flatpickr-weekday {
display: block;
flex: 1;
margin: 0;
text-align: center;
line-height: 1;
cursor: default;
}
.dayContainer,
.flatpickr-weeks {
padding: 1px 0 0 0;
}
.flatpickr-days {
position: relative;
display: flex;
overflow: hidden;
width: auto !important;
}
.flatpickr-days:focus {
outline: 0;
}
.flatpickr-calendar.hasTime .flatpickr-days {
border-bottom: 0 !important;
border-bottom-right-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.dayContainer {
display: inline-block;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
box-sizing: border-box;
padding: 0;
min-width: 15.75rem;
max-width: 15.75rem;
width: 15.75rem;
outline: 0;
opacity: 1;
transform: translate3d(0px, 0px, 0px);
}
.flatpickr-day {
position: relative;
display: inline-block;
flex-basis: 14.2857143%;
justify-content: center;
box-sizing: border-box;
margin: 0;
max-width: 2.25rem;
width: 15.2857143%;
height: 2.25rem;
border: 1px solid transparent;
background: none;
text-align: center;
font-weight: 400;
line-height: calc(2.25rem - 2px);
cursor: pointer;
}
.flatpickr-day.inRange, .flatpickr-day.prevMonthDay.inRange, .flatpickr-day.nextMonthDay.inRange, .flatpickr-day.today.inRange, .flatpickr-day.prevMonthDay.today.inRange, .flatpickr-day.nextMonthDay.today.inRange, .flatpickr-day:hover, .flatpickr-day.prevMonthDay:hover, .flatpickr-day.nextMonthDay:hover, .flatpickr-day:focus, .flatpickr-day.prevMonthDay:focus, .flatpickr-day.nextMonthDay:focus {
outline: 0;
cursor: pointer;
}
.flatpickr-day.inRange:not(.startRange):not(.endRange) {
border-radius: 0 !important;
}
.flatpickr-day.disabled, .flatpickr-day.flatpickr-disabled, .flatpickr-day.flatpickr-disabled.today, .flatpickr-day.disabled:hover, .flatpickr-day.flatpickr-disabled:hover, .flatpickr-day.flatpickr-disabled.today:hover {
border-color: transparent;
background: transparent !important;
cursor: default;
pointer-events: none;
box-shadow: none;
}
.flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay {
border-color: transparent;
background: transparent;
cursor: default;
}
.flatpickr-day.notAllowed, .flatpickr-day.notAllowed.prevMonthDay, .flatpickr-day.notAllowed.nextMonthDay {
border-color: transparent;
background: transparent;
cursor: default;
}
.flatpickr-day.week.selected {
border-radius: 0;
}
html:not([dir=rtl]) .flatpickr-day.selected.startRange, html:not([dir=rtl]) .flatpickr-day.startRange.startRange, html:not([dir=rtl]) .flatpickr-day.endRange.startRange {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
html:not([dir=rtl]) .flatpickr-day.selected.endRange, html:not([dir=rtl]) .flatpickr-day.startRange.endRange, html:not([dir=rtl]) .flatpickr-day.endRange.endRange {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
[dir=rtl] .flatpickr-day.selected.startRange, [dir=rtl] .flatpickr-day.startRange.startRange, [dir=rtl] .flatpickr-day.endRange.startRange {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
[dir=rtl] .flatpickr-day.selected.endRange, [dir=rtl] .flatpickr-day.startRange.endRange, [dir=rtl] .flatpickr-day.endRange.endRange {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.flatpickr-weekwrapper {
display: inline-block;
float: left;
}
.flatpickr-weekwrapper .flatpickr-weeks {
background-clip: padding-box !important;
}
html:not([dir=rtl]) .flatpickr-weekwrapper .flatpickr-weeks .flatpickr-weeks {
border-bottom-right-radius: 0 !important;
}
[dir=rtl] .flatpickr-weekwrapper .flatpickr-weeks .flatpickr-weeks {
border-bottom-left-radius: 0 !important;
}
.flatpickr-weekwrapper .flatpickr-weeks .flatpickr-day {
font-size: 0.8125rem;
}
.flatpickr-weekwrapper .flatpickr-calendar.hasTime .flatpickr-weeks {
border-bottom: 0 !important;
border-bottom-right-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.flatpickr-weekwrapper .flatpickr-weekday {
float: none;
width: 100%;
line-height: 2.25rem;
position: relative;
top: 1px;
margin-bottom: 0.4rem;
}
.flatpickr-weekwrapper span.flatpickr-day {
display: block;
max-width: none;
width: 2.25rem;
background: none !important;
}
.flatpickr-calendar.hasTime .flatpickr-weeks {
border-bottom: 0 !important;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.flatpickr-innerContainer {
display: block;
display: flex;
overflow: hidden;
box-sizing: border-box;
}
html:not([dir=rtl]) .flatpickr-innerContainer:has(.flatpickr-weeks) .flatpickr-weeks {
padding-left: 0.445rem;
}
[dir=rtl] .flatpickr-innerContainer:has(.flatpickr-weeks) .flatpickr-weeks {
padding-left: 0.445rem;
}
[dir=rtl] .flatpickr-innerContainer:has(.flatpickr-weeks) .flatpickr-weekdaycontainer {
padding-left: 0.625rem;
}
.flatpickr-innerContainer:has(.flatpickr-weeks) .flatpickr-weekwrapper .flatpickr-weekday {
padding-left: 0.445rem;
}
[dir=rtl] .flatpickr-innerContainer:has(.flatpickr-weeks) .flatpickr-weekwrapper {
padding-right: 0.5rem;
}
.flatpickr-rContainer {
display: inline-block;
box-sizing: border-box;
padding: 0;
}
.flatpickr-time {
display: block;
display: flex;
overflow: hidden;
box-sizing: border-box;
max-height: 40px;
height: 0;
outline: 0;
background-clip: padding-box !important;
text-align: center;
line-height: 40px;
}
.flatpickr-time:after {
content: "";
display: table;
clear: both;
}
.flatpickr-time .numInputWrapper {
float: left;
flex: 1;
width: 40%;
height: 40px;
}
.flatpickr-time.hasSeconds .numInputWrapper {
width: 26%;
}
.flatpickr-time.time24hr .numInputWrapper {
width: 49%;
}
.flatpickr-time input {
position: relative;
box-sizing: border-box;
margin: 0;
padding: 0;
height: inherit;
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
text-align: center;
line-height: inherit;
cursor: pointer;
font-size: 0.9375rem;
}
.flatpickr-time input.flatpickr-hour, .flatpickr-time input.flatpickr-minute, .flatpickr-time input.flatpickr-second {
font-weight: 400;
}
.flatpickr-time input:focus {
outline: 0;
border: 0;
}
.flatpickr-time .flatpickr-time-separator,
.flatpickr-time .flatpickr-am-pm {
display: inline-block;
float: left;
align-self: center;
width: 2%;
height: inherit;
line-height: inherit;
user-select: none;
}
.flatpickr-time .flatpickr-am-pm {
width: 18%;
outline: 0;
text-align: center;
font-weight: normal;
cursor: pointer;
}
.flatpickr-time .flatpickr-am-pm:hover {
background: rgba(0, 0, 0, 0.05);
}
.flatpickr-calendar.noCalendar .flatpickr-time {
box-shadow: none !important;
}
.flatpickr-calendar:not(.noCalendar) .flatpickr-time {
border-top: 0;
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
.flatpickr-input[readonly] {
cursor: pointer;
}
@-webkit-keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@-moz-keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.light-style .flatpickr-calendar {
background: #fff;
}
.light-style .flatpickr-prev-month,
.light-style .flatpickr-next-month {
background-color: #edeef0;
}
.light-style .flatpickr-prev-month svg,
.light-style .flatpickr-next-month svg {
fill: #646e78;
stroke: #646e78;
}
.light-style .flatpickr-calendar,
.light-style .flatpickr-days {
width: calc(16.75rem + 0 * 2px) !important;
}
.light-style .flatpickr-calendar {
background-color: #fff;
border-radius: 0.375rem !important;
}
.light-style:not([dir=rtl]) .flatpickr-calendar.hasWeeks {
width: calc(19rem + 0 * 3px + 0.35rem) !important;
}
.light-style[dir=rtl] .flatpickr-calendar.hasWeeks {
width: calc(19rem + 0 * 3px + 1rem) !important;
}
.light-style .flatpickr-calendar.open {
z-index: 1091;
}
.light-style .flatpickr-input[readonly],
.light-style .flatpickr-input ~ .form-control[readonly] {
background: transparent;
}
.light-style .flatpickr-days {
background: #fff;
padding: 0.25rem 0.5rem 0.5rem;
border: 0 solid #e4e6e8;
border-top: 0;
background-clip: padding-box;
border-bottom-right-radius: 0.375rem;
border-bottom-left-radius: 0.375rem;
}
.light-style:not([dir=rtl]) .flatpickr-calendar.hasWeeks .flatpickr-days {
border-left: 0;
padding-left: calc(0.5rem + 0px);
box-shadow: 0 0 0 #e4e6e8 inset;
}
.light-style[dir=rtl] .flatpickr-calendar.hasWeeks .flatpickr-days {
border-right: 0;
padding-right: calc(0.5rem + 0px);
box-shadow: 0 0 0 #e4e6e8 inset;
}
.light-style .flatpickr-calendar {
line-height: 1.375;
font-size: 0.9375rem;
box-shadow: 0 0.25rem 0.75rem 0 rgba(34, 48, 62, 0.14);
border-radius: 0.375rem;
}
.light-style .flatpickr-calendar.hasTime:not(.noCalendar):not(.hasTime) .flatpickr-time {
display: none !important;
}
.light-style .flatpickr-calendar.hasTime .flatpickr-time {
box-shadow: 0 1px 0 #e4e6e8 inset;
}
.light-style .flatpickr-monthDropdown-months {
color: #384551;
}
.light-style .flatpickr-current-month {
font-size: 112%;
color: #384551;
}
.light-style .flatpickr-current-month .cur-month,
.light-style .flatpickr-current-month .cur-year {
font-size: 0.9375rem;
font-weight: 400;
color: #384551;
}
.light-style .flatpickr-month,
.light-style span.flatpickr-weekday,
.light-style .flatpickr-weekdays {
background: #fff;
}
.light-style .flatpickr-month {
border-top-left-radius: 0.375rem;
border-top-right-radius: 0.375rem;
}
.light-style .flatpickr-month option.flatpickr-monthDropdown-month {
color: #384551;
background: transparent;
}
.light-style span.flatpickr-weekday {
color: #384551;
font-size: 0.8125rem;
}
.light-style .flatpickr-day {
color: #384551;
border-radius: 0.375rem;
}
.light-style .flatpickr-day:hover, .light-style .flatpickr-day:focus, .light-style .flatpickr-day.prevMonthDay:hover, .light-style .flatpickr-day.nextMonthDay:hover, .light-style .flatpickr-day.today:hover, .light-style .flatpickr-day.prevMonthDay:focus, .light-style .flatpickr-day.nextMonthDay:focus, .light-style .flatpickr-day.today:focus {
color: #384551;
background: #f2f3f3;
}
.light-style .flatpickr-day:hover:not(.today), .light-style .flatpickr-day:focus:not(.today), .light-style .flatpickr-day.prevMonthDay:hover:not(.today), .light-style .flatpickr-day.nextMonthDay:hover:not(.today), .light-style .flatpickr-day.today:hover:not(.today), .light-style .flatpickr-day.prevMonthDay:focus:not(.today), .light-style .flatpickr-day.nextMonthDay:focus:not(.today), .light-style .flatpickr-day.today:focus:not(.today) {
border-color: transparent;
}
.light-style .flatpickr-day.prevMonthDay, .light-style .flatpickr-day.nextMonthDay, .light-style .flatpickr-day.flatpickr-disabled {
color: #a7acb2 !important;
}
.light-style .flatpickr-day.prevMonthDay.today, .light-style .flatpickr-day.nextMonthDay.today, .light-style .flatpickr-day.flatpickr-disabled.today {
border: none;
}
.light-style .flatpickr-day.disabled {
color: #a7acb2 !important;
}
.light-style .flatpickr-day.selected.startRange.endRange {
border-radius: 0.375rem !important;
}
.light-style .flatpickr-weeks {
border-bottom: 0 solid #e4e6e8;
border-left: 0 solid #e4e6e8;
background: #fff;
border-bottom-right-radius: 0.375rem;
border-bottom-left-radius: 0.375rem;
border-bottom-right-radius: 0;
}
.light-style .flatpickr-weeks .flatpickr-day {
color: #384551;
}
.light-style[dir=rtl] .flatpickr-weeks {
border-right: 0 solid #e4e6e8;
border-left: 0;
border-bottom-right-radius: 0.375rem;
border-bottom-left-radius: 0.375rem;
border-bottom-left-radius: 0;
}
.light-style .flatpickr-time {
border: 0 solid #e4e6e8;
background: #fff;
border-radius: 0.375rem;
}
.light-style .flatpickr-time input {
color: #646e78;
font-size: 0.9375rem;
}
.light-style .flatpickr-time .numInputWrapper span.arrowUp:after {
border-bottom-color: #a7acb2;
}
.light-style .flatpickr-time .numInputWrapper span.arrowDown:after {
border-top-color: #a7acb2;
}
.light-style .flatpickr-time .flatpickr-am-pm {
color: #646e78;
}
.light-style .flatpickr-time .flatpickr-time-separator {
color: #646e78;
font-weight: 500;
}
.dark-style .flatpickr-calendar {
background: #2b2c40;
}
.dark-style .flatpickr-prev-month,
.dark-style .flatpickr-next-month {
background-color: rgba(230, 230, 241, 0.08);
}
.dark-style .flatpickr-prev-month svg,
.dark-style .flatpickr-next-month svg {
fill: #b2b2c4;
stroke: #b2b2c4;
}
.dark-style .flatpickr-calendar,
.dark-style .flatpickr-days {
width: calc(16.75rem + 0 * 2px) !important;
}
.dark-style:not([dir=rtl]) .flatpickr-calendar.hasWeeks {
width: calc(19rem + 0 * 3px + 0.355rem) !important;
}
.dark-style[dir=rtl] .flatpickr-calendar.hasWeeks {
width: calc(19rem + 0 * 3px + 1rem) !important;
}
.dark-style .flatpickr-calendar.open {
z-index: 1091;
}
.dark-style .flatpickr-input:not(.is-invalid):not(.is-valid) ~ .form-control:disabled,
.dark-style .flatpickr-input:not(.is-invalid):not(.is-valid)[readonly],
.dark-style .flatpickr-input:not(.is-invalid):not(.is-valid) ~ .form-control[readonly] {
background-color: transparent;
}
.dark-style .flatpickr-days {
border: 0 solid #4e4f6c;
border-top: 0;
padding: 0.25rem 0.5rem;
padding-bottom: 0.5rem;
background: #2b2c40;
background-clip: padding-box;
border-bottom-right-radius: 0.375rem;
border-bottom-left-radius: 0.375rem;
}
.dark-style:not([dir=rtl]) .flatpickr-calendar.hasWeeks .flatpickr-days {
border-left: 0;
padding-left: calc(0.5rem + 0px);
box-shadow: 0 0 0 #4e4f6c inset;
}
.dark-style[dir=rtl] .flatpickr-calendar.hasWeeks .flatpickr-days {
border-right: 0;
padding-right: calc(0.5rem + 0px);
box-shadow: 0 0 0 #4e4f6c inset;
}
.dark-style .flatpickr-calendar {
line-height: 1.375;
font-size: 0.9375rem;
box-shadow: 0 0.25rem 0.75rem 0 rgba(20, 20, 29, 0.24);
background-color: #2b2c40;
border-radius: 0.375rem;
}
.dark-style .flatpickr-calendar.hasTime:not(.noCalendar):not(.hasTime) .flatpickr-time {
display: none !important;
}
.dark-style .flatpickr-calendar.hasTime .flatpickr-time {
box-shadow: 0 1px 0 #4e4f6c inset;
}
.dark-style .flatpickr-month,
.dark-style span.flatpickr-weekday,
.dark-style .flatpickr-weekdays {
background: #2b2c40;
}
.dark-style .flatpickr-month {
border-top-left-radius: 0.375rem;
border-top-right-radius: 0.375rem;
}
.dark-style .flatpickr-month option.flatpickr-monthDropdown-month {
color: #b2b2c4;
background: #2b2c40;
}
.dark-style .flatpickr-monthDropdown-months {
color: #d5d5e2;
}
.dark-style .flatpickr-current-month {
font-size: 112%;
color: #d5d5e2;
}
.dark-style .flatpickr-current-month .cur-month,
.dark-style .flatpickr-current-month .cur-year {
font-size: 0.9375rem;
font-weight: 400;
color: #d5d5e2;
}
.dark-style span.flatpickr-weekday {
font-size: 0.8125rem;
color: #d5d5e2;
}
.dark-style .flatpickr-day {
color: #d5d5e2;
font-weight: 500;
border-radius: 0.375rem;
}
.dark-style .flatpickr-day:hover, .dark-style .flatpickr-day:focus, .dark-style .flatpickr-day.nextMonthDay:hover, .dark-style .flatpickr-day.prevMonthDay:hover, .dark-style .flatpickr-day.today:hover, .dark-style .flatpickr-day.nextMonthDay:focus, .dark-style .flatpickr-day.prevMonthDay:focus, .dark-style .flatpickr-day.today:focus {
border-color: transparent;
color: #d5d5e2;
background: #434463;
}
.dark-style .flatpickr-day.nextMonthDay, .dark-style .flatpickr-day.prevMonthDay, .dark-style .flatpickr-day.flatpickr-disabled {
color: #7e7f96 !important;
}
.dark-style .flatpickr-day.nextMonthDay.today, .dark-style .flatpickr-day.prevMonthDay.today, .dark-style .flatpickr-day.flatpickr-disabled.today {
border: 0;
}
.dark-style .flatpickr-day.selected.startRange.endRange {
border-radius: 0.375rem !important;
}
.dark-style .flatpickr-day.disabled {
color: #7e7f96 !important;
}
.dark-style .flatpickr-weeks {
border-bottom: 0 solid #4e4f6c;
border-left: 0 solid #4e4f6c;
background: #2b2c40;
border-bottom-right-radius: 0.375rem;
border-bottom-left-radius: 0.375rem;
border-bottom-right-radius: 0;
}
.dark-style .flatpickr-weeks .flatpickr-day {
color: #d5d5e2;
}
.dark-style[dir=rtl] .flatpickr-weeks {
border-right: 0 solid #4e4f6c;
border-left: 0;
}
.dark-style .flatpickr-time {
border: 0 solid #4e4f6c;
background: #2b2c40;
border-radius: 0.375rem;
}
.dark-style .flatpickr-time input {
color: #b2b2c4;
}
.dark-style .flatpickr-time .numInputWrapper span.arrowUp:after {
border-bottom-color: #7e7f96;
}
.dark-style .flatpickr-time .numInputWrapper span.arrowDown:after {
border-top-color: #7e7f96;
}
.dark-style .flatpickr-time .flatpickr-am-pm {
color: #b2b2c4;
}
.dark-style .flatpickr-time .flatpickr-time-separator {
color: #b2b2c4;
font-weight: 500;
}

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ import { useNavigate } from "react-router-dom";
const Attendance = ( {attendance, getRole, handleModalData} ) =>
{
console.log(attendance)
const { currentPage, totalPages, currentItems, paginate } = usePagination(attendance, 5);
const [loading,setLoading] = useState(false);
const navigate = useNavigate()
@ -47,7 +47,6 @@ console.log(attendance)
></Avatar>
<div className="d-flex flex-column">
<a
// href="#"
onClick={(e) =>navigate(`/employee/${item.employeeId}?for=attendance`)}
className="text-heading text-truncate cursor-pointer"
>
@ -68,7 +67,7 @@ console.log(attendance)
<td>{item.checkInTime ? convertShortTime(item.checkInTime):"--"}</td>
<td>{item.checkOutTime ? convertShortTime(item.checkOutTime):"--"}</td>
<td className="mx-24" >
<td className="text-center" >
<RenderAttendanceStatus attendanceData={item} handleModalData={handleModalData} Tab={1} currentDate={null}/>
</td>
</tr>

View File

@ -4,7 +4,7 @@ import AttendLogs from './AttendLogs'
import Confirmation from './Confirmation'
const AttendanceModel = ({modelConfig,closeModal,handleSubmitForm}) => {
console.log(modelConfig)
return (
<div className={`modal-dialog modal-md modal-simple ${modelConfig.type === "view" ? "modal-lg":"modal-md"}`} >
<div className="modal-content">

View File

@ -21,11 +21,15 @@ const InfraPlanning = () =>
{
const {profile: LoggedUser} = useProfile()
const dispatch = useDispatch()
const {projects,loading:project_listLoader,error:projects_error} = useProjects()
const selectedProject = useSelector((store)=>store.localVariables.projectId)
const ManageInfra = useHasUserPermission( MANAGE_PROJECT_INFRA )
const {projects,loading:project_listLoader,error:projects_error} = useProjects()
const {projects_Details, loading: project_deatilsLoader, error: project_error} = useProjectDetails(selectedProject)
useEffect( () =>
{
dispatch(setProjectId(projects[0]?.id))
})
return (
<div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4">
@ -93,8 +97,13 @@ const InfraPlanning = () =>
</button>
</div> */}
</div>
<div className="row ">
<InfraTable buildings={projects_Details?.buildings}/>
<div className="row ">
{project_deatilsLoader && ( <p>Loading...</p> )}
{( !project_deatilsLoader && projects_Details?.buildings.length === 0 ) && ( <p>No Result Found</p> )}
{(!project_deatilsLoader && projects_Details?.buildings?.length > 0) && (<InfraTable buildings={projects_Details?.buildings}/>)}
</div>
</div>
</div>

View File

@ -23,7 +23,7 @@ const RenderAttendanceStatus = ({ attendanceData, handleModalData,Tab,currentDat
};
return (
<div className="w-auto d-flex gap-2 align-items-center justify-content-between">
<div className="d-flex justify-content-center">
<button
type="button"
className={`btn btn-xs ${color}`}
@ -39,7 +39,7 @@ const RenderAttendanceStatus = ({ attendanceData, handleModalData,Tab,currentDat
{attendanceData?.checkInTime && (
<button
type="button"
className="btn btn-xs btn-success"
className="btn btn-xs btn-success ms-2"
tabIndex="0"
aria-controls="DataTables_Table_0"
data-bs-toggle="modal"

View File

@ -0,0 +1,180 @@
import React, { useState } from "react";
import { formatDate } from "../../utils/dateUtils";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import showToast from "../../services/toastService";
import { TasksRepository } from "../../repositories/TaskRepository";
export const ReportTask = ({ report,closeModal,refetch }) => {
const [ loading, setloading ] = useState( false );
const schema = z.object({
completedTask: z
.number()
.min(1, "Completed Work must be at least 1")
.max(
report?.plannedTask,
`Completed Work cannot exceed ${report?.plannedTask}`
)
.int("Completed Work must be an integer")
.positive("Completed Work must be a positive number")
.optional(),
comment: z.string().min(1, "Comment cannot be empty"),
});
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
});
const onSubmit = async (data) => {
try
{
setloading(true)
const reportData = {
...data,
id: report?.id,
reportedDate: new Date().toISOString(),
};
let response = await TasksRepository.reportTsak( reportData )
showToast( "succesfully", "success" )
refetch()
closeModal()
} catch (error) {
showToast("Somthing wrog", "error");
}
};
const handleClose = () => {
closeModal();
};
return (
<div className="modal-dialog modal-md modal-simple report-task-modal" role="document">
<div className="modal-content">
<div className="modal-body px-1">
<button
type="button"
className="btn-close"
onClick={handleClose}
aria-label="Close"
></button>
<div className="container m-0">
<div className="d-flex justify-content-between">
<figure class="text-start p-0 m-0">
<blockquote class="blockquote">
<small> Assigned Date : {formatDate(report?.assignmentDate)}</small>
</blockquote>
</figure>
<figure class="text-end p-0 m-0">
<blockquote class="blockquote">
<small>Assigned By</small>
</blockquote>
<figcaption className="blockquote-footer mb-0">
{/* <div className="d-flex avatar avatar-xs">
<span className="avatar-initial rounded-circle bg-label-primary">
{report?.assignedBy?.firstName.slice(0, 1)}
</span> */}
<cite title="Source Title m-0">{` ${report?.assignedBy.firstName} ${report?.assignedBy.lastName}`}</cite>
{/* </div> */}
</figcaption>
</figure>
</div>
<div className="d-flex p-0 m-0">
<div className="flex-shrink-0 mt-1 mx-sm-0 px-2 mx-auto">
<i class="bx bx-buildings"></i>
</div>
<p class="lead">{report?.workItem?.workArea?.floor?.building?.name}</p>
<p class="lead ms-12">{report?.workItem?.workArea?.floor?.floorName}</p>
</div>
<dl class="row text-start ms-3">
<dt class="col-sm-6">
Work Area : {report?.workItem?.workArea?.areaName}
</dt>
<dd class="col-sm-6">
<small> {report?.workItem?.activityMaster.activityName}</small>
</dd>
</dl>
<dl class="row text-start ms-3">
<dt class="col-sm-4">Team</dt>
<dd class="col-sm-4 d-flex align-items-center avatar-group justify-content-start">
{report?.teamMembers.map((member) => (
<>
<div
data-bs-toggle="tooltip"
data-bs-html="true"
data-popup="tooltip-custom"
data-bs-placement="top"
title={`${member.firstName} ${member.lastName}`}
className="avatar avatar-xs"
>
{/* <img src="..." alt="Avatar" class="rounded-circle pull-up" /> */}
<span className="avatar-initial rounded-circle bg-label-primary">
{member?.firstName.slice(0, 1)}
</span>
</div>
</>
))}
</dd>
<dt class="col-sm-4 text-start">Planned : {report?.plannedTask}</dt>
</dl>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row p-0">
<div className="col-4">
<input
{...register("completedTask", { valueAsNumber: true })}
id="smallInput"
className="form-control form-control-sm"
type="number"
placeholder="Completed Work"
/>
{errors.completedTask && (
<div className="text-danger">{errors.completedTask.message}</div>
)}
</div>
<div className="col-8">
<textarea
{...register("comment")}
className="form-control"
id="exampleFormControlTextarea1"
rows="1"
placeholder="Enter comment"
/>
{errors.comment && (
<div className="text-danger">{errors.comment.message}</div>
)}
</div>
</div>
<div className="col-12 text-center my-2">
<button type="submit" className="btn btn-sm btn-primary me-3">
{loading ? "Please wait":"Submit Report"}
</button>
<button
type="button"
className="btn btn-sm btn-label-secondary"
onClick={handleClose}
>
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,158 @@
import React, {useEffect, useState} from "react";
import {useProfile} from "../../hooks/useProfile";
import moment from "moment";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {TasksRepository} from "../../repositories/TaskRepository";
import showToast from "../../services/toastService";
const schema = z.object({
comment: z.string().min(1, "Comment cannot be empty"),
});
const ReportTaskComments = ( {commentsData, closeModal} ) =>
{
const [loading,setloading]=useState(false)
const {profile} = useProfile()
const [comments,setComment] = useState([])
const {
register,
handleSubmit,
formState: { errors },reset
} = useForm({
resolver: zodResolver(schema),
});
useEffect( () =>
{
setComment(commentsData?.comments
)
}, [commentsData] )
const isLoggedUser = ( usrId ) => profile?.employeeInfo.id;
const onSubmit = async(data) =>
{
let sendComment = {
...data,
taskAllocationId: commentsData?.id,
commentDate: new Date().toISOString(),
}
try
{
setloading(true)
// const resp = await TasksRepository.taskComments( sendComment );
// console.timeLog( resp )
reset()
setloading(false)
showToast( "Successfully Sent", "success" )
closeModal()
} catch ( err )
{
setloading(false)
showToast(error.response.data?.message || "Something wrong","error")
}
}
return (
<div
className="modal-dialog modal-md modal-simple report-task-comments-modal"
role="document"
>
<div className="modal-content">
<div className="modal-body px-1">
<button
type="button"
className="btn-close"
onClick={closeModal}
aria-label="Close"
></button>
<div className="container ">
{
comments && comments.map( ( data ) =>
(
<div className="text-start" key={data.id}>
<div class={`li-wrapper d-flex justify-content-${isLoggedUser(data?.employee?.id) ? "end":"start"} align-items-start`}>
<div class="avatar avatar-xs me-1">
<span class="avatar-initial rounded-circle bg-label-success">
M
</span>
</div>
<div class="text-start py-0">
<p class="mb-0">
<strong>{ `${data?.employee?.firstName} ${data?.employee?.lastName}`}</strong>
</p>
<small style={{fontSize: "10px"}}>{ moment(data?.commentDate).fromNow()}</small>
</div>
</div>
<p className={`ms-${ isLoggedUser( data?.employee?.id ) ? "0 text-end me-6" : "6 " } mt-1`}>{ data?.comment
}</p>
</div>
))
}
{/* by other users */}
{/* <div className="text-start">
<div class="li-wrapper d-flex justify-content-start align-items-start">
<div class="avatar avatar-xs me-1">
<span class="avatar-initial rounded-circle bg-label-success">
M
</span>
</div>
<div class="text-start py-0">
<p class="mb-0">
<strong>Mahajan</strong>
</p>
<small style={{ fontSize: "10px" }}>2 hour ago</small>
</div>
</div>
<p className="ms-6 mt-1">Stylized implementation of HTMLs element for abbreviations and acronyms to show the expanded version on hover. Abbreviations have a default underline and gain a help cursor to provide additional context on hov </p>
</div> */}
{/* by login usrer */}
{/* <div className="text-start">
<div class="li-wrapper d-flex justify-content-end align-items-start">
<div class="avatar avatar-xs me-1">
<span class="avatar-initial rounded-circle bg-label-success">
M
</span>
</div>
<div class="text-start py-0">
<p class="mb-0">
<strong>Pramod Mahajan</strong>
</p>
<small style={{ fontSize: "10px" }}>2 hour ago</small>
</div>
</div>
<p className="ms-6 mt-1">Stylized implementation of HTMLs element for abbreviations and acronyms to show the expanded version on hover. Abbreviations have a default underline and gain a help cursor to provide additional context on hov </p>
</div> */}
<form onSubmit={handleSubmit( onSubmit )}>
<textarea
{...register("comment")}
className="form-control"
id="exampleFormControlTextarea1"
rows="1"
placeholder="Enter comment"
/>
{errors.comment && (
<div className="danger-text">{errors.comment.message}</div>
)}
<div class="text-end my-1">
<button type="button" class="btn btn-secondary" onClick={closeModal} data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary ms-2">{ loading ? "Sending...":"Comment"}</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
};
export default ReportTaskComments;

View File

@ -16,13 +16,12 @@ import showToast from "../../services/toastService";
const schema = z.object({
selectedEmployees: z.array( z.number() ).min( 1, {message: "At least one employee must be selected"} ),
description: z.string().min( 1, {message: "description required"} ),
pannedTask: z.number()
.refine(value => value > 0, { message: "pannedTask should be greater than zero" })
.refine(value => value !== undefined, { message: "pannedTask is required" }),
})
const AssignRoleModel = ( {assignData,onClose}) => {
const [ plannedTask, setPlannedTask ] = useState()
const { openModal, closeModal } = useModal()
const selectedProject = useSelector((store)=>store.localVariables.projectId)
const {employees} = useEmployeesAllOrByProjectId( selectedProject )
@ -78,7 +77,7 @@ const handleEmployeeSelection = (employeeId,field) => {
const removeEmployee = (employeeId) => {
setSelectedEmployees((prevSelected) => {
const updatedSelection = prevSelected.filter((id) => id !== employeeId);
setValue("selectedEmployees", updatedSelection);
setValue("selectedEmployees", updatedSelection); // Ensure form state is updated
return updatedSelection;
});
};
@ -105,7 +104,6 @@ const onSubmit = async(data) => {
}
};
console.log(assignData?.workItem?.workItem)
useEffect(()=>{
dispatch(changeMaster("Job Role"))
return ()=> setSelectedRole("all")
@ -212,6 +210,8 @@ useEffect(()=>{
</div>
</div>
)}
{selectedEmployees.length > 0 && (
<div className="mt-1">
<div className="text-start px-2">
@ -236,28 +236,22 @@ useEffect(()=>{
</div>
</div>
)}
{errors.selectedEmployees && (
<div className="danger-text mt-1">
<p>{errors.selectedEmployees[0]}</p>
</div>
)}
<div class="col-md text-start mx-0 px-0">
<div class="form-check form-check-inline mt-4 px-1">
<label className="form-text fs-6" for="inlineCheckbox1">Pending Work</label>
<label className="form-check-label ms-2" for="inlineCheckbox1">{ assignData?.workItem?.workItem?.plannedWork - assignData?.workItem?.workItem?.completedWork}</label>
</div>
<div className="form-check form-check-inline col-sm-2 col">
<label for="defaultFormControlInput" className="form-label">Target</label>
<Controller name="pannedTask" control={control} render={( {field} ) => (
<input type="text" className="form-control form-control-sm " {...field} />
)} />
{errors.pannedTask && (
<div className="danger-text">
<p>{errors.pannedTask.message}</p>
</div>
)}
<label for="defaultFormControlInput" className="form-label">Target</label>
<input type="text" className="form-control form-control-sm " value={plannedTask} onChange={(e)=>setPlannedTask(e.target.value)} id="defaultFormControlInput" aria-describedby="defaultFormControlHelp" />
</div>
</div>
{errors.selectedEmployees && (
<div className="danger-text mt-1">
<p>{errors.selectedEmployees[0]}</p>
</div>
)}
<label for="exampleFormControlTextarea1" className="form-label">Description</label>
<Controller

View File

@ -16,6 +16,7 @@ const buildingSchema = z.object({
const BuildingModel = ({
project,
onClose,
onSubmit,
clearTrigger,
onClearComplete,
@ -70,6 +71,8 @@ const BuildingModel = ({
className="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
onClick={onClose}
></button>
<h5 className="text-center mb-2">Manage Buildings - {project.name}</h5>
<form onSubmit={handleSubmit(onSubmitHandler)} className="row g-2">
@ -115,7 +118,7 @@ const BuildingModel = ({
<button type="submit" className="btn btn-primary me-3">
{formData.id ? "Edit Building" : "Add Building"}
</button>
<button type="reset" className="btn btn-label-secondary" data-bs-dismiss="modal" aria-label="Close">
<button type="reset" className="btn btn-label-secondary" data-bs-dismiss="modal" aria-label="Close" onClick={onClose}>
Cancel
</button>
</div>

View File

@ -2,11 +2,16 @@ import React, { useState } from "react";
import { useModal } from "../../../ModalContext";
import AssignRoleModel from "../AssignRole";
import {useParams} from "react-router-dom";
import GlobalModel from "../../common/GlobalModel";
const WorkItem = ( {workItem, forBuilding, forFloor, forWorkArea} ) =>{
const {projectId} = useParams()
const { openModal ,closedModal} = useModal();
const [itemName, setItemName] = useState('');
const [ itemName, setItemName ] = useState( '' );
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);
const getProgress = (planned, completed) => {
return (completed * 100) / planned + "%";
};
@ -16,12 +21,12 @@ const WorkItem = ( {workItem, forBuilding, forFloor, forWorkArea} ) =>{
setItemName('');
};
const showCreateItemModal = (modalData) => {
openModal(
<AssignRoleModel assignData={modalData} onClose={closedModal} />,
handleAssignTask ,"lg"
);
};
// const showCreateItemModal = (modalData) => {
// openModal(
// <AssignRoleModel assignData={modalData} onClose={closedModal} />,
// handleAssignTask ,"lg"
// );
// };
let assigndata = {
building: forBuilding,
@ -30,6 +35,11 @@ const WorkItem = ( {workItem, forBuilding, forFloor, forWorkArea} ) =>{
workItem
}
return (
<>
<GlobalModel isOpen={isModalOpen}
closeModal={closeModal} dialogClass="modal-dialog-centered" role="document" size="lg" >
<AssignRoleModel assignData={assigndata} onClose={closeModal} />
</GlobalModel>
<tr>
<td className="text-start table-cell-small">
<i className="bx bx-right-arrow-alt"></i>
@ -68,10 +78,7 @@ const WorkItem = ( {workItem, forBuilding, forFloor, forWorkArea} ) =>{
className="btn p-0"
data-bs-toggle="modal"
data-bs-target="#project-modal"
onClick={() =>
{
showCreateItemModal(assigndata)
}}
onClick={openModal}
>
<span className="badge bg-label-primary me-1">Assign</span>
</button>)}
@ -91,7 +98,8 @@ const WorkItem = ( {workItem, forBuilding, forFloor, forWorkArea} ) =>{
</button>
</div>
</td>
</tr>
</tr>
</>
);
};

View File

@ -5,12 +5,14 @@ import ProjectRepository from "../../repositories/ProjectRepository";
import { cacheData,getCachedData } from "../../slices/apiDataManager";
import {hasUserPermission} from "../../utils/authUtils";
import moment from "moment";
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
import {MANAGE_PROJECT} from "../../utils/constants";
const ProjectBanner = ( {project_data} ) =>
{
const [showModal, setShowModal] = useState(false);
const manageProject = useHasUserPermission(MANAGE_PROJECT)
const [ CurrentProject, setCurrentProject ] = useState( project_data )
if (project_data == null) {
return <span>incomplete project information</span>;
@ -118,7 +120,7 @@ const ProjectBanner = ( {project_data} ) =>
</div>
<button
type="button"
className={`btn btn-sm btn-primary ${hasUserPermission("53176ebf-c75d-42e5-839f-4508ffac3def") ? "":"d-none"}`}
className={`btn btn-sm btn-primary ${!manageProject && 'd-none'}`}
data-bs-toggle="modal"
data-bs-target="#edit-project-modal"
onClick={handleShow}

View File

@ -223,6 +223,7 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
const closeBuildingModel = () => {
setIsBuildingModalOpen(false);
};
const handleBuildingModelFormSubmit = (buildingmodel) => {
if (buildingmodel.id == "" || buildingmodel.id == 0)
delete buildingmodel.id;
@ -337,7 +338,6 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
return (
<>
<div
className={`modal fade ${showModal ? 'show' : ''}`}
tabIndex="-1"
@ -347,14 +347,12 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
>
<BuildingModel
project={data}
onClose={closeBuildingModel}
onClose={handleClose}
onSubmit={handleBuildingModelFormSubmit}
clearTrigger={clearFormTrigger}
onClearComplete={() => setClearFormTrigger(false)}
></BuildingModel>
</div>
{isFloorModalOpen && (
<div
className={`modal fade `}

View File

@ -0,0 +1,77 @@
import React, { useEffect, useRef } from 'react';
const GlobalModel = ({
isOpen,
closeModal,
children,
modalType = '', // For custom positioning like modal-top, modal-bottom
dialogClass = '', // For additional custom classes on modal dialog
role = 'dialog', // Accessibility role for the modal
size = '', // Dynamically set the size (sm, lg, xl)
dataAttributes = {} // Additional dynamic data-bs-* attributes
}) => {
const modalRef = useRef(null); // Reference to the modal element
useEffect(() => {
const modalElement = modalRef.current;
const modalInstance = new window.bootstrap.Modal(modalElement);
// Show modal if isOpen is true
if (isOpen) {
modalInstance.show();
} else {
modalInstance.hide();
}
// Handle modal hide event to invoke the closeModal function
const handleHideModal = () => {
closeModal(); // Close the modal via React state
};
modalElement.addEventListener('hidden.bs.modal', handleHideModal);
return () => {
modalElement.removeEventListener('hidden.bs.modal', handleHideModal);
};
}, [isOpen, closeModal]);
// Dynamically set the modal size classes (modal-sm, modal-lg, modal-xl)
const modalSizeClass = size ? `modal-${size}` : ''; // Default is empty if no size is specified
// Dynamically generate data-bs attributes
const dataAttributesProps = Object.keys(dataAttributes).map(key => ({
[key]: dataAttributes[key],
}));
return (
<div
className={`modal fade ${modalType}`}
id="customModal"
tabIndex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
ref={modalRef} // Assign the ref to the modal element
{...dataAttributesProps}
>
<div className={`modal-dialog ${dialogClass} ${modalSizeClass}`} role={role}>
<div className="modal-content">
<div className="modal-header">
{/* Close button inside the modal header */}
<button
type="button"
className="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
onClick={closeModal} // Trigger the React closeModal function
></button>
</div>
<div className="modal-body">
{children} {/* Render children here, which can be the ReportTask component */}
</div>
</div>
</div>
</div>
);
};
export default GlobalModel;

40
src/hooks/useTasks.js Normal file
View File

@ -0,0 +1,40 @@
import { useEffect, useState } from "react";
import { TasksRepository } from "../repositories/TaskRepository";
import { cacheData, getCachedData } from "../slices/apiDataManager";
export const useTaskList = (projectId, fromDate, ToDate) => {
const [TaskList, setTaskList] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchList = async () => {
const taskList_cached = getCachedData("taskList");
if (!taskList_cached || taskList_cached?.projectId !== projectId) {
try {
setLoading(true);
const resp = await TasksRepository.getTaskList(
projectId,
fromDate,
ToDate
);
setTaskList(resp.data);
cacheData("taskList", { projectId: projectId, data: resp.data });
setLoading(false);
} catch (err) {
setLoading(false);
console.log(err);
setError(err);
}
} else {
setTaskList(taskList_cached.data);
}
};
console.log(TaskList)
useEffect(() => {
if (projectId) {
fetchList();
}
}, [projectId, fromDate, ToDate]);
return { TaskList, loading, error, refetch:fetchList};
};

View File

@ -1,10 +1,81 @@
import { useDispatch, useSelector } from "react-redux";
import Breadcrumb from "../../components/common/Breadcrumb";
import { dailyTask } from "../../data/masters";
import { useTaskList } from "../../hooks/useTasks";
import { useProjects } from "../../hooks/useProjects";
import { setProjectId } from "../../slices/localVariablesSlice";
import { useProfile } from "../../hooks/useProfile";
import React, { useEffect, useState } from "react";
import {formatDate} from "../../utils/dateUtils";
import GlobalModel from "../../components/common/GlobalModel";
import AssignRoleModel from "../../components/Project/AssignRole";
import {ReportTask} from "../../components/Activities/ReportTask";
import ReportTaskComments from "../../components/Activities/ReportTaskComments";
import Breadcrumb from "../../components/common/Breadcrumb"
import { dailyTask } from "../../data/masters"
const DailyTask =()=>{
return (
<>
<div className="container-xxl flex-grow-1 container-p-y">
const DailyTask = () => {
const { profile: LoggedUser } = useProfile();
const {
projects,
loading: project_lodaing,
error: projects_Error,
} = useProjects();
const selectedProject = useSelector(
(store) => store.localVariables.projectId
);
const dispatch = useDispatch( selectedProject );
const {
TaskList,
loading: task_loading,
error: task_error,
refetch
} = useTaskList(selectedProject);
const [TaskLists, setTaskLists] = useState([]);
useEffect(() => {
setTaskLists(TaskList);
}, [ TaskList, selectedProject ] );
const [ selectedTask, selectTask ] = useState( null )
const [comments,setComment] = useState(null)
const [ isModalOpen, setIsModalOpen ] = useState( false );
const [ isModalOpenComment, setIsModalOpenComment ] = useState( false )
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen( false );
const openComment = () =>setIsModalOpenComment(true)
const closeCommentModal =()=>setIsModalOpenComment(false)
const handletask = (task) =>
{
selectTask(task)
openModal()
}
return (
<>
<div
className={`modal fade ${isModalOpen ? 'show' : ''}`}
tabIndex="-1"
role="dialog"
style={{ display: isModalOpen ? 'block' : 'none' }}
aria-hidden={!isModalOpen}
>
<ReportTask report={selectedTask} closeModal={ closeModal} refetch={refetch} />
</div>
<div
className={`modal fade ${isModalOpenComment ? 'show' : ''}`}
tabIndex="-1"
role="dialog"
style={{ display: isModalOpenComment ? 'block' : 'none' }}
aria-hidden={!isModalOpenComment}
>
<ReportTaskComments commentsData={comments} closeModal={ closeCommentModal} />
</div>
<div className="container-xxl flex-grow-1 container-p-y">
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
@ -14,61 +85,153 @@ const DailyTask =()=>{
<div className="card card-action mb-6">
<div className="card-body">
<div className="row">
<div className="col-sm-3 col-8 text-start mb-1">
<select
name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0"
className="form-select form-select-sm"
value={selectedProject}
onChange={(e) => dispatch(setProjectId(e.target.value))}
aria-label=""
>
{(project_lodaing || projects.length < 0) && (
<option value="Loading..." disabled>
Loading...
</option>
)}
{!project_lodaing &&
projects
?.filter((project) =>
LoggedUser?.projects?.map(Number).includes(project.id)
)
.map((project) => (
<option value={project.id} key={project.id}>
{project.name}
</option>
))}
</select>
</div>
</div>
<div className="table-responsive text-nowrap">
{/* {employees && employees.length > 0 ? ( */}
<table className="table ">
<thead>
<table className="table">
<thead>
<tr>
<th></th>
<th>Activity</th>
<th>Planned </th>
<th>Compeleted</th>
<th>Assign On</th>
<th>Team</th>
<th>Actions</th>
</tr>
</thead>
<tbody className="table-border-bottom-0">
{TaskLists?.length === 0 && !task_loading && (
<tr>
<th>Sr</th>
<th>Project Name</th>
<th>Target</th>
<th>Employees</th>
<th>Actions</th>
<td colSpan={7} className="text-center">
No Data Found
</td>
</tr>
</thead>
<tbody className="table-border-bottom-0">
)}
{task_loading && (
<tr>
<td colSpan={7} className="text-center">
<p>Loading..</p>
</td>
</tr>
)}
{TaskLists.map((task, index) => {
const accordionId = `accordion-${index}`;
return (
<React.Fragment key={index}>
{/* Main Row */}
<tr >
<td>
<div className="d-flex justify-content-center align-items-center">
<div className="d-flex justify-content-center align-items-center" data-bs-toggle="collapse"
data-bs-target={`#${accordionId}`}
aria-expanded="false"
aria-controls={accordionId}
>
<div className="d-flex flex-column">
<a href="#" className="text-heading text-truncate">
<span className="fw-medium ">
1
</span>
<a
href="#"
className="text-heading text-truncate"
>
<i class='bx bx-chevron-right'></i>
</a>
</div>
</div>
</td>
<td className="flex-wrap">
project Name project Name project Name
{task.workItem.activityMaster.activityName || "No Activity Name"}
</td>
<td>{task.plannedTask || "NA"}</td>
<td>{task.completedTask}</td>
<td>{formatDate( task.assignmentDate )}</td>
<td className="text-center">
<div class="d-flex align-items-center avatar-group justify-content-center">
{task.teamMembers.map( ( member ) => (
<td>
80
<div key={member.id} data-bs-toggle="tooltip" data-bs-html="true" data-popup="tooltip-custom" data-bs-placement="top" title={`${member.firstName} ${member.lastName}`} className="avatar avatar-xs">
{/* <img src="..." alt="Avatar" class="rounded-circle pull-up" /> */}
<span className="avatar-initial rounded-circle bg-label-primary">{member?.firstName.slice(0,1) }</span>
</div>
))}
</div>
</td>
<td> NA</td>
<td>
<button type="button" className="btn btn-xs btn-primary">Report</button>
<td className="text-center">
<div className="d-flex justify-content-center">
<button
type="button"
className="btn btn-xs btn-primary"
onClick={() =>
{
selectTask( task )
openModal()
}}
>
Report
</button>
<button
type="button"
className="btn btn-xs btn-primary ms-2"
onClick={() =>
{
setComment( task )
openComment()
}}
>
Comment
</button>
</div>
</td>
</tr>
{/* ))} */}
</tbody>
</table>
{/* Accordion Content */}
<tr
id={accordionId}
className="accordion-collapse collapse"
>
<td colSpan={5}>
<div className="row">
<p>{task.subdata?.name}</p>
</div>
</td>
</tr>
</React.Fragment>
);
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
</>
)
}
export default DailyTask
</>
);
};
export default DailyTask;

View File

@ -17,7 +17,8 @@ const loginScheam = z.object( {
const LoginPage = () => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [ loading, setLoading ] = useState( false );
const[hidepass,setHidepass] = useState(true)
const {register,
handleSubmit,
@ -93,7 +94,7 @@ const LoginPage = () => {
</div>
<div className="input-group input-group-merge">
<input
type="password"
type={hidepass ? "password" :"text"}
autoComplete="true"
id="password"
{...register("password")}
@ -102,7 +103,7 @@ const LoginPage = () => {
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
aria-describedby="password"
/>
<span className="input-group-text cursor-pointer"></span>
<span class="input-group-text cursor-pointer" onClick={()=>setHidepass(!hidepass)}>{ hidepass ? <i className="bx bx-hide"></i> :<i className="bx bx-show"></i>}</span>
</div>
{errors.password && (
<div

View File

@ -0,0 +1,7 @@
import {api} from "../utils/axiosClient";
export const TasksRepository = {
getTaskList: ( id, fromdate = null, todate = null ) => api.get( `api/task/list?projectId=${ id }` ),
reportTsak: ( data ) => api.post( 'api/task/report', data ),
taskComments:(data)=>api.post("api/task/comment",data)
}