Compare commits

...

414 Commits

Author SHA1 Message Date
96bcdffdca added optional chain for n=menu 2025-12-05 18:45:19 +05:30
f8963ef476 Merge pull request 'At Assigned Employee when selecting a Job role it change according to master now show correct data.' (#520) from Assigned_Employee_Job_Role into main
Reviewed-on: #520
merged
2025-11-26 12:20:28 +00:00
9ad3b8726c At Assigned Employee when selecting a Job role it change according to master now show correct data. 2025-11-25 18:09:32 +05:30
d4582c101a Merge pull request 'Project_Branch_Management : New Feature Service Project - Branch Management' (#519) from Project_Branch_Management into main
Reviewed-on: #519
merged
2025-11-25 09:42:47 +00:00
c6af020c85 changed fs-6 and body-font-size 2025-11-25 15:09:25 +05:30
822ff1a7e4 UI Alignment in Service Card view. 2025-11-25 11:20:19 +05:30
92d17167b1 added zoom in-out 2025-11-25 10:11:43 +05:30
8ec62827d5 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-24 17:58:26 +05:30
965fce1587 Merge pull request 'Service_Project_ListView :- Creating List view for service Project.' (#518) from Service_Project_ListView into Project_Branch_Management
Reviewed-on: #518
Merged
2025-11-24 11:16:57 +00:00
bb37fd7044 set align for Service Project List card 2025-11-24 11:16:57 +00:00
03d60bf66d Adding search functionality in Projectpage. 2025-11-24 11:16:57 +00:00
071e5956a7 Changes. 2025-11-24 11:16:57 +00:00
c669eb90c3 Correction in List view alignment. 2025-11-24 11:16:57 +00:00
4d2e37f52e Adding Message for Card and list view in Services and Infra 2025-11-24 11:16:57 +00:00
6dde240926 Creating a list view for Services. 2025-11-24 11:16:57 +00:00
482b5a1680 Adding lIst view. 2025-11-24 11:16:57 +00:00
098090cf69 Merge pull request 'Status Dropdown Should Auto-Close After Job Status Update' (#516) from Kartik_Bug#1773 into Project_Branch_Management
Reviewed-on: #516
mergd
2025-11-24 11:05:51 +00:00
5747d4ec71 Status Dropdown Should Auto-Close After Job Status Update 2025-11-24 10:35:03 +05:30
48f314eac4 added signalR for project Branch 2025-11-21 15:28:31 +05:30
18e739f3ab fixed collection header ui 2025-11-21 14:51:31 +05:30
a5329f1a2a added new selectproject field inside ManageRecurring 2025-11-21 14:25:32 +05:30
8b4a9d2d1c added new project filed inside ManagePaymentRequest 2025-11-21 14:08:52 +05:30
4506f740eb Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-21 13:13:50 +05:30
f001dff5b0 Creating new icon for Un-archive 2025-11-21 13:09:14 +05:30
33d94f6f06 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-21 12:58:52 +05:30
0ac1141579 changed filed for project and employee now not need project for select employee 2025-11-21 12:58:47 +05:30
4d8af5da91 added pre-fill updating 2025-11-21 12:57:54 +05:30
fead3a37a6 Adding Pending in Loading in Job list. 2025-11-21 12:31:10 +05:30
98e9a8b625 When we in Archive tab then canvas will be open now it work correctly. 2025-11-21 12:21:22 +05:30
2781da8906 Change the logic according to archieve show by status. 2025-11-21 12:04:36 +05:30
d13c48dd39 Change the Ui for Archieve jobs. 2025-11-21 11:47:55 +05:30
ee4ef18594 Implementing Restore Functionality in Jobs. 2025-11-21 11:39:36 +05:30
742337a3d0 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-21 11:12:03 +05:30
5abab06b0c removed console error 2025-11-21 11:11:44 +05:30
17a9f4a9b1 Implementing Project change 2025-11-21 11:10:55 +05:30
de088fcfc2 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-21 09:46:32 +05:30
8e12eb6797 Adding Switch button on Jobs. 2025-11-21 09:45:28 +05:30
14c1da7888 added restore 2025-11-20 22:01:25 +05:30
753d8c4a2c Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-20 17:09:09 +05:30
766d19744c added suggestion for branch Type inside service Projects 2025-11-20 17:09:04 +05:30
caaadc4c08 Implementing Validation when we status will be done or closed then Archieve button will be shown. 2025-11-20 16:38:25 +05:30
1531fbe6f2 Implementing Archieve functionality in Jobs. 2025-11-20 16:16:20 +05:30
9f5a167613 added copy button for localtion url 2025-11-20 14:49:54 +05:30
fa923d4c3a added popup for branch view 2025-11-20 14:39:54 +05:30
bd43475d12 Merge pull request 'Advance_Payment_List :- Implementing advance Payment new API.' (#514) from Advance_Payment_List into Project_Branch_Management
Reviewed-on: #514
merged
2025-11-20 09:08:07 +00:00
521d46bdee Merge pull request 'Adding Billed To field in Manage Collection.' (#513) from Collection_Service_Project into Project_Branch_Management
Reviewed-on: #513
merged
2025-11-20 07:23:25 +00:00
604bb68dc2 Adding Billed To field in Manage Collection. 2025-11-20 07:23:25 +00:00
047e563505 added mutiple contact person inside branch 2025-11-20 12:49:56 +05:30
e4f053ee65 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Advance_Payment_List 2025-11-20 12:41:39 +05:30
7105a3640f Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Advance_Payment_List 2025-11-20 12:36:40 +05:30
d167c57ab0 Implementing the Update api for branches. 2025-11-20 12:34:45 +05:30
d3c006279c Increasing the size of Service details page. 2025-11-20 10:29:42 +05:30
195a0c83bb Correction in api for details. 2025-11-20 10:00:06 +05:30
8f86b05d35 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-20 09:51:29 +05:30
dd716187da Implementing edit api for branches. 2025-11-20 09:51:23 +05:30
5b86a2f64f removed console error 2025-11-20 09:50:49 +05:30
e99d49b83e added view comments 2025-11-20 09:26:02 +05:30
0210e17170 added branch details 2025-11-19 22:58:41 +05:30
e7fddd41c2 added branch for projects 2025-11-19 21:28:01 +05:30
7e4dffff34 Implementing Get api. 2025-11-19 18:47:30 +05:30
e7a68aeab7 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-19 18:32:03 +05:30
8a5d6158c2 Implementing Creating API. 2025-11-19 18:31:17 +05:30
50269aead2 added selectFiled for branch 2025-11-19 18:28:54 +05:30
0ede07b0d5 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-19 16:36:37 +05:30
df24b18a27 At the time of hit in cancel form will be closed. 2025-11-19 16:36:32 +05:30
f5d89f2bab Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-19 16:36:01 +05:30
15978b2ac7 fixed useClient implmention 2025-11-19 16:35:53 +05:30
2dbf5dc109 Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management 2025-11-19 16:31:12 +05:30
7cc8cc99c2 Creating a list view and modal for Branches. 2025-11-19 16:30:51 +05:30
c04ee8dab5 configure branch related api inside service repo. and made hooks for branch 2025-11-19 16:28:37 +05:30
c52f85ee4a added Advance payment flag inside paymentRequest list and view details 2025-11-19 15:44:54 +05:30
c718269dfd added spinner in service profile page 2025-11-19 15:35:39 +05:30
268bd2875f Changing the position of Status in Collection list view. 2025-11-19 14:26:12 +05:30
7773b7a43b Adding Status and UI implementation in Collection. 2025-11-19 12:59:57 +05:30
25003d912e add green color class for onfieldwork.com 2025-11-19 12:35:13 +05:30
a3be63b74f Implementing api for Advance payment all data. 2025-11-19 12:22:38 +05:30
54420c70d9 change landing page logo to marco from ON 2025-11-19 12:15:08 +05:30
4b5f8756b3 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Advance_Payment_List 2025-11-19 11:27:51 +05:30
10e54637d5 Adding list view in Advance payment list. 2025-11-19 10:47:11 +05:30
3fe533c8e3 addd serach poject field both projects 2025-11-19 10:42:12 +05:30
5b91c13b85 adding list view in advance payment list. 2025-11-19 09:46:41 +05:30
ed87e3f11b removed conole error 2025-11-18 18:28:38 +05:30
e276836653 added prevent to take action after close ticket 2025-11-18 18:13:49 +05:30
523d6baea6 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-18 17:56:21 +05:30
9e47f10440 updated padding of multi- select tag 2025-11-18 17:56:17 +05:30
54d89e1429 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Advance_Payment_List 2025-11-18 17:38:30 +05:30
018f132501 Adding Navigate functionality in ServiceprjectTeams. 2025-11-18 17:31:51 +05:30
f3eed11540 Merge pull request 'Service_Management_CardView :- Creating a card for Services project.' (#512) from Service_Management_CardView into Service_Project_Managment
Reviewed-on: #512
merged
2025-11-18 11:56:35 +00:00
836b34c237 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Management_CardView 2025-11-18 17:24:04 +05:30
bf92ffcb3e added job status during update job 2025-11-18 17:22:52 +05:30
dd380f6820 Edit button add in About project for Services. 2025-11-18 17:20:53 +05:30
f997c2f2cc Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Management_CardView 2025-11-18 17:01:22 +05:30
1c5d62f5ed Correction in HoverPopup when it we select single the all will be open. 2025-11-18 16:57:48 +05:30
37dbbf53f3 fixed table align 2025-11-18 16:56:11 +05:30
2cb9b41f98 Increasing the size of datepicker at TaskReport filter panel. 2025-11-18 16:38:57 +05:30
e6e27beb6f Creating Card for ServiceProject. 2025-11-18 16:35:05 +05:30
9822ae91ec Adding AdvancePayment a list view. 2025-11-18 15:56:16 +05:30
3753d01f7d prevent to change status once job status is done 2025-11-18 14:25:13 +05:30
c8a599fc8f Adding Extra fields in Service Project Card. 2025-11-18 12:47:39 +05:30
1c2674d435 Filteration using status in Services and Infra. 2025-11-18 12:24:41 +05:30
31d340c645 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Management_CardView 2025-11-18 12:15:25 +05:30
61e210c305 Filtering according to Dropdown filter. 2025-11-18 12:14:35 +05:30
dd41600045 modify Job view Ui 2025-11-18 11:58:57 +05:30
72052a4708 Creating a ServiceCard view. 2025-11-18 11:50:29 +05:30
4dfc0fa7ca Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-18 11:08:44 +05:30
3cfac9a2bb fixed - useProject (infra) params 2025-11-18 11:08:33 +05:30
b14353dd4f Adding Organization form at Create Service. 2025-11-18 10:10:44 +05:30
8de1230f79 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-17 20:26:33 +05:30
9b65e0a320 added update job 2025-11-17 20:26:28 +05:30
70ed617b9b Adding Payment_processed at the place of payment_Processed. 2025-11-17 19:21:40 +05:30
d4bb7373f2 Adding Plus icon in Create Services. 2025-11-17 19:12:34 +05:30
f4f14127b7 Merge pull request 'We are creating filter chips in the Payment Request filter panel.' (#508) from Katik_Task#1746 into Service_Project_Managment
Reviewed-on: #508
2025-11-17 13:12:58 +00:00
c8bbf6b8a8 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Katik_Task#1746 2025-11-17 18:40:18 +05:30
36be17daa7 Merge pull request 'Hide “Advance Payment” Mode When Creating Expense After Payment Processed (Mobile & Web)' (#509) from Kartik_Bug#1735 into Service_Project_Managment
Reviewed-on: #509
2025-11-17 13:06:01 +00:00
d6b2ed9f84 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-17 18:35:49 +05:30
088e5e0368 UI Implementation at Job List 2025-11-17 18:35:42 +05:30
977e5c77a7 Merge pull request 'Invoice list should display according to the selected date range in Collection module.' (#510) from Kartik_Bug#1505 into Service_Project_Managment
Reviewed-on: #510
2025-11-17 13:03:50 +00:00
2b1d3e5890 Merge branch 'Service_Project_Managment' into Kartik_Bug#1505 2025-11-17 13:03:42 +00:00
a61e621ce2 Merge pull request 'Kartik_Bug#1737 :- UI Implementation in Expense Weidget.' (#507) from Kartik_Bug#1737 into Service_Project_Managment
Reviewed-on: #507
2025-11-17 13:01:33 +00:00
0bd1cea703 1) Handle blank space in menu bar
2) Breadcrum  "Home" link to "Dashboard" on pages where it was missing
2025-11-17 18:30:13 +05:30
d6ef713370 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-17 18:28:20 +05:30
da391368a6 setup job data for update 2025-11-17 18:28:15 +05:30
6860ba3dff Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-17 18:25:48 +05:30
849567f999 UI changes in View Jobs. 2025-11-17 18:25:42 +05:30
507bc68da9 Added the server timeout for signalR 2025-11-17 18:12:35 +05:30
767475c7e5 Adding Create Service Project and Create Infra project project at ManageProject. 2025-11-17 17:53:01 +05:30
5f649cd917 Added Job Id in Jobs. 2025-11-17 17:50:41 +05:30
ea6686560d added missing job id 2025-11-17 17:46:13 +05:30
1358cd749a fixed error during came merged 2025-11-17 17:09:35 +05:30
d95ba4c800 added changes status fun 2025-11-17 17:06:05 +05:30
78eafd2e5a Showing ProjectName at the breadcrum in Services Project. 2025-11-17 16:15:36 +05:30
6598ec9f73 Adding placeholder and adding in TagInput a background color. 2025-11-17 15:44:03 +05:30
679ae95b8c Removing project selection at Job Creation. 2025-11-17 15:21:31 +05:30
4f03cbb305 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-17 13:34:09 +05:30
d342550b0f added refresh Manage Job form whenever closing after editing and creating job 2025-11-17 13:34:04 +05:30
4f7ad519cf Change label in Advance Payment in Total balance to currenct balance and hide project selection dropdown in PR and Advance. 2025-11-17 13:11:22 +05:30
c311324bad Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-17 12:45:01 +05:30
937531afb6 updated PmsEmployeeInputTag for getting all exiten employee for update 2025-11-17 12:44:56 +05:30
b68f8306fd added confirmation modal inside serviceproject employee list 2025-11-17 12:44:01 +05:30
7470a6836f UI alignment at View Managejob Ticket in jobs. 2025-11-17 12:33:19 +05:30
12184091cd UI alignment at view Jobs. 2025-11-17 11:29:44 +05:30
8761c128e6 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-17 10:57:51 +05:30
8eb34addcd added serviceproject list 2025-11-17 10:57:48 +05:30
b568188a3e Adding TDS Calculation in ViewExpense. 2025-11-17 10:31:41 +05:30
a21bec943d Added GST number in Organization. 2025-11-17 10:05:02 +05:30
3d1d8264ad Merge pull request 'Service_Project_Management_Bugs :- Changes for Jobs' (#511) from Service_Project_Management_Bugs into Service_Project_Managment
Reviewed-on: #511
Merged
2025-11-17 04:16:04 +00:00
d831d08aaf Adding validation in Contact number and adding trim functioanlity. 2025-11-17 04:16:04 +00:00
8553b6bee7 At New Project popup show the Active selected all the time when popup open. 2025-11-17 04:16:04 +00:00
e47262123d Added columns in jobs list and implementing functionality for daysleft. 2025-11-17 04:16:04 +00:00
852ebd7dd0 Correction in UI in ProjectPage. 2025-11-17 04:16:04 +00:00
fc1235caef added allocation and deallocation of service Project team 2025-11-17 00:42:50 +05:30
7e646ca3e8 add new EmployeeSearch for multiple 2025-11-14 20:07:28 +05:30
92bcfb73c6 Merge branch 'migrate_main' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-14 16:47:40 +05:30
770123119c Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-14 16:39:57 +05:30
7190a3c190 added offcanavs for create and update 2025-11-14 16:39:46 +05:30
b399977946 Invoice list should display according to the selected date range in Collection module. 2025-11-14 15:39:40 +05:30
314dd67118 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-14 15:12:23 +05:30
74e3b11174 Adding some space in Payment Request list. 2025-11-14 15:12:07 +05:30
dab3c5fe52 fixed attachement error 2025-11-14 14:58:50 +05:30
d1bcc9981c Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-14 13:04:29 +05:30
86819dde03 added comment, attchment 2025-11-14 13:04:15 +05:30
64d96d46d0 Correction in UI in Dashboard weidget. 2025-11-14 12:58:47 +05:30
05f45a66d8 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Kartik_Bug#1737 2025-11-14 11:59:06 +05:30
0b38196847 Hide “Advance Payment” Mode When Creating Expense After Payment Processed (Mobile & Web) 2025-11-14 11:52:12 +05:30
74e645a41e Increase the size of datepicker at gallery filter panel and remove unwanted PaidBy field in ViewPayment Request. 2025-11-14 11:23:10 +05:30
598601c515 We are creating filter chips in the Payment Request filter panel. 2025-11-14 10:22:27 +05:30
297e0712bc integrated job details api and optimized form 2025-11-14 02:51:29 +05:30
9b7988d6d8 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-13 19:50:41 +05:30
844a6f59d1 added fetch api of jobs 2025-11-13 19:50:19 +05:30
c1be1dafc8 Changes in Payment Request List view adding Due Date, Created at and Payee. 2025-11-13 17:45:05 +05:30
14ed888fe8 UI implementation has been done in Expense 2025-11-13 17:22:54 +05:30
b13034a995 Expense widget - Layout change 2025-11-13 15:14:07 +05:30
c4b589460a Correction in EmpReportingManger. 2025-11-13 12:33:50 +05:30
32b7c53b6c Merge branch 'migrate_main' of https://git.marcoaiot.com/admin/marco.pms.web into migrate_main 2025-11-13 12:31:47 +05:30
c0378e1d7d revert body background color change. It was turned white after making changes for Landing Page. Ideallly it had to be gray 2025-11-13 12:31:43 +05:30
8453a09426 Merge branch 'Service_Project_Managment' of https://git.marcoaiot.com/admin/marco.pms.web into Service_Project_Managment 2025-11-13 11:34:34 +05:30
60421d1028 Adding Navigate functionality in ManageReporting. 2025-11-13 11:34:05 +05:30
2746e9d2bb added Job tab 2025-11-13 11:00:51 +05:30
651779adea Merge branch 'Organization_Hierarchy' into Service_Project_Managment 2025-11-13 10:20:48 +05:30
910565e003 Merge branch 'Organization_Hierarchy' of https://git.marcoaiot.com/admin/marco.pms.web into Organization_Hierarchy 2025-11-13 10:07:48 +05:30
a40f96fe72 removed unneeded border and replace toggle for activtive - inactie 2025-11-13 10:07:40 +05:30
cd467efcb3 Correction in EmpReporting Manger. 2025-11-13 10:05:41 +05:30
559d6cdaac Adding Employee details at mange Reporting popup. 2025-11-13 09:54:54 +05:30
3500adc1eb Change the icon in Employee List manage Reporting button. 2025-11-13 09:45:41 +05:30
d7f00898a0 added delete service project 2025-11-12 19:38:04 +05:30
1bba9ab68a added created, fetch and edit operration 2025-11-12 18:12:37 +05:30
5c1b14bd38 added create service project and separated service project and orgnaization project 2025-11-12 14:18:43 +05:30
6019114853 Corrected the spelling mistake in landing page file 2025-11-12 12:41:21 +05:30
fa944f5abe Removing unwanted placeholder. 2025-11-12 12:14:09 +05:30
4351fafed7 Adding Card in Profile related to Reporting Manger. 2025-11-11 18:59:58 +05:30
15e67da7f6 modify hero images for landing page 2025-11-11 18:47:00 +05:30
94413b9beb Implementing ManageReporting at Employee. 2025-11-11 18:00:34 +05:30
8900ed6fc4 Merge branch 'landing-page-consmatic-changes-migration' into migrate_main 2025-11-11 17:34:50 +05:30
04a3141f65 remove swiper from login main and added bootstrap corausal 2025-11-11 17:34:25 +05:30
264ed4a74d added pluse minus sign 2025-11-11 16:50:17 +05:30
1067ac02b3 arrange images for login screen swiper module 2025-11-11 16:05:09 +05:30
96abc8d42c Additing validation at payment Request schema. 2025-11-11 14:40:31 +05:30
ba0036b1fa Adding Badge. 2025-11-11 14:14:02 +05:30
c52fb2c999 Adding TDS percentage calculation at View Payment Request. 2025-11-11 14:11:35 +05:30
867ee92151 Adding popup in employees for ManageReporting. 2025-11-11 11:11:13 +05:30
1195cae2d7 Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Organization_Hierarchy 2025-11-11 09:45:48 +05:30
4eecaf0509 Merge branch 'migrate_main' of https://git.marcoaiot.com/admin/marco.pms.web into migrate_main 2025-11-10 18:54:01 +05:30
d82f405e08 changed Expense view u 2025-11-10 18:53:47 +05:30
a37c6b32cb Changing label in Create Payment Request. 2025-11-10 18:31:13 +05:30
9e18dda02f Adding End Date at the view Recurring Expense. 2025-11-10 17:08:23 +05:30
b016f4a523 UI updation at recurring expense. 2025-11-10 17:04:28 +05:30
081b042308 corrected signal key 2025-11-10 16:27:26 +05:30
ca4d57cdc0 Merge branch 'migrate_main' of https://git.marcoaiot.com/admin/marco.pms.web into migrate_main 2025-11-10 16:10:45 +05:30
ec9bcd1ea5 added validation for Tax , base amount, and added signal for PR and Recurring 2025-11-10 16:10:18 +05:30
00e74b645c Ui alignment in Recurring Expense and Payment Request page. 2025-11-10 16:00:28 +05:30
26675d61c3 UI correction at Collection form. 2025-11-10 15:36:17 +05:30
78faab1b1e Merge branch 'migrate_main' of https://git.marcoaiot.com/admin/marco.pms.web into migrate_main 2025-11-10 12:58:34 +05:30
6982bb9d77 added heading for PR and Expense Modal 2025-11-10 12:58:30 +05:30
2e49a61aab Changing Date in Expense list grouping. 2025-11-10 12:44:13 +05:30
3723f05a38 Merge branch 'migrate_main' of https://git.marcoaiot.com/admin/marco.pms.web into migrate_main 2025-11-10 12:40:33 +05:30
bc6a935e95 modify view of PR 2025-11-10 12:40:29 +05:30
717d215d8e handle UI to look sliker and add missing images 2025-11-10 12:30:16 +05:30
6531eb7576 Merge branch 'migrate_main' of https://git.marcoaiot.com/admin/marco.pms.web into migrate_main 2025-11-10 11:50:00 +05:30
c52cbb1435 Adding End Date in Recurring Expense Popup and adding i icon on it. 2025-11-10 11:49:56 +05:30
b86bf204a9 add landing page 2025-11-10 11:36:32 +05:30
30615e4bba login page swaper added 2025-11-10 11:28:51 +05:30
dad9551e52 add images
(cherry picked from commit 34b0fa3d43d45e67098ce768f30a00fca0a72925)
2025-11-10 11:24:58 +05:30
449de6edba added new attribute - disabled 2025-11-10 10:15:13 +05:30
34b0fa3d43 add images 2025-11-10 10:06:09 +05:30
c14dc3d765 prevent calling unnecessary call during logout and fixed currency displaying in update form 2025-11-08 21:32:35 +05:30
e7fcfdb154 added Advance Payment 2025-11-08 16:07:09 +05:30
b35c39ca36 removed unsused 2025-11-08 15:42:00 +05:30
2b5858a885 removed unused code 2025-11-08 15:38:51 +05:30
04806c6d53 fixed error - building image 2025-11-08 15:34:59 +05:30
0dac797e21 upadated recurringList after create 2025-11-08 15:22:24 +05:30
5a7c6a29ba added recurring payment 2025-11-08 15:20:53 +05:30
40cb641428 added Create Expense action - inSequence of PaymentRequest 2025-11-08 14:29:17 +05:30
587fb420fd migrated PR from upgrade_Expense 2025-11-08 12:53:24 +05:30
2f70b83548 added route and initially setup of payment-request 2025-11-08 09:24:45 +05:30
1058b223b0 removed console and debugger 2025-11-08 09:13:11 +05:30
6f228ed5f1 added currency,rename of ExpenseType to expenseCategory - hooks,( expenseCategoryIds = fileter object) 2025-11-08 09:10:35 +05:30
6a97dcf5f6 added initially expense 2025-11-07 18:15:45 +05:30
dcbb4a3997 fixed DatePicker size issue 2025-11-07 16:47:51 +05:30
7b2bb4d59a Merge branch 'Issues_Oct_4W_V1' into migrate_main 2025-11-07 15:17:58 +05:30
f6ea489a0e removed unwanted code 2025-11-07 15:15:24 +05:30
3259a1ba81 Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Payment_Getway_Management 2025-11-07 14:56:27 +05:30
57850acd63 Merge pull request 'Progress Bar Misalignment in Project Widgets on Dashboard' (#497) from Kartik_Bug_V1#1539 into Issues_Oct_4W_V1
Reviewed-on: #497
Merged
2025-11-07 09:22:44 +00:00
3204602b25 Progress Bar Misalignment in Project Widgets on Dashboard 2025-11-07 09:22:44 +00:00
034859e88b Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Issues_Oct_4W_V1 2025-11-07 14:51:05 +05:30
a656907695 migrated suscripton onfield to main sub-branch 2025-11-07 14:47:09 +05:30
5f2a567600 Document Filter should close automatically when navigating to another menu from Project Profile page 2025-11-07 12:51:28 +05:30
9ef1e39041 Changes in Directory card and Notes view at the UI. 2025-11-07 12:34:57 +05:30
c3d1b46861 Increasing some gap in document view button and uploaded by and approved at. 2025-11-07 11:43:22 +05:30
2596739fc5 Giving some extra gap in document view form. 2025-11-07 11:36:27 +05:30
c216ea1162 some minor label changes 2025-11-03 12:32:34 +05:30
c7fe28a4ed change application title from all places from PMS to OnFieldWork 2025-11-03 12:07:07 +05:30
7b1ed97452 UI changes in Expense Weidget and Attendance weidget. 2025-10-31 16:42:07 +05:30
72eb64177f Correction in Attendance Overview. 2025-10-31 15:50:33 +05:30
466b09df88 Correction in Tenant Schema at onboarding date. 2025-10-31 15:39:27 +05:30
8522f0ca82 Correction in Date format in Attendance logs, and adding spinnerloade at attendance tab. 2025-10-31 15:37:26 +05:30
166c0794ed Image filter is not auto close when we move to next tab. 2025-10-31 15:15:36 +05:30
1c64fac499 Adding Spinners in all files. 2025-10-31 15:06:11 +05:30
4a08394015 Merge pull request 'Adding Spinner in Dashbard.' (#496) from Kartik_Bug_V1#1543 into Issues_Oct_4W_V1
Reviewed-on: #496
Merged
2025-10-31 05:28:48 +00:00
86f931929a Adding Spinner in Dashbard. 2025-10-31 10:57:56 +05:30
9fbe3cf6a5 Merge pull request 'Alignment Correction for Organization & Employee Filters in Attendance → Regularization Tab' (#495) from Kartik_Bug_V1#1569 into Issues_Oct_4W_V1
Reviewed-on: #495
merged
2025-10-31 05:21:20 +00:00
f7c55b4e67 Changes in Attendance tabs. 2025-10-31 05:21:20 +00:00
6a78eddd2b Changes in UI of Attendance Tabs 2025-10-31 05:21:20 +00:00
8fb2091dbd Changes in Expense Component UI. 2025-10-30 16:30:29 +05:30
565ce3f757 UI changes in View popup at attendance logs 2025-10-30 12:16:25 +05:30
c7976a5a0a Changes in Expense List at heading Expense Type to Expense Category. 2025-10-30 12:06:57 +05:30
31a8329254 added initial setp for aing subscription 2025-10-30 09:48:35 +05:30
46ad5429cf intergrated self subscription 2025-10-29 18:59:05 +05:30
e5b4e8325f integrated self subsciption 2025-10-29 18:58:48 +05:30
e95a78e1a3 added payment getway using raz razorpay 2025-10-29 10:58:28 +05:30
a2f105dd41 Merge pull request 'Expense_Weidget_main' (#489) from Expense_Weidget_main into main
Reviewed-on: #489
merged
2025-10-25 10:18:16 +00:00
aa1fc00611 initially setup paymentgatway 2025-10-25 15:37:38 +05:30
247bda1be4 format figure in at dashboard 2025-10-25 12:16:22 +05:30
fc2d115f40 fixed move figure out f card - using type= compact 2025-10-25 12:12:36 +05:30
7f848eeb38 fixed figure moved out of card 2025-10-25 11:40:34 +05:30
b38eb1bc4b handle view documents inside expense 2025-10-25 11:37:23 +05:30
9cd9e0fbbe added document files properly handling inside 2025-10-25 11:20:57 +05:30
4238157fd4 prevent error occurering due to zoom in -out 2025-10-25 11:02:57 +05:30
d7caf47498 added zoom functionality inside prevw documents 2025-10-25 10:45:52 +05:30
b4f1c48293 removed debugger 2025-10-25 10:27:21 +05:30
a070d23304 added skeleton for dashboard card figures cards 2025-10-25 10:26:41 +05:30
32f16092db changed ui for org 2025-10-24 17:29:38 +05:30
1372f9870a updated - add org. ui 2025-10-24 16:01:35 +05:30
d130ede851 changed button size of resetbutton inside preview-document component 2025-10-24 13:19:09 +05:30
9355efa4af fixed search position of master search bar 2025-10-24 13:12:41 +05:30
c438b51ef1 Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Expense_Weidget_main 2025-10-24 13:04:39 +05:30
5b3f002772 Merge branch 'Collection_Management' 2025-10-24 12:33:58 +05:30
0d9037dbdc Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Collection_Management 2025-10-24 12:32:42 +05:30
55d455bb10 Merge pull request 'Collection_Management' (#487) from Collection_Management into main
Reviewed-on: #487
Merged
2025-10-24 06:41:58 +00:00
767e218987 changed useCollection file extension syntax js to jsx 2025-10-24 06:41:58 +00:00
e0eec8ff69 added ratoted image inside preview documents 2025-10-24 06:41:58 +00:00
c38efe9934 added mismatch permission 2025-10-24 06:41:58 +00:00
9e5555ff9d added properly permissions 2025-10-24 06:41:58 +00:00
8a18c0860e added collection permission 2025-10-24 06:41:58 +00:00
e5b01e9e0f added xl, and doc file allow to upload and getSubscription plan api properly 2025-10-24 06:41:58 +00:00
b7268f9294 removed console 2025-10-24 06:41:58 +00:00
43ecdadfa9 added paymentadjustment head crud operation 2025-10-24 06:41:58 +00:00
005261abab adjust history table whenever select comment tab 2025-10-24 06:41:58 +00:00
9527427f47 added provideAll is flag inside basic projctname api 2025-10-24 06:41:58 +00:00
10fc88c022 added mark as completd api operation 2025-10-24 06:41:58 +00:00
3b0097f46b integrated editcollection and vew douments 2025-10-24 06:41:58 +00:00
ecd726bbb1 integrated comment api added comment and payments history inside view collection 2025-10-24 06:41:58 +00:00
d14fb130c6 updated payhistory after add payment 2025-10-24 06:41:58 +00:00
b6ef3e0d22 added skeleton for payment history at add payment form 2025-10-24 06:41:58 +00:00
b3f489fe9f rmoved console 2025-10-24 06:41:58 +00:00
edae70f8af integrated collection details api 2025-10-24 06:41:58 +00:00
4563e328a5 changed useCollection file extension syntax js to jsx 2025-10-24 12:01:24 +05:30
8f0ca4a9ca added ratoted image inside preview documents 2025-10-17 12:26:57 +05:30
dec959c495 added mismatch permission 2025-10-17 11:55:55 +05:30
cb7e044b27 added properly permissions 2025-10-17 11:19:44 +05:30
4c059afb72 added collection permission 2025-10-17 10:18:11 +05:30
97525e3cb2 Correction in Expense compoent at name column. 2025-10-16 12:51:03 +05:30
2a0f7794b5 Changes in filter panel. 2025-10-16 10:17:12 +05:30
798ea24088 Merge branch 'Issues_Oct_main_2W' of https://git.marcoaiot.com/admin/marco.pms.web into Expense_Weidget_main 2025-10-16 10:03:02 +05:30
8460460caf Merge pull request 'Excessive space between Organization dropdown and Search bar in Attendance menu.' (#484) from Kartik_Bug#1499 into Issues_Oct_main_2W
Reviewed-on: #484
Merged
2025-10-15 12:37:16 +00:00
c8273070ac Changes in Attendance and Expenselist. 2025-10-15 12:37:16 +00:00
dc4e48ad3b Changes in Employee list at pagination and at mobile view then scrollbar is shown. 2025-10-15 12:37:16 +00:00
20b508bebc Changes in Regularization tab. 2025-10-15 12:37:16 +00:00
05c01d1d34 Changes in Attendance page. 2025-10-15 12:37:16 +00:00
073897156e Excessive space between Organization dropdown and Search bar in Attendance menu. 2025-10-15 12:37:16 +00:00
3a2fcf71ee Merge pull request 'Export to PDF button not functioning' (#479) from Kartik_Bug#1434 into Issues_Oct_main_2W
Reviewed-on: #479
Merged
2025-10-15 12:33:55 +00:00
f0c6aea55d Create a seprate file for export functionality in employee. 2025-10-15 12:33:55 +00:00
278a5651fe Changes in excel import. 2025-10-15 12:33:55 +00:00
6fc3e674e5 Export to PDF button not functioning 2025-10-15 12:33:55 +00:00
9f4d82eb06 Merge pull request '“No images match the selected filters.” message should be displayed at the center of the page.' (#481) from Kartik_Bug#1497 into Issues_Oct_main_2W
Reviewed-on: #481
Merged
2025-10-15 12:26:25 +00:00
4ba0c823c0 “No images match the selected filters.” message should be displayed at the center of the page. 2025-10-15 12:26:25 +00:00
acf6a28191 Merge pull request 'Attendance "No record" message improvement' (#480) from Kartik_Bug#1437 into Issues_Oct_main_2W
Reviewed-on: #480
merged
2025-10-15 12:24:37 +00:00
98c90f2a9b Attendance "No record" message improvement 2025-10-15 12:24:37 +00:00
6e89fbd680 added xl, and doc file allow to upload and getSubscription plan api properly 2025-10-15 17:31:58 +05:30
cc2a82e3f0 Removing error in Contact Filter panel. 2025-10-15 16:56:18 +05:30
b23518f796 Changes in expense filter panel. 2025-10-15 15:58:59 +05:30
9648d1a98b removed console 2025-10-15 15:55:46 +05:30
aa947b791b added paymentadjustment head crud operation 2025-10-15 15:42:30 +05:30
bd6332fa61 Changes in Filter panel. 2025-10-15 14:10:03 +05:30
51cca64dd5 adjust history table whenever select comment tab 2025-10-15 12:55:07 +05:30
57d65a5fe7 Adding Chips in Document, Directory and Expense Page. 2025-10-15 12:46:47 +05:30
ca88928850 added provideAll is flag inside basic projctname api 2025-10-15 12:45:11 +05:30
962286a4da added mark as completd api operation 2025-10-15 12:35:07 +05:30
0b02531909 Adding Table-respnsive in projectNav. 2025-10-15 11:33:09 +05:30
dec15278fa Changes in Emp Attendance and DateRange picker. 2025-10-15 11:00:18 +05:30
76df08e921 integrated editcollection and vew douments 2025-10-14 20:17:48 +05:30
0052fed1e6 integrated comment api added comment and payments history inside view collection 2025-10-14 18:20:36 +05:30
da56c59ac9 Merge branch 'Issues_Oct_main_2W' of https://git.marcoaiot.com/admin/marco.pms.web into Expense_Weidget_main 2025-10-14 17:40:42 +05:30
d9392c244e Changes in UI of Weigets and show selected project on 2 heading. 2025-10-14 17:29:20 +05:30
a7f1ba97c3 Creating new weidgets in Dashboard in main. 2025-10-14 17:14:06 +05:30
b80af5467c Changes in Infrastructure. 2025-10-14 15:07:07 +05:30
e2035e1fd8 updated payhistory after add payment 2025-10-14 14:28:49 +05:30
7176a86913 Merge pull request 'integrated collection details api' (#485) from collection_dev into Collection_Management
Reviewed-on: #485
Merged
2025-10-14 08:54:29 +00:00
a26e4d1dc2 added skeleton for payment history at add payment form 2025-10-14 08:54:29 +00:00
f91c7d6da1 rmoved console 2025-10-14 08:54:29 +00:00
272645f0b4 integrated collection details api 2025-10-14 08:54:29 +00:00
301684a12b Merge pull request 'collection_dev' (#482) from collection_dev into Collection_Management
Reviewed-on: #482
merged
2025-10-14 04:55:41 +00:00
9288ac1fbc successfuuly fetch data and create collection 2025-10-14 10:24:45 +05:30
376a2a397f intergrated get list and create collection 2025-10-14 00:21:19 +05:30
58c2fbdf1b initiallly setup 2025-10-13 17:15:59 +05:30
9592108472 Removing extra margin-top on Project-details. 2025-10-13 16:35:28 +05:30
3b032b7b07 Merge pull request 'Incorrect Toggle Switch Text for Active/Inactive Employee in Project Teams' (#475) from Kartik_Bug#1455 into Issues_Oct_main_2W
Reviewed-on: #475
merged
2025-10-13 09:33:09 +00:00
b8891d403f Incorrect Toggle Switch Text for Active/Inactive Employee in Project Teams 2025-10-13 09:33:09 +00:00
01568db61c Merge pull request '“NA” Should Be Displayed When Employee Has No Email Instead of “–”' (#476) from Kartik_Bug#1451 into Issues_Oct_main_2W
Reviewed-on: #476
Merged
2025-10-13 09:32:51 +00:00
80a974e3be “NA” Should Be Displayed When Employee Has No Email Instead of “–” 2025-10-13 09:32:51 +00:00
f3e05a11d6 Merge pull request 'Adding Project and Service field in View Organization popup.' (#477) from Kartik_Task#1477 into Issues_Oct_main_2W
Reviewed-on: #477
Merged
2025-10-13 09:32:32 +00:00
222e6495a8 Adding Project and Service field in View Organization popup. 2025-10-13 09:32:32 +00:00
18a3b8a85b Merge pull request 'Filter Sidebar Should Auto-Close When Navigating to Another Page' (#478) from Kartik_Bug#1450 into Issues_Oct_main_2W
Reviewed-on: #478
merged
2025-10-13 09:32:17 +00:00
d75296ffe8 Filter Sidebar Should Auto-Close When Navigating to Another Page 2025-10-13 14:51:47 +05:30
6649cab6a2 Added cursor-not-allowed when user can delete the organization. 2025-10-13 14:16:11 +05:30
eab23389ed Correction in Projects Completion Status in this weidget data cannot be shown. 2025-10-13 12:51:42 +05:30
12b632f087 added new api for list of org 2025-10-11 18:06:51 +05:30
6ee4fb6d04 Updated TeamEmployeeList to fetch employees using useOrganizationEmployees during search. 2025-10-11 17:48:25 +05:30
67bb685d4b added new api for orgaization dropdown 2025-10-11 16:55:22 +05:30
8fd4e7f3f1 added space between first and last name 2025-10-11 16:30:27 +05:30
aca2decb00 clear fully cache after remove session 2025-10-11 16:26:04 +05:30
f7f4b68997 changed reimburse data during transaction to current date 2025-10-11 15:09:04 +05:30
f839613066 Merge pull request 'Fix: Ensure orgData can be cleared when opening Organization Modal' (#474) from HotChanges_11_10_25 into main
Reviewed-on: #474
Merged
2025-10-11 08:42:26 +00:00
b58bd33774 added requested b and requested at column inside rgularization 2025-10-11 14:08:27 +05:30
136bc94c5b removed dbugger and add new classes 2025-10-11 13:12:23 +05:30
281a956ac8 fixed directory header layout 2025-10-11 12:43:37 +05:30
31882c3d12 REMOVED UNNEEDED CARD CLASS 2025-10-11 11:13:38 +05:30
a64635cd37 Fix: Ensure orgData can be cleared when opening Organization Modal 2025-10-11 10:29:40 +05:30
9b8c8c34ab Merge pull request 'HotChanges_10_10_25' (#472) from HotChanges_10_10_25 into main
Reviewed-on: #472
Merged
2025-10-10 14:50:04 +00:00
704ba79289 prevent to select future date 2025-10-10 20:17:06 +05:30
3440467107 wapped header content into separated div 2025-10-10 18:25:19 +05:30
f4edcfd2f3 changed prod url 2025-10-10 18:18:20 +05:30
62e5c6899a profile icon should display always at end 2025-10-10 18:17:24 +05:30
00a23b3de9 changed class for table responsive 2025-10-10 18:11:43 +05:30
79161a8ede prevent to pick future date 2025-10-10 18:09:35 +05:30
860779d096 fixed service list show in Dialy Task 2025-10-10 18:08:21 +05:30
a2067e150d fixed service list show 2025-10-10 17:51:12 +05:30
edce5ef614 added newhook that return only organization employee for employee list (Active or Inactive) 2025-10-10 16:48:22 +05:30
dd944b3414 datepicker should not take future date 2025-10-10 16:46:34 +05:30
13d3572cf6 fixed datepicke ui 2025-10-10 16:45:43 +05:30
0dd7c19457 added navigation hook to replace path 2025-10-10 15:37:59 +05:30
3cc4f0b416 mismatch service api fixed 2025-10-10 14:15:30 +05:30
f8095ac9bf upgraded progress 2025-10-10 10:55:51 +05:30
6280abf95e added marging between fltes input box 2025-10-09 17:32:39 +05:30
1200937097 Merge pull request 'HotChanges_09_10_25 : Added Daily Task Filter' (#467) from HotChanges_09_10_25 into main
Reviewed-on: #467
Merged
2025-10-09 12:00:21 +00:00
5d1ccb9572 changed buttons size 2025-10-09 17:25:03 +05:30
91ffc5a0e0 added filter for DailyTask Report 2025-10-09 17:23:04 +05:30
1c4804fed2 added for serviceI dropdown list 2025-10-09 16:48:15 +05:30
0e5e716df2 removed debuggger 2025-10-09 16:20:43 +05:30
aecaee7116 addded project when project goin to update state 2025-10-09 15:38:59 +05:30
97dca1a10b removed unused code 2025-10-07 14:53:48 +05:30
c35eacca5a addded filter added inside api 2025-10-07 14:02:25 +05:30
5e27ed36fa Merge pull request 'HotChanges_06-10-25 : Image Gallery and Attendanc CheckIn check-Out' (#459) from HotChanges_06-10-25 into main
Reviewed-on: #459
Merged
2025-10-07 07:12:49 +00:00
8bcfcc5718 fixed attendance - check In -out and persisted date range from Redux store for attendance logs 2025-10-07 07:12:49 +00:00
ad1bef4f7b added gallery and gallery filter 2025-10-07 07:12:49 +00:00
9886fac03e employee attendance display sorted by date, 2025-10-06 17:20:36 +05:30
a28a7fb444 checked-Out Time should be greather than or equal to checked In time 2025-10-06 16:07:31 +05:30
470421b730 added two date and service-name column inside ProjectAssignedOrgslist and fixed inactive fn in project team 2025-10-06 10:58:07 +05:30
7505b790a7 added trim fn to avoid extra spacing 2025-10-06 10:06:31 +05:30
8fd13247c7 Merge pull request 'HotChanges_04_10_25' (#450) from HotChanges_04_10_25 into main
Reviewed-on: #450
Merged
2025-10-05 04:28:55 +00:00
0fec257354 make removeSession flexible to clear local, session, or both 2025-10-05 00:21:19 +05:30
638c033705 fixed employee name show whenever update expense - for paid by 2025-10-05 00:14:39 +05:30
7872e21477 changed date utils and added search employee inside manage expense 2025-10-04 19:50:26 +05:30
6928bbd309 Changes in ProjectRepository api for getProjectInfraByProject 2025-10-04 17:50:54 +05:30
3693af3d00 added missing projects at ManageContact (Edit&Crate) 2025-10-04 16:36:11 +05:30
347 changed files with 29262 additions and 12717 deletions

View File

@ -5,7 +5,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Marco PMS</title>
<title>OnFieldWork.com</title>
<meta name="description" content="" />
@ -46,6 +46,8 @@
<link rel="stylesheet" href="/assets/vendor/libs/animate-css/animate.css" />
<link rel="stylesheet" href="/assets/vendor/libs/sweetalert2/sweetalert2.css" />
<link rel="stylesheet" href="/assets/vendor/libs/spinkit/spinkit.css" />
<link rel="stylesheet" href="/assets/vendor/libs/tagify/tagify.css" />
<link rel="stylesheet" href="/assets/vendor/libs/tagify/tagify.js" />
<!-- Helpers -->
<script src="/assets/vendor/js/helpers.js"></script>
@ -94,6 +96,15 @@
<script src="/assets/js/main.js"></script>
<!-- Page JS -->
<script src="/assets/js/form-wizard-icons.js"></script>
<script src="/assets/js/dashboards-analytics.js"></script>
<!-- Bloack Ui -->
<!-- <script src="/assets/js/extended-ui-blockui.js"></script> -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- BlockUI core plugin -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js"></script>
<script src="/assets/js/form-wizard-icons.js"></script>
<script src="/assets/js/dashboards-analytics.js"></script>

View File

@ -3,10 +3,456 @@
--bs-nav-link-font-size: 0.7375rem;
--bg-border-color :#f8f6f6
}
.offcanvas.offcanvas-wide {
width: 700px !important; /* adjust as needed */
}
.sticky-section {
position: sticky;
top: var(--sticky-top, 0px) !important;
z-index: 1025;
}
/* ===========================% Background_Colors %========================================================== */
.bg-light-primary {
background-color: color-mix(in srgb, var(--bs-primary) 10.4%, transparent);
border:var(--bs-primary-border-subtle)
}
.bg-light-secondary {
background-color: color-mix(in srgb, var(--bs-secondary) 10.4%, transparent);
}
.bg-light-danger {
background-color: color-mix(in srgb, var(--bs-danger) 10.4%, transparent);
}
.bg-light-success {
background-color: color-mix(in srgb, var(--bs-success) 10.4%, transparent);
}
.bg-light-info {
background-color: color-mix(in srgb, var(--bs-info) 10.4%, transparent);
}
.bg-light-warning {
background-color: color-mix(in srgb, var(--bs-warning) 10.4%, transparent);
}
.card-header {
padding: 0.5rem var(--bs-card-cap-padding-x);
}
.table_header_border {
border-bottom:2px solid var(--bs-table-border-color) ;
}
}
.text-gary-80 {
color:var(--bs-gray-500)
}
.text-royalblue{
color: #1796e3;
}
.text-md {
font-size: 2rem;
}
.text-md-b {
font-weight: normal;
}
.stepper-container {
position: relative;
}
.timeline-horizontal {
position: relative;
padding: 0;
margin: 0;
}
.timeline-item {
position: relative;
flex: 1;
}
.timeline-point {
width: 20px;
height: 20px;
border-radius: 50%;
background: #dee2e6;
color: #6c757d;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
z-index: 2;
position: relative;
padding: 3px;
transition: all 0.3s ease;
}
.timeline-point.completed {
background-color: var(--bs-success);
color: #fff;
box-shadow: 0 0 5px rgba(25, 135, 84, 0.5);
}
.timeline-point.failed {
background-color: var(--bs-danger);
color: #fff;
box-shadow: 0 0 5px rgba(220, 53, 69, 0.5);
}
.timeline-point.active {
background-color: var(--bs-info);
color: #fff;
transform: scale(1.15);
box-shadow: 0 0 6px rgba(13, 202, 240, 0.5);
}
.timeline-line-horizontal {
content: "";
position: absolute;
top: 10px;
left: 50%;
width: 100%;
height: 2px;
background-color: #dee2e6;
z-index: 1;
transition: background-color 0.3s ease;
}
/* Make line green for completed sections */
.timeline-item.completed ~ .timeline-line-horizontal {
background-color: var(--bs-success);
}
/* Optional: subtle pulse for active step */
.timeline-point.active::after {
content: "";
position: absolute;
width: 25px;
height: 25px;
border-radius: 50%;
border: 2px solid var(--bs-info);
animation: pulse 1.5s infinite;
opacity: 0.6;
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 0.6;
}
70% {
transform: scale(1.5);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 0;
}
}
.text-xxs { font-size: 0.55rem; } /* 8px */
.text-xs { font-size: 0.75rem; } /* 12px */
.text-sm { font-size: 0.875rem; } /* 14px */
.text-base { font-size: 1rem; } /* 16px */
.text-lg { font-size: 1.125rem; } /* 18px */
.text-xl { font-size: 1.25rem; } /* 20px */
.text-2xl { font-size: 1.5rem; } /* 24px */
.text-3xl { font-size: 1.875rem; } /* 30px */
.text-4xl { font-size: 2.25rem; } /* 36px */
.text-5xl { font-size: 3rem; } /* 48px */
.text-6xl { font-size: 3.75rem; } /* 60px */
.text-7xl { font-size: 4.5rem; } /* 72px */
.text-8xl { font-size: 6rem; } /* 96px */
.text-9xl { font-size: 8rem; } /* 128px */
/* */
.w-0 { width: 0px; }
.w-px { width: 1px; }
.w-1 { width: 0.25rem; } /* 4px */
.w-2 { width: 0.5rem; } /* 8px */
.w-3 { width: 0.75rem; } /* 12px */
.w-4 { width: 1rem; } /* 16px */
.w-5 { width: 1.25rem; } /* 20px */
.w-6 { width: 1.5rem; } /* 24px */
.w-8 { width: 2rem; } /* 32px */
.w-10 { width: 2.5rem; } /* 40px */
.w-12 { width: 3rem; } /* 48px */
.w-16 { width: 4rem; } /* 64px */
.w-20 { width: 5rem; } /* 80px */
.w-24 { width: 6rem; } /* 96px */
.w-32 { width: 8rem; } /* 128px */
.w-40 { width: 10rem; } /* 160px */
.w-48 { width: 12rem; } /* 192px */
.w-56 { width: 14rem; } /* 224px */
.w-64 { width: 16rem; } /* 256px */
.w-auto { width: auto; }
.w-full { width: 100%; }
.w-screen{ width: 100vw; }
.w-min { width: min-content; }
.w-max { width: max-content; }
.h-0 { height: 0px; }
.h-px { height: 1px; }
.h-1 { height: 0.25rem; } /* 4px */
.h-2 { height: 0.5rem; } /* 8px */
.h-3 { height: 0.75rem; } /* 12px */
.h-4 { height: 1rem; } /* 16px */
.h-5 { height: 1.25rem; } /* 20px */
.h-6 { height: 1.5rem; } /* 24px */
.h-8 { height: 2rem; } /* 32px */
.h-10 { height: 2.5rem; } /* 40px */
.h-12 { height: 3rem; } /* 48px */
.h-16 { height: 4rem; } /* 64px */
.h-20 { height: 5rem; } /* 80px */
.h-24 { height: 6rem; } /* 96px */
.h-32 { height: 8rem; } /* 128px */
.h-40 { height: 10rem; } /* 160px */
.h-48 { height: 12rem; } /* 192px */
.h-56 { height: 14rem; } /* 224px */
.h-64 { height: 16rem; } /* 256px */
.h-auto { height: auto; }
.h-full { height: 100%; }
.h-screen{ height: 100vh; }
.h-min { height: min-content; }
.h-max { height: max-content; }
/* ==========================
Base Font Sizes (mobile first)
========================== */
.text-xxs { font-size: 0.55rem; } /* 8px */
.text-xs { font-size: 0.75rem; } /* 12px */
.text-sm { font-size: 0.875rem; } /* 14px */
.text-base{ font-size: 1rem; } /* 16px */
.text-lg { font-size: 1.125rem; } /* 18px */
.text-xl { font-size: 1.25rem; } /* 20px */
.text-2xl { font-size: 1.5rem; } /* 24px */
.text-3xl { font-size: 1.875rem; } /* 30px */
.text-4xl { font-size: 2.25rem; } /* 36px */
.text-5xl { font-size: 3rem; } /* 48px */
.text-6xl { font-size: 3.75rem; } /* 60px */
.text-7xl { font-size: 4.5rem; } /* 72px */
.text-8xl { font-size: 6rem; } /* 96px */
.text-9xl { font-size: 8rem; } /* 128px */
/* ==========================
Base Heights
========================== */
.h-0 { height: 0; }
.h-px { height: 1px; }
.h-1 { height: 0.25rem; } /* 4px */
.h-2 { height: 0.5rem; } /* 8px */
.h-3 { height: 0.75rem; } /* 12px */
.h-4 { height: 1rem; } /* 16px */
.h-5 { height: 1.25rem; } /* 20px */
.h-6 { height: 1.5rem; } /* 24px */
.h-8 { height: 2rem; } /* 32px */
.h-10 { height: 2.5rem; } /* 40px */
.h-12 { height: 3rem; } /* 48px */
.h-16 { height: 4rem; } /* 64px */
.h-20 { height: 5rem; } /* 80px */
.h-24 { height: 6rem; } /* 96px */
.h-32 { height: 8rem; } /* 128px */
.h-40 { height: 10rem; } /* 160px */
.h-48 { height: 12rem; } /* 192px */
.h-56 { height: 14rem; } /* 224px */
.h-64 { height: 16rem; } /* 256px */
.h-70 { height: 20rem; } /* 256px */
.h-74 { max-height: 35rem; } /* 256px */
.h-full { height: 100%; }
.h-screen{ height: 100vh; }
/* ==========================
Base Widths
========================== */
.w-0 { width: 0; }
.w-px { width: 1px; }
.w-1 { width: 0.25rem; }
.w-2 { width: 0.5rem; }
.w-3 { width: 0.75rem; }
.w-4 { width: 1rem; }
.w-5 { width: 1.25rem; }
.w-6 { width: 1.5rem; }
.w-8 { width: 2rem; }
.w-10 { width: 2.5rem; }
.w-12 { width: 3rem; }
.w-16 { width: 4rem; }
.w-20 { width: 5rem; }
.w-24 { width: 6rem; }
.w-32 { width: 8rem; }
.w-40 { width: 10rem; }
.w-48 { width: 12rem; }
.w-56 { width: 14rem; }
.w-64 { width: 16rem; }
.w-full { width: 100%; }
.w-screen{ width: 100vw; }
/* ==========================
Responsive Variants
========================== */
@media (min-width: 576px) { /* sm */
/* Font */
.text-xxs-sm { font-size: 0.55rem; }
.text-xs-sm { font-size: 0.75rem; }
.text-sm-sm { font-size: 0.875rem; }
.text-base-sm{ font-size: 1rem; }
.text-lg-sm { font-size: 1.125rem; }
.text-xl-sm { font-size: 1.25rem; }
.text-2xl-sm{ font-size: 1.5rem; }
/* Height */
.h-1-sm{ height: 0.25rem; }
.h-2-sm{ height: 0.5rem; }
.h-3-sm{ height: 0.75rem; }
.h-4-sm{ height: 1rem; }
.h-5-sm{ height: 1.25rem; }
.h-6-sm{ height: 1.5rem; }
.h-8-sm{ height: 2rem; }
.h-10-sm{ height: 2.5rem; }
/* Width */
.w-1-sm{ width: 0.25rem; }
.w-2-sm{ width: 0.5rem; }
.w-3-sm{ width: 0.75rem; }
.w-4-sm{ width: 1rem; }
.w-5-sm{ width: 1.25rem; }
.w-6-sm{ width: 1.5rem; }
.w-8-sm{ width: 2rem; }
.w-10-sm{ width: 2.5rem; }
}
@media (min-width: 768px) { /* md */
/* Font */
.text-xxs-md { font-size: 0.55rem; }
.text-xs-md { font-size: 0.75rem; }
.text-sm-md { font-size: 0.875rem; }
.text-base-md{ font-size: 1rem; }
.text-lg-md { font-size: 1.125rem; }
.text-xl-md { font-size: 1.25rem; }
.text-2xl-md{ font-size: 1.5rem; }
/* Height */
.h-1-md{ height: 0.25rem; }
.h-2-md{ height: 0.5rem; }
.h-3-md{ height: 0.75rem; }
.h-4-md{ height: 1rem; }
.h-5-md{ height: 1.25rem; }
.h-6-md{ height: 1.5rem; }
.h-8-md{ height: 2rem; }
.h-10-md{ height: 2.5rem; }
/* Width */
.w-1-md{ width: 0.25rem; }
.w-2-md{ width: 0.5rem; }
.w-3-md{ width: 0.75rem; }
.w-4-md{ width: 1rem; }
.w-5-md{ width: 1.25rem; }
.w-6-md{ width: 1.5rem; }
.w-8-md{ width: 2rem; }
.w-10-md{ width: 2.5rem; }
}
@media (min-width: 992px) { /* lg */
/* Font */
.text-xxs-lg { font-size: 0.55rem; }
.text-xs-lg { font-size: 0.75rem; }
.text-sm-lg { font-size: 0.875rem; }
.text-base-lg{ font-size: 1rem; }
.text-lg-lg { font-size: 1.125rem; }
.text-xl-lg { font-size: 1.25rem; }
.text-2xl-lg{ font-size: 1.5rem; }
/* Height */
.h-1-lg{ height: 0.25rem; }
.h-2-lg{ height: 0.5rem; }
.h-3-lg{ height: 0.75rem; }
.h-4-lg{ height: 1rem; }
.h-5-lg{ height: 1.25rem; }
.h-6-lg{ height: 1.5rem; }
.h-8-lg{ height: 2rem; }
.h-10-lg{ height: 2.5rem; }
/* Width */
.w-1-lg{ width: 0.25rem; }
.w-2-lg{ width: 0.5rem; }
.w-3-lg{ width: 0.75rem; }
.w-4-lg{ width: 1rem; }
.w-5-lg{ width: 1.25rem; }
.w-6-lg{ width: 1.5rem; }
.w-8-lg{ width: 2rem; }
.w-10-lg{ width: 2.5rem; }
}
@media (min-width: 1200px) { /* xl */
/* Font */
.text-xxs-xl { font-size: 0.55rem; }
.text-xs-xl { font-size: 0.75rem; }
.text-sm-xl { font-size: 0.875rem; }
.text-base-xl{ font-size: 1rem; }
.text-lg-xl { font-size: 1.125rem; }
.text-xl-xl { font-size: 1.25rem; }
.text-2xl-xl{ font-size: 1.5rem; }
/* Height */
.h-1-xl{ height: 0.25rem; }
.h-2-xl{ height: 0.5rem; }
.h-3-xl{ height: 0.75rem; }
.h-4-xl{ height: 1rem; }
.h-5-xl{ height: 1.25rem; }
.h-6-xl{ height: 1.5rem; }
.h-8-xl{ height: 2rem; }
.h-10-xl{ height: 2.5rem; }
/* Width */
.w-1-xl{ width: 0.25rem; }
.w-2-xl{ width: 0.5rem; }
.w-3-xl{ width: 0.75rem; }
.w-4-xl{ width: 1rem; }
.w-5-xl{ width: 1.25rem; }
.w-6-xl{ width: 1.5rem; }
.w-8-xl{ width: 2rem; }
.w-10-xl{ width: 2.5rem; }
}
/* ------------------------Text------------------------- */
@media (min-width: 576px) {
.fs-sm-1 { font-size: calc(1.3rem + 1.6vw) !important; }
.fs-sm-2 { font-size: calc(1.2rem + 1.2vw) !important; }
.fs-sm-3 { font-size: calc(1.1rem + 0.8vw) !important; }
.fs-sm-4 { font-size: calc(1rem + 0.5vw) !important; }
.fs-sm-5 { font-size: 1.05rem !important; }
.fs-sm-6 { font-size: 0.9rem !important; }
.fs-sm-tiny { font-size: 72% !important; }
.fs-sm-big { font-size: 115% !important; }
.fs-sm-large { font-size: 155% !important; }
.fs-sm-xlarge { font-size: 175% !important; }
.fs-sm-xxlarge { font-size: calc(1.6rem + 3.5vw) !important; }
}
/* 💻 Medium devices (≥768px) */
@media (min-width: 768px) {
.fs-md-1 { font-size: calc(1.4125rem + 1.95vw) !important; }
.fs-md-2 { font-size: calc(1.3625rem + 1.35vw) !important; }
.fs-md-3 { font-size: calc(1.3rem + 0.6vw) !important; }
.fs-md-4 { font-size: calc(1.275rem + 0.3vw) !important; }
.fs-md-5 { font-size: 1.125rem !important; }
.fs-md-6 { font-size: 0.9375rem !important; }
.fs-md-tiny { font-size: 70% !important; }
.fs-md-big { font-size: 112% !important; }
.fs-md-large { font-size: 150% !important; }
.fs-md-xlarge { font-size: 170% !important; }
.fs-md-xxlarge { font-size: calc(1.725rem + 5.7vw) !important; }
}

View File

@ -30,11 +30,6 @@
width: 45px;
}
.app-brand-logo-border {
border: 1px solid #d5d5d5;
}
.app-brand-text {
font-size: 1.75rem;
letter-spacing: -0.5px;
@ -165,10 +160,9 @@ thead tr {
.app-brand-logo-login {
max-width: 50px; /* default for mobile */
height: auto; /* keep aspect ratio */
height: auto; /* keep aspect ratio */
}
/* Tablet and up (≥768px) */
@media (min-width: 768px) {
.app-brand-logo-login {
@ -182,4 +176,3 @@ thead tr {
max-width: 80px;
}
}

View File

@ -76,6 +76,7 @@
--bs-dark-border-subtle: #bfc0c6;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 34, 48, 62;
--bs-font-roboto:"Segoe UI", Roboto, "sans-serif",
--bs-font-sans-serif: "Public Sans", -apple-system, blinkmacsystemfont,
"Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", sans-serif;
@ -88,7 +89,7 @@
);
--bs-root-font-size: 16px;
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 0.8375rem;
--bs-body-font-size: 0.85rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.375;
--bs-body-color: #646e78;
@ -9059,7 +9060,7 @@ img[data-app-light-img][data-app-dark-img] {
}
.table th {
color: var(--bs-heading-color);
font-size: 0.8125rem;
font-size: 0.8025rem;
letter-spacing: 0.2px;
text-transform: uppercase;
}
@ -20344,7 +20345,7 @@ li:not(:first-child) .dropdown-item,
}
.fs-6 {
font-size: 0.9375rem !important;
font-size: 0.8375rem !important;
}
.fs-tiny {
@ -32559,9 +32560,7 @@ body:not(.modal-open) .layout-content-navbar .layout-navbar {
.bg-blue {
background-color:var(--bs-blue)
}
.text-blue{
color:var(--bs-blue)
}
.bg-indigo {
background-color:var(--bs-indigo)
}
@ -32573,4 +32572,10 @@ body:not(.modal-open) .layout-content-navbar .layout-navbar {
}
.text-red{
color:var(--bs-red)
}
.text-blue{
color:var(--bs-blue)
}
.text-green{
color:var(--bs-green)
}

View File

@ -0,0 +1,879 @@
@charset "UTF-8";
:root {
--tagify-dd-color-primary: rgb(53,149,246);
--tagify-dd-bg-color: white;
--tagify-dd-item-pad: .3em .5em;
--tagify-dd-max-height: 300px;
}
.tagify {
--tags-disabled-bg: #F1F1F1;
--tags-border-color: #DDD;
--tags-hover-border-color: #CCC;
--tags-focus-border-color: #3595f6;
--tag-border-radius: 3px;
--tag-bg: rgba(167, 172, 178, 0.5);
--tag-hover: #D3E2E2;
--tag-text-color: black;
--tag-text-color--edit: black;
--tag-pad: 0.3em 0.5em;
--tag-inset-shadow-size: 2em;
--tag-invalid-color: #ff3e1d;
--tag-invalid-bg: rgba(255, 62, 29, 0.5);
--tag--min-width: 1ch;
--tag--max-width: auto;
--tag-hide-transition: 0.3s;
--tag-remove-bg: rgba(255, 62, 29, 0.3);
--tag-remove-btn-color: #7a838b;
--tag-remove-btn-bg: none;
--tag-remove-btn-bg--hover: #ff2804;
--input-color: inherit;
--placeholder-color: rgba(0, 0, 0, 0.4);
--placeholder-color-focus: rgba(0, 0, 0, 0.25);
--loader-size: .8em;
--readonly-striped: 1;
display: inline-flex;
align-items: flex-start;
flex-wrap: wrap;
border: 1px solid var(--tags-border-color);
padding: 0;
line-height: 0;
cursor: text;
outline: none;
position: relative;
box-sizing: border-box;
transition: 0.1s;
}
@keyframes tags--bump {
30% {
transform: scale(1.2);
}
}
@keyframes rotateLoader {
to {
transform: rotate(1turn);
}
}
.tagify:hover:not(.tagify--focus):not(.tagify--invalid) {
--tags-border-color: var(--tags-hover-border-color);
}
.tagify[disabled] {
background: var(--tags-disabled-bg);
filter: saturate(0);
opacity: 0.5;
pointer-events: none;
}
.tagify[readonly].tagify--select, .tagify[disabled].tagify--select {
pointer-events: none;
}
.tagify[readonly]:not(.tagify--mix):not(.tagify--select), .tagify[disabled]:not(.tagify--mix):not(.tagify--select) {
cursor: default;
}
.tagify[readonly]:not(.tagify--mix):not(.tagify--select) > .tagify__input, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) > .tagify__input {
visibility: hidden;
width: 0;
margin: 5px 0;
}
.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div {
padding: var(--tag-pad);
}
.tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before, .tagify[disabled]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before {
animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused;
}
@keyframes readonlyStyles {
0% {
background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px;
box-shadow: none;
filter: brightness(0.95);
}
}
.tagify[readonly] .tagify__tag__removeBtn, .tagify[disabled] .tagify__tag__removeBtn {
display: none;
}
.tagify--loading .tagify__input > br:last-child {
display: none;
}
.tagify--loading .tagify__input::before {
content: none;
}
.tagify--loading .tagify__input::after {
content: "";
vertical-align: middle;
opacity: 1;
width: 0.7em;
height: 0.7em;
width: var(--loader-size);
height: var(--loader-size);
min-width: 0;
border: 3px solid;
border-color: #EEE #BBB #888 transparent;
border-radius: 50%;
animation: rotateLoader 0.4s infinite linear;
content: "" !important;
margin: -2px 0 -2px 0.5em;
}
.tagify--loading .tagify__input:empty::after {
margin-left: 0;
}
.tagify + input,
.tagify + textarea {
position: absolute !important;
left: -9999em !important;
transform: scale(0) !important;
}
.tagify__tag {
display: inline-flex;
align-items: center;
max-width: calc(var(--tag--max-width) - 10px);
margin-inline: 5px 0;
margin-block: 5px;
position: relative;
z-index: 1;
outline: none;
line-height: normal;
cursor: default;
transition: 0.13s ease-out;
}
.tagify__tag > div {
vertical-align: top;
box-sizing: border-box;
max-width: 100%;
padding: var(--tag-pad);
color: var(--tag-text-color);
line-height: inherit;
border-radius: var(--tag-border-radius);
white-space: nowrap;
transition: 0.13s ease-out;
}
.tagify__tag > div > * {
white-space: pre-wrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: top;
min-width: var(--tag--min-width);
max-width: var(--tag--max-width);
transition: 0.8s ease, 0.1s color;
}
.tagify__tag > div > *[contenteditable] {
outline: none;
user-select: text;
cursor: text;
margin: -2px;
padding: 2px;
max-width: 350px;
}
.tagify__tag > div::before {
content: "";
position: absolute;
border-radius: inherit;
inset: var(--tag-bg-inset, 0);
z-index: -1;
pointer-events: none;
transition: 120ms ease;
animation: tags--bump 0.3s ease-out 1;
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-bg) inset;
}
.tagify__tag:hover:not([readonly]) div::before, .tagify__tag:focus div::before {
--tag-bg-inset: -2.5px;
--tag-bg: var(--tag-hover);
}
.tagify__tag--loading {
pointer-events: none;
}
.tagify__tag--loading .tagify__tag__removeBtn {
display: none;
}
.tagify__tag--loading::after {
--loader-size: .4em;
content: "";
vertical-align: middle;
opacity: 1;
width: 0.7em;
height: 0.7em;
width: var(--loader-size);
height: var(--loader-size);
min-width: 0;
border: 3px solid;
border-color: #EEE #BBB #888 transparent;
border-radius: 50%;
animation: rotateLoader 0.4s infinite linear;
margin: 0 0.5em 0 -0.1em;
}
.tagify__tag--flash div::before {
animation: none;
}
.tagify__tag--hide {
width: 0 !important;
padding-left: 0;
padding-right: 0;
margin-left: 0;
margin-right: 0;
opacity: 0;
transform: scale(0);
transition: var(--tag-hide-transition);
pointer-events: none;
}
.tagify__tag--hide > div > * {
white-space: nowrap;
}
.tagify__tag.tagify--noAnim > div::before {
animation: none;
}
.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div > span {
opacity: 0.5;
}
.tagify__tag.tagify--notAllowed:not(.tagify__tag--editable) div::before {
--tag-bg: var(--tag-invalid-bg);
transition: 0.2s;
}
.tagify__tag[readonly] .tagify__tag__removeBtn {
display: none;
}
.tagify__tag[readonly] > div::before {
animation: readonlyStyles 1s calc(-1s * (var(--readonly-striped) - 1)) paused;
}
@keyframes readonlyStyles {
0% {
background: linear-gradient(45deg, var(--tag-bg) 25%, transparent 25%, transparent 50%, var(--tag-bg) 50%, var(--tag-bg) 75%, transparent 75%, transparent) 0/5px 5px;
box-shadow: none;
filter: brightness(0.95);
}
}
.tagify__tag--editable > div {
color: var(--tag-text-color--edit);
}
.tagify__tag--editable > div::before {
box-shadow: 0 0 0 2px var(--tag-hover) inset !important;
}
.tagify__tag--editable > .tagify__tag__removeBtn {
pointer-events: none;
}
.tagify__tag--editable > .tagify__tag__removeBtn::after {
opacity: 0;
transform: translateX(100%) translateX(5px);
}
.tagify__tag--editable.tagify--invalid > div::before {
box-shadow: 0 0 0 2px var(--tag-invalid-color) inset !important;
}
.tagify__tag__removeBtn {
order: 5;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50px;
cursor: pointer;
font: 14px/1 Arial;
background: var(--tag-remove-btn-bg);
color: var(--tag-remove-btn-color);
width: 14px;
height: 14px;
margin-inline: auto 4.6666666667px;
overflow: hidden;
transition: 0.2s ease-out;
}
.tagify__tag__removeBtn::after {
content: "×";
transition: 0.3s, color 0s;
}
.tagify__tag__removeBtn:hover {
color: white;
background: var(--tag-remove-btn-bg--hover);
}
.tagify__tag__removeBtn:hover + div > span {
opacity: 0.5;
}
.tagify__tag__removeBtn:hover + div::before {
box-shadow: 0 0 0 var(--tag-inset-shadow-size) var(--tag-remove-bg, rgba(255, 62, 29, 0.3)) inset !important;
transition: box-shadow 0.2s;
}
.tagify:not(.tagify--mix) .tagify__input br {
display: none;
}
.tagify:not(.tagify--mix) .tagify__input * {
display: inline;
white-space: nowrap;
}
.tagify__input {
flex-grow: 1;
display: inline-block;
min-width: 110px;
margin: 5px;
padding: var(--tag-pad);
line-height: normal;
position: relative;
white-space: pre-wrap;
color: var(--input-color);
box-sizing: inherit;
/* Seems firefox newer versions don't need this any more
@supports ( -moz-appearance:none ){
&::before{
line-height: inherit;
position:relative;
}
}
*/
}
@-moz-document url-prefix() {}
.tagify__input:empty::before {
position: static;
}
.tagify__input:focus {
outline: none;
}
.tagify__input:focus::before {
transition: 0.2s ease-out;
opacity: 0;
transform: translatex(6px);
/* ALL MS BROWSERS: hide placeholder (on focus) otherwise the caret is placed after it, which is weird */
/* IE Edge 12+ CSS styles go here */
}
@supports (-ms-ime-align: auto) {
.tagify__input:focus::before {
display: none;
}
}
.tagify__input:focus:empty::before {
transition: 0.2s ease-out;
opacity: 1;
transform: none;
color: rgba(0, 0, 0, 0.25);
color: var(--placeholder-color-focus);
}
@-moz-document url-prefix() {
.tagify__input:focus:empty::after {
display: none;
}
}
.tagify__input::before {
content: attr(data-placeholder);
height: 1em;
line-height: 1em;
margin: auto 0;
z-index: 1;
color: var(--placeholder-color);
white-space: nowrap;
pointer-events: none;
opacity: 0;
position: absolute;
}
.tagify__input::after {
content: attr(data-suggest);
display: inline-block;
vertical-align: middle;
position: absolute;
min-width: calc(100% - 1.5em);
text-overflow: ellipsis;
overflow: hidden;
white-space: pre; /* allows spaces at the beginning */
color: var(--tag-text-color);
opacity: 0.3;
pointer-events: none;
max-width: 100px;
}
.tagify__input .tagify__tag {
margin: 0 1px;
}
.tagify--mix {
display: block;
}
.tagify--mix .tagify__input {
padding: 5px;
margin: 0;
width: 100%;
height: 100%;
line-height: 1.5;
display: block;
}
.tagify--mix .tagify__input::before {
height: auto;
display: none;
line-height: inherit;
}
.tagify--mix .tagify__input::after {
content: none;
}
.tagify--select::after {
content: ">";
opacity: 0.5;
position: absolute;
top: 50%;
right: 0;
bottom: 0;
font: 16px monospace;
line-height: 8px;
height: 8px;
pointer-events: none;
transform: translate(-150%, -50%) scaleX(1.2) rotate(90deg);
transition: 0.2s ease-in-out;
}
.tagify--select[aria-expanded=true]::after {
transform: translate(-150%, -50%) rotate(270deg) scaleY(1.2);
}
.tagify--select .tagify__tag {
position: absolute;
top: 0;
right: 1.8em;
bottom: 0;
}
.tagify--select .tagify__tag div {
display: none;
}
.tagify--select .tagify__input {
width: 100%;
}
.tagify--empty .tagify__input::before {
transition: 0.2s ease-out;
opacity: 1;
transform: none;
display: inline-block;
width: auto;
}
.tagify--mix .tagify--empty .tagify__input::before {
display: inline-block;
}
.tagify--focus {
--tags-border-color: var(--tags-focus-border-color);
transition: 0s;
}
.tagify--invalid {
--tags-border-color: #ff3e1d;
}
.tagify__dropdown {
position: absolute;
z-index: 9999;
transform: translateY(-1px);
border-top: 1px solid var(--tagify-dd-color-primary);
overflow: hidden;
}
.tagify__dropdown[dir=rtl] {
transform: translate(-100%, -1px);
}
.tagify__dropdown[placement=top] {
margin-top: 0;
transform: translateY(-100%);
}
.tagify__dropdown[placement=top] .tagify__dropdown__wrapper {
border-top-width: 1.1px;
border-bottom-width: 0;
}
.tagify__dropdown[position=text] {
box-shadow: 0 0 0 3px rgba(var(--tagify-dd-color-primary), 0.1);
font-size: 0.9em;
}
.tagify__dropdown[position=text] .tagify__dropdown__wrapper {
border-width: 1px;
}
.tagify__dropdown__wrapper {
max-height: var(--tagify-dd-max-height);
overflow: hidden;
overflow-x: hidden;
background: var(--tagify-dd-bg-color);
border: 1px solid;
border-color: var(--tagify-dd-color-primary);
border-bottom-width: 1.5px;
border-top-width: 0;
box-shadow: 0 2px 4px -2px rgba(0, 0, 0, 0.2);
transition: 0.3s cubic-bezier(0.5, 0, 0.3, 1), transform 0.15s;
animation: dd-wrapper-show 0s 0.3s forwards;
}
@keyframes dd-wrapper-show {
to {
overflow-y: auto;
}
}
.tagify__dropdown__header:empty {
display: none;
}
.tagify__dropdown__footer {
display: inline-block;
margin-top: 0.5em;
padding: var(--tagify-dd-item-pad);
font-size: 0.7em;
font-style: italic;
opacity: 0.5;
}
.tagify__dropdown__footer:empty {
display: none;
}
.tagify__dropdown--initial .tagify__dropdown__wrapper {
max-height: 20px;
transform: translateY(-1em);
}
.tagify__dropdown--initial[placement=top] .tagify__dropdown__wrapper {
transform: translateY(2em);
}
.tagify__dropdown__item {
box-sizing: border-box;
padding: var(--tagify-dd-item-pad);
margin: 1px;
white-space: pre-wrap;
cursor: pointer;
border-radius: 2px;
position: relative;
outline: none;
max-height: 60px;
max-width: 100%;
/* custom hidden transition effect is needed for horizontal-layout suggestions */
}
.tagify__dropdown__item--active {
background: var(--tagify-dd-color-primary);
color: white;
}
.tagify__dropdown__item:active {
filter: brightness(105%);
}
.tagify__dropdown__item--hidden {
padding-top: 0;
padding-bottom: 0;
margin: 0 1px;
pointer-events: none;
overflow: hidden;
max-height: 0;
transition: var(--tagify-dd-item--hidden-duration, 0.3s) !important;
}
.tagify__dropdown__item--hidden > * {
transform: translateY(-100%);
opacity: 0;
transition: inherit;
}
/* Suggestions items */
.tagify__dropdown.users-list {
font-size: 1rem;
}
.tagify__dropdown.users-list .addAll {
display: block !important;
}
.tagify__dropdown.users-list .tagify__dropdown__item {
padding: 0.5em 0.7em;
display: grid;
grid-template-columns: auto 1fr;
gap: 0 1em;
grid-template-areas: "avatar name" "avatar email";
}
.tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap {
grid-area: avatar;
width: 36px;
height: 36px;
border-radius: 50%;
overflow: hidden;
transition: 0.1s ease-out;
}
.tagify__dropdown.users-list img {
width: 100%;
vertical-align: top;
}
.tagify__dropdown.users-list strong {
grid-area: name;
width: 100%;
align-self: center;
font-weight: 500;
}
.tagify__dropdown.users-list span {
grid-area: email;
width: 100%;
font-size: 0.9em;
opacity: 0.6;
}
/* Tags items */
.tagify__tag {
white-space: nowrap;
}
.tagify__tag .tagify__tag__avatar-wrap {
width: 22px;
height: 22px;
white-space: normal;
border-radius: 50%;
margin-right: 5px;
transition: 0.12s ease-out;
vertical-align: middle;
}
.tagify__tag img {
width: 100%;
vertical-align: top;
}
[dir=rtl] .tagify__tag .tagify__tag__avatar-wrap {
margin-left: 5px;
margin-right: auto;
}
.light-style .tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap {
background: #f5f5f9;
}
.light-style .tagify__tag .tagify__tag__avatar-wrap {
background: #f5f5f9;
}
.light-style .tagify__dropdown.users-list .addAll {
border-bottom: 1px solid #e4e6e8;
}
.dark-style .tagify__dropdown.users-list .tagify__dropdown__item__avatar-wrap {
background: #232333;
}
.dark-style .tagify__tag .tagify__tag__avatar-wrap {
background: #232333;
}
.dark-style .tagify__dropdown.users-list .addAll {
border-bottom: 1px solid #4e4f6c;
}
.tags-inline .tagify__dropdown__wrapper {
padding: 0 0.4375rem 0.4375rem 0.4375rem;
}
.tags-inline .tagify__dropdown__item {
display: inline-block;
border-radius: 3px;
padding: 0.3em 0.5em;
margin: 0.4375rem 0.4375rem 0 0;
font-size: 0.85em;
transition: 0s;
}
[dir=rtl] .tags-inline .tagify__dropdown__item {
margin: 0.4375rem 0 0 0.4375rem;
}
.light-style .tags-inline .tagify__dropdown__item {
border: 1px solid #e4e6e8;
color: #646e78;
}
.dark-style .tags-inline .tagify__dropdown__item {
border: 1px solid #4e4f6c;
color: #b2b2c4;
}
.tagify-email-list {
display: inline-block;
min-width: 0;
border: none;
/* Do not show the "remove tag" (x) button when only a single tag remains */
}
.tagify-email-list.tagify {
padding: 0 !important;
padding-bottom: calc(0.4375rem - var(--bs-border-width)) !important;
}
.tagify-email-list.tagify {
padding: 0 !important;
padding-bottom: calc(0.4375rem - var(--bs-border-width)) !important;
}
.tagify-email-list.tagify.tagify--focus {
padding-left: 0 !important;
}
.tagify-email-list .tagify__tag {
margin: 0;
margin-inline-start: 0 !important;
margin-inline-end: 0.625rem !important;
margin-bottom: 0.4375rem !important;
}
.tagify-email-list .tagify__tag > div {
padding: 0.21875rem 0.4375rem !important;
padding-inline: 0.875rem !important;
}
.tagify-email-list .tagify__tag:only-of-type > div {
padding-inline: 0.4375rem !important;
}
.tagify-email-list .tagify__tag:only-of-type .tagify__tag__removeBtn {
display: none;
}
.tagify-email-list .tagify__tag__removeBtn {
opacity: 0;
transform: translateX(-6px) scale(0.5);
margin-left: -3ch;
transition: 0.12s;
position: absolute;
inset-inline-end: 0;
}
.tagify-email-list .tagify__tag:hover .tagify__tag__removeBtn {
transform: none;
opacity: 1;
margin-left: -1ch;
}
.tagify-email-list .tagify__input {
display: none;
}
.tagify__tag > div {
border-radius: 50rem;
}
[dir=rtl] .tagify-email-list .tagify__tag {
margin: 0 0.4375rem 0.4375rem 0;
}
[dir=rtl] .tagify-email-list .tagify__tag:hover .tagify__tag__removeBtn {
margin-left: auto;
margin-right: -1ch;
}
[dir=rtl] .tagify-email-list .tagify__tag__removeBtn {
transform: translateX(6px) scale(0.5);
margin-left: auto;
margin-right: -3ch;
}
.light-style .tagify-email-list .tagify__tag--editable:not(.tagify--invalid) > div::before {
box-shadow: 0 0 0 2px #e4e6e8 inset !important;
}
.dark-style .tagify-email-list .tagify__tag--editable:not(.tagify--invalid) > div::before {
box-shadow: 0 0 0 2px #4e4f6c inset !important;
}
.tagify.form-control {
transition: none;
display: flex;
align-items: flex-end;
/* padding: calc(2px - var(--bs-border-width)) 0.4375rem 0.4231rem !important; */
padding: calc(2px - var(--bs-border-width)) 0.4375rem 0.2rem !important;
}
.fv-plugins-bootstrap5-row-invalid .tagify.form-control {
padding: 0 calc(0.4375rem - var(--bs-border-width)) calc(0.4375rem - 2px) !important;
}
.tagify.tagify--focus, .tagify.form-control:focus {
padding: 0 calc(0.4375rem - var(--bs-border-width)) 0.3606rem !important;
border-width: 2px;
}
.tagify__tag, .tagify__input {
margin: 0.1875rem 0.625rem 0 0 !important;
line-height: 1;
}
.tagify__input {
line-height: 1.5rem;
}
.tagify__input:empty::before {
top: 4px;
}
.tagify__tag > div {
line-height: 1.5rem;
padding: 0 0 0 0.4375rem;
}
.tagify__tag__removeBtn {
margin-right: 0.1375rem;
margin-left: 0.21875rem;
font-family: "boxicons";
font-size: 1rem;
opacity: 0.7;
}
.tagify__tag__removeBtn:hover {
background: none;
color: #ff2804 !important;
}
.tagify__tag__removeBtn::after {
content: "\ef06";
}
.tagify__tag:hover:not([readonly]) div::before, .tagify__tag:focus div::before {
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
}
.tagify__dropdown {
transform: translateY(0);
}
.tagify[readonly]:not(.tagify--mix) .tagify__tag > div {
padding: 0 0.4375rem 0 0.4375rem !important;
}
.tagify__input {
padding: 0;
}
.tagify__tag-text {
font-size: 0.8125rem;
font-weight: 500;
}
.tagify.form-control {
padding-top: 0.1412rem !important;
}
.tagify.tagify--focus, .tagify.form-control:focus {
padding-top: calc(0.1412rem - 1px) !important;
}
.tagify__tag__removeBtn {
margin-inline-end: 0.3rem;
}
[dir=rtl] .tagify__tag, [dir=rtl] .tagify__input {
margin: 0.4375rem 0 0 0.4375rem;
}
[dir=rtl] .tagify + input,
[dir=rtl] .tagify + textarea {
left: 0;
right: -9999em !important;
}
[dir=rtl] .tagify__tag > div {
padding: 0 0.6875rem 0 0;
}
[dir=rtl] .tagify__tag__removeBtn {
margin-left: 0.4375rem;
margin-right: 0.21875rem;
}
.light-style .tagify__tag > div::before {
box-shadow: 0 0 0 1.3em rgba(34, 48, 62, 0.08) inset;
}
.light-style .tagify__tag .tagify__tag-text {
color: #384551;
}
.light-style .tagify__tag:hover:not([readonly]) div::before, .light-style .tagify__tag:focus div::before {
box-shadow: 0 0 0 1.3em rgba(34, 48, 62, 0.12) inset;
}
.light-style .tagify__tag__removeBtn {
color: #7a838b;
}
.light-style .tagify__tag__removeBtn:hover + div::before {
background: rgba(255, 62, 29, 0.3);
}
.light-style .tagify:hover:not([readonly]) {
border-color: #ced1d5;
}
.light-style .tagify__input::before {
color: #a7acb2 !important;
}
.light-style .tagify__dropdown {
box-shadow: 0 0.25rem 0.75rem 0 rgba(34, 48, 62, 0.14);
border-top-color: #e4e6e8;
}
.light-style .tagify__dropdown__wrapper {
background: #fff;
border-color: #e4e6e8;
}
.dark-style .tagify__tag > div::before {
box-shadow: 0 0 0 1.3em rgba(230, 230, 241, 0.08) inset;
}
.dark-style .tagify__tag > div .tagify__tag-text {
color: #d5d5e2;
}
.dark-style .tagify__tag:hover:not([readonly]) div::before, .dark-style .tagify__tag:focus div::before {
box-shadow: 0 0 0 1.3em rgba(230, 230, 241, 0.12) inset;
}
.dark-style .tagify__tag__removeBtn {
color: #a1a1b5;
}
.dark-style .tagify__tag__removeBtn:hover + div::before {
background: rgba(255, 62, 29, 0.3);
}
.dark-style .tagify:hover:not([readonly]) {
border-color: #5f607b;
}
.dark-style .tagify__input::before {
color: #7e7f96 !important;
}
.dark-style .tagify[readonly]:not(.tagify--mix) .tagify__tag > div::before {
background: linear-gradient(45deg, #5f607b 25%, transparent 25%, transparent 50%, #5f607b 50%, #5f607b 75%, transparent 75%, transparent) 0/5px 5px;
}
.dark-style .tagify[readonly]:not(.tagify--mix):not(.tagify--select) .tagify__tag > div::before {
animation: none;
box-shadow: none;
}
.dark-style .tagify__dropdown {
box-shadow: 0 0.25rem 0.75rem 0 rgba(20, 20, 29, 0.24);
border-top-color: #4e4f6c;
}
.dark-style .tagify__dropdown__wrapper {
box-shadow: 0 0.25rem 0.75rem 0 rgba(20, 20, 29, 0.24);
background: #2b2c40;
border-color: #4e4f6c;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
public/img/hero/bg-01.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

BIN
public/img/hero/bg-02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

BIN
public/img/hero/bg-03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
public/img/hero/bg-04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

View File

Before

Width:  |  Height:  |  Size: 860 KiB

After

Width:  |  Height:  |  Size: 860 KiB

BIN
public/img/icons/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,5 @@
<svg width="65" height="65" viewBox="0 0 65 65" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.2" d="M46.5001 10.5288H32.5001L20.2251 26.5288L32.5001 56.5288L60.5001 26.5288L46.5001 10.5288Z" fill="#03C3EC"/>
<path d="M18.5 10.5288H46.5L60.5 26.5288L32.5 56.5288L4.5 26.5288L18.5 10.5288Z" stroke="#03C3EC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.2934 9.92012C33.1042 9.67343 32.8109 9.52881 32.5 9.52881C32.1891 9.52881 31.8958 9.67343 31.7066 9.92012L19.7318 25.5288H4.5C3.94772 25.5288 3.5 25.9765 3.5 26.5288C3.5 27.0811 3.94772 27.5288 4.5 27.5288H19.5537L31.5745 56.9075C31.7282 57.2833 32.094 57.5288 32.5 57.5288C32.906 57.5288 33.2718 57.2833 33.4255 56.9075L45.4463 27.5288H60.5C61.0523 27.5288 61.5 27.0811 61.5 26.5288C61.5 25.9765 61.0523 25.5288 60.5 25.5288H45.2682L33.2934 9.92012ZM42.7474 25.5288L32.5 12.1717L22.2526 25.5288H42.7474ZM21.7146 27.5288L32.5 53.8881L43.2854 27.5288H21.7146Z" fill="#03C3EC"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/img/icons/report.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

View File

@ -1,42 +0,0 @@
<svg width="25" viewBox="0 0 25 42" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path
d="M13.7918663,0.358365126 L3.39788168,7.44174259 C0.566865006,9.69408886 -0.379795268,12.4788597 0.557900856,15.7960551 C0.68998853,16.2305145 1.09562888,17.7872135 3.12357076,19.2293357 C3.8146334,19.7207684 5.32369333,20.3834223 7.65075054,21.2172976 L7.59773219,21.2525164 L2.63468769,24.5493413 C0.445452254,26.3002124 0.0884951797,28.5083815 1.56381646,31.1738486 C2.83770406,32.8170431 5.20850219,33.2640127 7.09180128,32.5391577 C8.347334,32.0559211 11.4559176,30.0011079 16.4175519,26.3747182 C18.0338572,24.4997857 18.6973423,22.4544883 18.4080071,20.2388261 C17.963753,17.5346866 16.1776345,15.5799961 13.0496516,14.3747546 L10.9194936,13.4715819 L18.6192054,7.984237 L13.7918663,0.358365126 Z"
id="path-1"></path>
<path
d="M5.47320593,6.00457225 C4.05321814,8.216144 4.36334763,10.0722806 6.40359441,11.5729822 C8.61520715,12.571656 10.0999176,13.2171421 10.8577257,13.5094407 L15.5088241,14.433041 L18.6192054,7.984237 C15.5364148,3.11535317 13.9273018,0.573395879 13.7918663,0.358365126 C13.5790555,0.511491653 10.8061687,2.3935607 5.47320593,6.00457225 Z"
id="path-3"></path>
<path
d="M7.50063644,21.2294429 L12.3234468,23.3159332 C14.1688022,24.7579751 14.397098,26.4880487 13.008334,28.506154 C11.6195701,30.5242593 10.3099883,31.790241 9.07958868,32.3040991 C5.78142938,33.4346997 4.13234973,34 4.13234973,34 C4.13234973,34 2.75489982,33.0538207 2.37032616e-14,31.1614621 C-0.55822714,27.8186216 -0.55822714,26.0572515 -4.05231404e-15,25.8773518 C0.83734071,25.6075023 2.77988457,22.8248993 3.3049379,22.52991 C3.65497346,22.3332504 5.05353963,21.8997614 7.50063644,21.2294429 Z"
id="path-4"></path>
<path
d="M20.6,7.13333333 L25.6,13.8 C26.2627417,14.6836556 26.0836556,15.9372583 25.2,16.6 C24.8538077,16.8596443 24.4327404,17 24,17 L14,17 C12.8954305,17 12,16.1045695 12,15 C12,14.5672596 12.1403557,14.1461923 12.4,13.8 L17.4,7.13333333 C18.0627417,6.24967773 19.3163444,6.07059163 20.2,6.73333333 C20.3516113,6.84704183 20.4862915,6.981722 20.6,7.13333333 Z"
id="path-5"></path>
</defs>
<g id="g-app-brand" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Brand-Logo" transform="translate(-27.000000, -15.000000)">
<g id="Icon" transform="translate(27.000000, 15.000000)">
<g id="Mask" transform="translate(0.000000, 8.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use fill="#696cff" xlink:href="#path-1"></use>
<g id="Path-3" mask="url(#mask-2)">
<use fill="#696cff" xlink:href="#path-3"></use>
<use fill-opacity="0.2" fill="#FFFFFF" xlink:href="#path-3"></use>
</g>
<g id="Path-4" mask="url(#mask-2)">
<use fill="#696cff" xlink:href="#path-4"></use>
<use fill-opacity="0.2" fill="#FFFFFF" xlink:href="#path-4"></use>
</g>
</g>
<g id="Triangle"
transform="translate(19.000000, 11.000000) rotate(-300.000000) translate(-19.000000, -11.000000) ">
<use fill="#696cff" xlink:href="#path-5"></use>
<use fill-opacity="0.2" fill="#FFFFFF" xlink:href="#path-5"></use>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@ -1,19 +1,28 @@
import React, { useEffect } from "react";
import { useOrganizationModal } from "./hooks/useOrganization";
import OrganizationModal from "./components/Organization/OrganizationModal";
import { useAuthModal } from "./hooks/useAuth";
import { useAuthModal, useModal } from "./hooks/useAuth";
import SwitchTenant from "./pages/authentication/SwitchTenant";
import ChangePasswordPage from "./pages/authentication/ChangePassword";
import NewCollection from "./components/collections/ManageCollection";
import ServiceProjectTeamAllocation from "./components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation";
const ModalProvider = () => {
const { isOpen, onClose } = useOrganizationModal();
const { isOpen: isAuthOpen } = useAuthModal();
const { isOpen: isChangePass } = useModal("ChangePassword");
const { isOpen: isCollectionNew } = useModal("newCollection");
const { isOpen: isServiceTeamAllocation } = useModal("ServiceTeamAllocation");
return (
<>
{isOpen && <OrganizationModal />}
{isAuthOpen && <SwitchTenant />}
{isChangePass && <ChangePasswordPage />}
{isCollectionNew && <NewCollection />}
{isServiceTeamAllocation && <ServiceProjectTeamAllocation />}
</>
);
};
export default ModalProvider;
export default ModalProvider;

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -72,7 +72,7 @@
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-root-font-size: 16px;
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 0.9375rem;
--bs-body-font-size: 0.875rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.375;
--bs-body-color: #646e78;

View File

@ -123,12 +123,15 @@ const AttendLogs = ({ Id }) => {
}, []);
return (
<div className="table-responsive">
<div className="text-start">
<div className="mb-3">
<h5 className="mb-4">Attendance Logs</h5>
{logs && !loading && (
<p>
Attendance logs for{" "}
{logs[0]?.employee?.firstName + " " + logs[0]?.employee?.lastName}{" "}
on {formatUTCToLocalTime(logs[0]?.activityTime)}
<p className="mb-0 text-start">
Showing logs for{" "}
<strong>
{logs[0]?.employee?.firstName + " " + logs[0]?.employee?.lastName}
</strong>{" "}
on <strong>{formatUTCToLocalTime(logs[0]?.activityTime)}</strong>
</p>
)}
</div>
@ -142,9 +145,9 @@ const AttendLogs = ({ Id }) => {
<table className="table table-sm mb-0">
<thead>
<tr>
<th>Activity</th>
<th>Date</th>
<th>Time</th>
<th>Activity</th>
<th>Location</th>
<th>Recored By</th>
<th>Description</th>
@ -156,11 +159,16 @@ const AttendLogs = ({ Id }) => {
.sort((a, b) => b.id - a.id)
.map((log, index) => (
<tr key={index}>
<td>{formatUTCToLocalTime(log.activityTime)}</td>
<td>{convertShortTime(log.activityTime)}</td>
<td>
{whichActivityPerform(log.activity, log.activityTime)}
</td>
<td>
<div className="py-2">
{formatUTCToLocalTime(log.activityTime)}
</div>
</td>
<td>{convertShortTime(log.activityTime)}</td>
<td>
{log?.latitude != 0 ? (
<i
@ -179,9 +187,8 @@ const AttendLogs = ({ Id }) => {
)}
</td>
<td className="text-wrap">
{`${log?.updatedByEmployee?.firstName ?? ""} ${
log?.updatedByEmployee?.lastName ?? ""
}`}
{`${log?.updatedByEmployee?.firstName ?? ""} ${log?.updatedByEmployee?.lastName ?? ""
}`}
</td>
<td className="text-wrap" colSpan={3}>
{log?.comment?.length > 50

View File

@ -11,8 +11,9 @@ import { useSelector } from "react-redux";
import { useQueryClient } from "@tanstack/react-query";
import eventBus from "../../services/eventBus";
import { useSelectedProject } from "../../slices/apiDataManager";
import { SpinnerLoader } from "../common/Loader";
const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizationId, includeInactive, date }) => {
const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizationId, }) => {
const queryClient = useQueryClient();
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
@ -24,7 +25,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
loading: attLoading,
recall: attrecall,
isFetching
} = useAttendance(selectedProject, organizationId, includeInactive, date);
} = useAttendance(selectedProject, organizationId);
const filteredAttendance = ShowPending
? attendance?.filter(
(att) => att?.checkInTime !== null && att?.checkOutTime === null
@ -110,28 +111,41 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
return (
<>
<div className="d-flex justify-content-between align-items-center py-2 px-2">
{/* Left side - Date */}
<div className="text-start">
<strong>Date: {formatUTCToLocalTime(todayDate)}</strong>
</div>
{/* Right side - Pending Attendance toggle */}
<div className="form-check form-switch m-0">
<input
type="checkbox"
className="form-check-input"
role="switch"
id="inactiveEmployeesCheckbox"
disabled={isFetching}
checked={ShowPending}
onChange={(e) => setShowPending(e.target.checked)}
/>
<label className="form-check-label" htmlFor="inactiveEmployeesCheckbox">
Pending Attendance
</label>
</div>
</div>
<div
className="table-responsive text-nowrap h-100"
className="table-responsive modal-min-h text-nowrap h-100"
style={{ minHeight: "200px" }} // Ensures fixed height
>
<div className="d-flex text-start align-items-center py-2">
<strong>Date : {formatUTCToLocalTime(todayDate)}</strong>
<div className="form-check form-switch text-start m-0 ms-5">
<input
type="checkbox"
className="form-check-input"
role="switch"
id="inactiveEmployeesCheckbox"
disabled={isFetching}
checked={ShowPending}
onChange={(e) => setShowPending(e.target.checked)}
/>
<label className="form-check-label ms-0">Show Pending</label>
</div>
</div>
{attLoading ? (
<div>Loading...</div>
<div
className="d-flex justify-content-center align-items-center"
style={{ minHeight: "50vh" }}
>
<SpinnerLoader />
</div>
) : currentItems?.length > 0 ? (
<>
<table className="table ">
<thead>
@ -223,50 +237,6 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
)}
</tbody>
</table>
{!loading && finalFilteredData.length > ITEMS_PER_PAGE && (
<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)}
>
&laquo;
</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)}
>
&raquo;
</button>
</li>
</ul>
</nav>
)}
</>
) : (
<div
@ -281,6 +251,48 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
</div>
)}
</div>
{!loading && finalFilteredData.length > ITEMS_PER_PAGE && (
<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)}
>
&laquo;
</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)}
>
&raquo;
</button>
</li>
</ul>
</nav>
)}
</>
);
};

View File

@ -1,16 +1,22 @@
import React, { useEffect, useState, useMemo, useCallback } from "react";
import moment from "moment";
import Avatar from "../common/Avatar";
import { convertShortTime } from "../../utils/dateUtils";
import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils";
import RenderAttendanceStatus from "./RenderAttendanceStatus";
import { useSelector, useDispatch } from "react-redux";
import DateRangePicker from "../common/DateRangePicker";
import { clearCacheKey, getCachedData, useSelectedProject } from "../../slices/apiDataManager";
import {
clearCacheKey,
getCachedData,
useSelectedProject,
} from "../../slices/apiDataManager";
import eventBus from "../../services/eventBus";
import AttendanceRepository from "../../repositories/AttendanceRepository";
import { useAttendancesLogs } from "../../hooks/useAttendance";
import { queryClient } from "../../layouts/AuthLayout";
import { ITEMS_PER_PAGE } from "../../utils/constants";
import { useNavigate } from "react-router-dom";
import { SpinnerLoader } from "../common/Loader";
const usePagination = (data, itemsPerPage) => {
const [currentPage, setCurrentPage] = useState(1);
@ -33,18 +39,14 @@ const usePagination = (data, itemsPerPage) => {
};
};
const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
// const selectedProject = useSelector(
// (store) => store.localVariables.projectId
// );
const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
const selectedProject = useSelectedProject();
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
const dispatch = useDispatch();
const [loading, setLoading] = useState(false);
const [showPending, setShowPending] = useState(false)
const [showPending, setShowPending] = useState(false);
const [isRefreshing, setIsRefreshing] = useState(false);
const [processedData, setProcessedData] = useState([]);
const navigate = useNavigate();
const today = new Date();
today.setHours(0, 0, 0, 0);
@ -67,57 +69,32 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
};
const sortByName = (a, b) => {
const nameA = a.firstName.toLowerCase() + a.lastName.toLowerCase();
const nameB = b.firstName.toLowerCase() + b.lastName.toLowerCase();
return nameA?.localeCompare(nameB);
const nameA = (a.firstName + a.lastName).toLowerCase();
const nameB = (b.firstName + b.lastName).toLowerCase();
return nameA.localeCompare(nameB);
};
const {
data = [],
isLoading,
error,
refetch,
isFetching,
} = useAttendancesLogs(
const { data = [], isLoading, error, refetch, isFetching } = useAttendancesLogs(
selectedProject,
dateRange.startDate,
dateRange.endDate,
organizationId
);
const filtering = (data) => {
const processedData = useMemo(() => {
const filteredData = showPending
? data.filter((item) => item.checkOutTime === null)
: data;
const group1 = filteredData
.filter((d) => d.activity === 1 && isSameDay(d.checkInTime))
.sort(sortByName);
const group2 = filteredData
.filter((d) => d.activity === 4 && isSameDay(d.checkOutTime))
.sort(sortByName);
const group3 = filteredData
.filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime))
.sort(sortByName);
const group4 = filteredData.filter(
(d) => d.activity === 4 && isBeforeToday(d.checkOutTime)
);
const group5 = filteredData
.filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime))
.sort(sortByName);
const group6 = filteredData
.filter((d) => d.activity === 5)
.sort(sortByName);
const group1 = filteredData.filter((d) => d.activity === 1 && isSameDay(d.checkInTime)).sort(sortByName);
const group2 = filteredData.filter((d) => d.activity === 4 && isSameDay(d.checkOutTime)).sort(sortByName);
const group3 = filteredData.filter((d) => d.activity === 1 && isBeforeToday(d.checkInTime)).sort(sortByName);
const group4 = filteredData.filter((d) => d.activity === 4 && isBeforeToday(d.checkOutTime));
const group5 = filteredData.filter((d) => d.activity === 2 && isBeforeToday(d.checkOutTime)).sort(sortByName);
const group6 = filteredData.filter((d) => d.activity === 5).sort(sortByName);
const sortedList = [
...group1,
...group2,
...group3,
...group4,
...group5,
...group6,
];
const sortedList = [...group1, ...group2, ...group3, ...group4, ...group5, ...group6];
// Group by date
const groupedByDate = sortedList.reduce((acc, item) => {
const date = (item.checkInTime || item.checkOutTime)?.split("T")[0];
if (date) {
@ -127,57 +104,19 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
return acc;
}, {});
const sortedDates = Object.keys(groupedByDate).sort(
(a, b) => new Date(b) - new Date(a)
);
const finalData = sortedDates.flatMap((date) => groupedByDate[date]);
setProcessedData(finalData);
};
useEffect(() => {
filtering(data);
const sortedDates = Object.keys(groupedByDate).sort((a, b) => new Date(b) - new Date(a));
return sortedDates.flatMap((date) => groupedByDate[date]);
}, [data, showPending]);
// New useEffect to handle search filtering
const filteredSearchData = useMemo(() => {
if (!searchTerm) {
return processedData;
}
const lowercasedSearchTerm = searchTerm.toLowerCase();
return processedData.filter((item) => {
const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
return fullName.includes(lowercasedSearchTerm);
});
if (!searchTerm) return processedData;
const lowercased = searchTerm.toLowerCase();
return processedData.filter((item) =>
`${item.firstName} ${item.lastName}`.toLowerCase().includes(lowercased)
);
}, [processedData, searchTerm]);
// const filteredSearchData = useMemo(() => {
// let tempData = processedData;
// if (searchTerm) {
// const lowercasedSearchTerm = searchTerm.toLowerCase();
// tempData = tempData.filter((item) => {
// const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
// return fullName.includes(lowercasedSearchTerm);
// });
// }
// if (filters?.selectedOrganization) {
// tempData = tempData.filter(
// (item) => item.organization?.name === filters.selectedOrganization
// );
// }
// if (filters?.selectedServices?.length > 0) {
// tempData = tempData.filter((item) =>
// filters.selectedServices.includes(item.service?.name)
// );
// }
// return tempData;
// }, [processedData, searchTerm, filters]);
const {
currentPage,
totalPages,
@ -188,34 +127,27 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
useEffect(() => {
resetPage();
}, [filteredSearchData, resetPage]);
}, [filteredSearchData]);
const handler = useCallback(
(msg) => {
const { startDate, endDate } = dateRange;
const checkIn = msg.response.checkInTime.substring(0, 10);
if (
selectedProject === msg.projectId &&
startDate <= checkIn &&
checkIn <= endDate
) {
if (selectedProject === msg.projectId && startDate <= checkIn && checkIn <= endDate) {
queryClient.setQueriesData(["attendanceLogs"], (oldData) => {
if (!oldData) {
queryClient.invalidateQueries({ queryKey: ["attendanceLogs"] });
return;
}
const updatedAttendance = oldData.map((record) =>
record.id === msg.response.id
? { ...record, ...msg.response }
: record
return oldData.map((record) =>
record.id === msg.response.id ? { ...record, ...msg.response } : record
);
filtering(updatedAttendance);
return updatedAttendance;
});
resetPage();
}
},
[selectedProject, dateRange, filtering, resetPage]
[selectedProject, dateRange, resetPage]
);
useEffect(() => {
@ -227,18 +159,10 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
(msg) => {
const { startDate, endDate } = dateRange;
if (data.some((item) => item.employeeId == msg.employeeId)) {
// dispatch(
// fetchAttendanceData({
// ,
// fromDate: startDate,
// toDate: endDate,
// })
// );
refetch()
refetch();
}
},
[selectedProject, dateRange, data, refetch]
[data, refetch]
);
useEffect(() => {
@ -246,38 +170,56 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
return () => eventBus.off("employee", employeeHandler);
}, [employeeHandler]);
return (
<>
<div
className="dataTables_length text-start py-2 d-flex justify-content-between"
className="dataTables_length text-start py-2 d-flex flex-wrap justify-content-between align-items-center"
id="DataTables_Table_0_length"
>
<div className="d-flex align-items-center my-0 ">
{/* Left Side - Date Picker */}
<div className="d-flex align-items-center">
<DateRangePicker
onRangeChange={setDateRange}
defaultStartDate={yesterday}
/>
<div className="form-check form-switch text-start m-0 ms-5">
<input
type="checkbox"
className="form-check-input"
role="switch"
disabled={isFetching}
id="inactiveEmployeesCheckbox"
checked={showPending}
onChange={(e) => setShowPending(e.target.checked)}
/>
<label className="form-check-label ms-0">Show Pending</label>
</div>
</div>
{/* Right Side - Pending Attendance Switch */}
<div className="form-check form-switch d-flex align-items-center mb-2">
<input
type="checkbox"
className="form-check-input"
role="switch"
id="inactiveEmployeesCheckbox"
disabled={isFetching}
checked={showPending}
onChange={(e) => setShowPending(e.target.checked)}
/>
<label
className="form-check-label ms-2"
htmlFor="inactiveEmployeesCheckbox"
>
Pending Attendance
</label>
</div>
</div>
<div className="table-responsive text-nowrap" style={{ minHeight: "200px" }}>
<div
className="table-responsive modal-min-h text-nowrap"
style={{ minHeight: "200px" }}
>
{isLoading ? (
<div className="d-flex justify-content-center align-items-center" style={{ height: "200px" }}>
<p className="text-secondary">Loading...</p>
<div
className="d-flex justify-content-center align-items-center"
style={{ minHeight: "50vh" }}
>
<SpinnerLoader />
</div>
) : filteredSearchData?.length > 0 ? (
<table className="table mb-0">
<thead>
<tr>
@ -287,7 +229,8 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
<th className="border-top-1">Date</th>
<th>Organization</th>
<th>
<i className="bx bxs-down-arrow-alt text-success"></i> Check-In
<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
@ -315,8 +258,8 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
className="table-row-header"
>
<td colSpan={8} className="text-start">
<strong>
{moment(currentDate).format("DD-MM-YYYY")}
<strong className="d-inline-block my-1 ms-2">
{formatUTCToLocalTime(currentDate)}
</strong>
</td>
</tr>
@ -331,7 +274,12 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
lastName={attendance.lastName}
/>
<div className="d-flex flex-column">
<a href="#" className="text-heading text-truncate">
<a
onClick={() =>
navigate(`/employee/${attendance.employeeId}?for=attendance`)
}
className="text-heading text-truncate cursor-pointer"
>
<span className="fw-normal">
{attendance.firstName} {attendance.lastName}
</span>
@ -344,7 +292,7 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
attendance.checkInTime || attendance.checkOutTime
).format("DD-MMM-YYYY")}
</td>
<td>{attendance.organizationName || "--"}</td>
<td>{attendance.organizationName || "--"}</td>
<td>{convertShortTime(attendance.checkInTime)}</td>
<td>
{attendance.checkOutTime
@ -366,7 +314,11 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => {
</tbody>
</table>
) : (
<div className="my-12"><span className="text-secondary">No data available for the selected date range. Please Select another date.</span></div>
<div className="my-12">
<span className="text-secondary">
No attendance record found in selected date range.
</span>
</div>
)}
</div>
{paginatedAttendances?.length == 0 && filteredSearchData?.length > 0 && (

View File

@ -33,7 +33,7 @@ const createSchema = (modeldata) => {
const checkOut = new Date(checkIn);
checkOut.setHours(hour, minute, 0, 0);
return checkOut > checkIn;
return checkOut >= checkIn;
}
return true;
}, {
@ -96,12 +96,12 @@ const CheckInCheckOut = ({ modeldata, closeModal, handleSubmitForm }) => {
};
return (
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 d-flex justify-content-center">
<label className="fs-5 text-dark text-center">
<form className="row p-2" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 d-flex justify-content-center mb-4">
<label className="fs-5 tex-semibold text-center">
{modeldata?.checkInTime && !modeldata?.checkOutTime
? "Check-out :"
: "Check-in :"}
? "Check-Out "
: "Check-In "}
</label>
</div>
@ -120,7 +120,7 @@ const CheckInCheckOut = ({ modeldata, closeModal, handleSubmitForm }) => {
modeldata?.checkInTime && !modeldata?.checkOutTime
? formatDate(modeldata?.checkInTime?.split("T")[0]) || ""
: formatDate(today)
}
}
disabled
/>
</div>

View File

@ -24,8 +24,7 @@ import { useProfile } from "../../hooks/useProfile";
import { refreshData, setProjectId } from "../../slices/localVariablesSlice";
import InfraTable from "../Project/Infrastructure/InfraTable";
import { useSelectedProject } from "../../slices/apiDataManager";
import Loader from "../common/Loader";
import { SpinnerLoader } from "../common/Loader";
const InfraPlanning = () => {
const { profile: LoggedUser, refetch: fetchData } = useProfile();
const dispatch = useDispatch();
@ -33,7 +32,7 @@ const InfraPlanning = () => {
const selectedService = useCurrentService();
const { projectInfra, isLoading, isError, error, isFetched } =
useProjectInfra(selectedProject, selectedService || "" );
useProjectInfra(selectedProject, selectedService || "");
const canManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
const canApproveTask = useHasUserPermission(APPROVE_TASK);
@ -57,14 +56,25 @@ const InfraPlanning = () => {
}
if (isLoading) {
return <Loader />;
return (
<div
className="d-flex justify-content-center align-items-center"
style={{ height: "60vh" }}
>
<SpinnerLoader />
</div>
);
}
if (isFetched && (!projectInfra || projectInfra.length === 0)) {
return (
<div className="text-center">
<p className="my-3">No Result Found</p>
<div
className="text-center d-flex justify-content-center align-items-center text-muted"
style={{ minHeight: "40vh", fontSize: "0.9rem" }}
>
<p className="my-3 m-0">No Result Found</p>
</div>
);
}

View File

@ -1,25 +1,46 @@
import React, { useCallback, useEffect, useState, useMemo } from "react";
import Avatar from "../common/Avatar";
import { convertShortTime } from "../../utils/dateUtils";
import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils";
import RegularizationActions from "./RegularizationActions";
import { useSelector } from "react-redux";
import { useRegularizationRequests } from "../../hooks/useAttendance";
import moment from "moment";
import usePagination from "../../hooks/usePagination";
import eventBus from "../../services/eventBus";
import { cacheData, clearCacheKey, useSelectedProject } from "../../slices/apiDataManager";
import {
cacheData,
clearCacheKey,
useSelectedProject,
} from "../../slices/apiDataManager";
import { useQueryClient } from "@tanstack/react-query";
import Pagination from "../../components/common/Pagination";
import { useNavigate } from "react-router-dom";
import { SpinnerLoader } from "../common/Loader";
const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, IncludeInActive }) => {
const Regularization = ({
handleRequest,
searchTerm,
projectId,
organizationId,
IncludeInActive,
}) => {
const queryClient = useQueryClient();
// var selectedProject = useSelector((store) => store.localVariables.projectId);
const selectedProject = useSelectedProject();
const [regularizesList, setregularizedList] = useState([]);
const { regularizes, loading, error, refetch } =
useRegularizationRequests(selectedProject, organizationId, IncludeInActive);
const navigate = useNavigate();
const { regularizes, loading, error, refetch } = useRegularizationRequests(
selectedProject,
organizationId,
IncludeInActive
);
useEffect(() => {
setregularizedList(regularizes);
if (!regularizes) return
if (regularizes?.length) {
setregularizedList(regularizes);
}
}, [regularizes]);
const sortByName = (a, b) => {
@ -54,48 +75,15 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
}
const lowercasedSearchTerm = searchTerm.toLowerCase();
return sortedList.filter((item) => {
const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
const fullName = `${item?.firstName} ${item?.lastName}`.toLowerCase();
return fullName.includes(lowercasedSearchTerm);
});
}, [regularizesList, searchTerm]);
// const filteredSearchData = useMemo(() => {
// let sortedList = [...regularizesList].sort(sortByName);
// // Search filter
// if (searchTerm) {
// const lowercasedSearchTerm = searchTerm.toLowerCase();
// sortedList = sortedList.filter((item) => {
// const fullName = `${item.firstName} ${item.lastName}`.toLowerCase();
// return fullName.includes(lowercasedSearchTerm);
// });
// }
// // Organization filter
// if (filters?.selectedOrganization) {
// sortedList = sortedList.filter(
// (item) => item.organization?.name === filters.selectedOrganization
// );
// }
// // Services filter
// if (filters?.selectedServices?.length > 0) {
// sortedList = sortedList.filter((item) =>
// filters.selectedServices.includes(item.service?.name)
// );
// }
// return sortedList;
// }, [regularizesList, searchTerm, filters]);
const { currentPage, totalPages, currentItems, paginate } =
usePagination(filteredSearchData, 20);
// Reset pagination when the search term or data changes
useEffect(() => {
}, [filteredSearchData]);
const { currentPage, totalPages, currentItems, paginate } = usePagination(
filteredSearchData,
20
);
useEffect(() => {
eventBus.on("regularization", handler);
@ -117,116 +105,114 @@ const Regularization = ({ handleRequest, searchTerm,projectId, organizationId, I
}, [employeeHandler]);
return (
<div className="table-responsive text-nowrap pb-4" style={{ minHeight: "200px" }}>
{loading ? (
<div className="d-flex justify-content-center align-items-center" style={{ height: "200px" }}>
<p className="text-secondary">Loading...</p>
</div>
) : currentItems?.length > 0 ? (
<table className="table mb-0">
<thead>
<tr>
<th colSpan={2}>Name</th>
<th>Date</th>
<th>Organization</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>
{currentItems?.map((att, index) => (
<tr key={index}>
<td colSpan={2}>
<div className="d-flex justify-content-start align-items-center">
<Avatar
firstName={att.firstName}
lastName={att.lastName}
/>
<div className="d-flex flex-column">
<a href="#" className="text-heading text-truncate">
<div>
<div
className="table-responsive modal-min-h pt-3 text-nowrap pb-4"
style={{ minHeight: "200px" }}
>
{loading ? (
<div
className="d-flex justify-content-center align-items-center"
style={{ minHeight: "50vh" }}
>
<SpinnerLoader />
</div>
) : currentItems?.length > 0 ? (
<table className="table mb-0">
<thead>
<tr>
<th colSpan={2}>Name</th>
<th>Date</th>
<th>Organization</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 colSpan={2}>
Requested By
</th>
<th >
Requested At
</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{currentItems?.map((att, index) => (
<tr key={index}>
<td colSpan={2}>
<div className="d-flex justify-content-start align-items-center">
<Avatar firstName={att.firstName} lastName={att.lastName} />
<div className="d-flex flex-column"> <a
onClick={() =>
navigate(`/employee/${att.employeeId}?for=attendance`)
}
className="text-heading text-truncate cursor-pointer" >
<span className="fw-normal">
{att.firstName} {att.lastName}
</span>
</a>
</div>
</div>
</div>
</td>
<td>{moment(att.checkOutTime).format("DD-MMM-YYYY")}</td>
</td>
<td>{moment(att.checkOutTime).format("DD-MMM-YYYY")}</td>
<td>{att.organizationName || "--"}</td>
<td>{att.organizationName || "--"}</td>
<td>{convertShortTime(att.checkInTime)}</td>
<td>
{att.checkOutTime ? convertShortTime(att.checkOutTime) : "--"}
</td>
<td className="text-center ">
<RegularizationActions
attendanceData={att}
handleRequest={handleRequest}
refresh={refetch}
/>
</td>
</tr>
))}
</tbody>
</table>
<td>{convertShortTime(att.checkInTime)}</td>
<td>
{att.requestedAt ? convertShortTime(att.checkOutTime) : "--"}
</td>
) : (
<div
className="d-flex justify-content-center align-items-center"
style={{ height: "200px" }}
>
<span className="text-secondary">
{searchTerm
? "No results found for your search."
: "No Requests Found !"}
</span>
</div>
)}
{!loading && totalPages > 1 && (
<nav aria-label="Page ">
<ul className="pagination pagination-sm justify-content-end py-1 mt-3">
<li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}>
<button
className="page-link btn-xs"
onClick={() => paginate(currentPage - 1)}
>
&laquo;
</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)}
>
&raquo;
</button>
</li>
</ul>
</nav>
<td colSpan={2}>
{att.requestedBy ? (<div className="d-flex justify-content-start align-items-center">
<Avatar firstName={att?.requestedBy?.firstName} lastName={att?.requestedBy?.lastName} />
<div className="d-flex flex-column">
<a href="#" className="text-heading text-truncate">
<span className="fw-normal">
{att?.requestedBy?.firstName} {att?.requestedBy?.lastName}
</span>
</a>
</div>
</div>) : (<small>--</small>)}
</td>
<td>
{att?.requestedAt ? formatUTCToLocalTime(att.requestedAt, true) : "--"}
</td>
<td className="text-center ">
<RegularizationActions
attendanceData={att}
handleRequest={handleRequest}
refresh={refetch}
/>
</td>
</tr>
))}
</tbody>
</table>
) : (
<div
className="d-flex justify-content-center align-items-center"
style={{ height: "200px" }}
>
<span className="text-secondary">
{searchTerm
? "No results found for your search."
: "No Requests Found !"}
</span>
</div>
)}
</div>
{totalPages > 0 && (
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={paginate}
/>
)}
</div>
);
};

View File

@ -0,0 +1,233 @@
import React, { useEffect, useMemo } from "react";
import { useExpenseAllTransactionsList, useExpenseTransactions } from "../../hooks/useExpense";
import Error from "../common/Error";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
import Loader, { SpinnerLoader } from "../common/Loader";
import { useForm, useFormContext } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { employee } from "../../data/masters";
import { useAdvancePaymentContext } from "../../pages/AdvancePayment/AdvancePaymentPage";
import { formatFigure } from "../../utils/appUtils";
const AdvancePaymentList = ({ employeeId, searchString }) => {
const { setBalance } = useAdvancePaymentContext();
const { data, isError, isLoading, error, isFetching } =
useExpenseTransactions(employeeId, { enabled: !!employeeId });
const records = Array.isArray(data) ? data : [];
let currentBalance = 0;
const rowsWithBalance = records.map((r) => {
const isCredit = r.amount > 0;
const credit = isCredit ? r.amount : 0;
const debit = !isCredit ? Math.abs(r.amount) : 0;
currentBalance += credit - debit;
return {
id: r.id,
description: r.title || "-",
projectName: r.project?.name || "-",
createdAt: r.createdAt,
credit,
debit,
financeUId: r.financeUId,
balance: currentBalance,
};
});
useEffect(() => {
if (!employeeId) {
setBalance(null);
return;
}
if (rowsWithBalance.length > 0) {
setBalance(rowsWithBalance[rowsWithBalance.length - 1].balance);
} else {
setBalance(0);
}
}, [employeeId, data, setBalance]);
if (!employeeId) {
return (
<div
className="d-flex justify-content-center align-items-center"
style={{ height: "200px" }}
>
<p className="text-muted m-0">Please select an employee</p>
</div>
);
}
if (isLoading || isFetching) {
return (
<div
className="d-flex justify-content-center align-items-center"
style={{ height: "200px" }}
>
<SpinnerLoader />
</div>
);
}
if (isError) {
return (
<div className="text-center py-3">
{error?.status === 404
? "No advance payment transactions found."
: <Error error={error} />}
</div>
);
}
const columns = [
{
key: "date",
label: (
<>
Date
</>
),
align: "text-start",
},
{ key: "description", label: "Description", align: "text-start" },
{
key: "credit",
label: (
<>
Credit <i className="bx bx-rupee text-success"></i>
</>
),
align: "text-end",
},
{
key: "debit",
label: (
<>
Debit <i className="bx bx-rupee text-danger"></i>
</>
),
align: "text-end",
},
{
key: "balance",
label: (
<>
Balance <i className="bi bi-currency-rupee text-primary"></i>
</>
),
align: "text-end fw-bold",
},
];
// Handle empty records
if (rowsWithBalance.length === 0) {
return (
<div className="text-center text-muted py-3">
No advance payment records found.
</div>
);
}
const DecideCreditOrDebit = ({ financeUId }) => {
if (!financeUId) return null;
const prefix = financeUId?.substring(0, 2).toUpperCase();
if (prefix === "PR") return <span className="text-success">+</span>;
if (prefix === "EX") return <span className="text-danger">-</span>;
return null;
};
return (
<div className="table-responsive">
<table className="table align-middle">
<thead className="table_header_border">
<tr>
{columns.map((col) => (
<th key={col.key} className={col.align}>
{col.label}
</th>
))}
</tr>
</thead>
<tbody>
{Array.isArray(data) && data.length > 0 ? (
data.map((row) => (
<tr key={row.id}>
{columns.map((col) => (
<td key={col.key} className={`${col.align} p-2`}>
{col.key === "credit" ? (
row.amount > 0 ? (
<span>{row.amount.toLocaleString("en-IN")}</span>
) : (
"-"
)
) : col.key === "debit" ? (
row.amount < 0 ? (
<span>
{Math.abs(row.amount).toLocaleString("en-IN")}
</span>
) : (
"-"
)
) : col.key === "balance" ? (
<div className="d-flex align-items-center justify-content-end">
{/* <DecideCreditOrDebit financeUId={row?.financeUId} /> */}
<span className="mx-2">
{formatFigure(row.currentBalance)}
</span>
</div>
) : col.key === "date" ? (
<small className="text-muted px-1">
{formatUTCToLocalTime(row.paidAt)}
</small>
) : (
<div className="d-flex flex-column text-start gap-1 py-1">
<small className="fw-semibold text-dark">
{row.project?.name || "-"}
</small>
<small>{row.title || "-"}</small>
</div>
)}
</td>
))}
</tr>
))
) : (
<tr>
<td
colSpan={columns.length}
className="text-center text-muted py-3"
>
No advance payment records found.
</td>
</tr>
)}
</tbody>
<tfoot className=" fw-bold">
<tr className="tr-group text-dark py-2">
<td className="text-start">
{" "}
<div className="d-flex align-items-center px-1 py-2">
Final Balance
</div>
</td>
<td className="text-end" colSpan="4">
<div className="d-flex align-items-center justify-content-end px-1 py-2">
{currentBalance.toLocaleString("en-IN", {
style: "currency",
currency: "INR",
})}
</div>
</td>
</tr>
</tfoot>
</table>
</div>
);
};
export default AdvancePaymentList;

View File

@ -0,0 +1,100 @@
import React from 'react'
import Avatar from "../../components/common/Avatar"; // <-- ADD THIS
import { useExpenseAllTransactionsList } from '../../hooks/useExpense';
import { useNavigate } from 'react-router-dom';
import { formatFigure } from '../../utils/appUtils';
const AdvancePaymentList1 = ({ searchString }) => {
const { data, isError, isLoading, error } =
useExpenseAllTransactionsList(searchString);
const rows = data || [];
const navigate = useNavigate();
const columns = [
{
key: "employee",
label: "Employee Name",
align: "text-start",
customRender: (r) => (
<div className="d-flex align-items-center gap-2" onClick={() => navigate(`/advance-payment/${r.id}`)}
style={{ cursor: "pointer" }}>
<Avatar firstName={r.firstName} lastName={r.lastName} />
<span className="fw-medium">
{r.firstName} {r.lastName}
</span>
</div>
),
},
{
key: "jobRoleName",
label: "Job Role",
align: "text-start",
customRender: (r) => (
<span className="fw-semibold">
{r.jobRoleName}
</span>
),
},
{
key: "balanceAmount",
label: "Balance (₹)",
align: "text-end",
customRender: (r) => (
<span className="fw-semibold fs-6">
{formatFigure(r.balanceAmount, {
// type: "currency",
currency: "INR",
})}
</span>
),
},
];
if (isLoading) return <p className="text-center py-4">Loading...</p>;
if (isError) return <p className="text-center py-4 text-danger">{error.message}</p>;
return (
<div className="card-datatable" id="payment-request-table">
<div className="mx-2">
<table className="table border-top dataTable text-nowrap align-middle">
<thead>
<tr>
{columns.map((col) => (
<th key={col.key} className={`sorting ${col.align}`}>
{col.label}
</th>
))}
</tr>
</thead>
<tbody>
{rows.length > 0 ? (
rows.map((row) => (
<tr key={row.id} className="align-middle" style={{ height: "40px" }}>
{columns.map((col) => (
<td key={col.key} className={`d-table-cell ${col.align} py-3`}>
{col.customRender
? col.customRender(row)
: col.getValue(row)}
</td>
))}
</tr>
))
) : (
<tr>
<td colSpan={columns.length} className="text-center border-0 py-3">
No Employees Found
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}
export default AdvancePaymentList1;

View File

@ -1,6 +1,7 @@
import React from "react";
import ReactApexChart from "react-apexcharts";
import PropTypes from "prop-types";
import { SpinnerLoader } from "../common/Loader";
const HorizontalBarChart = ({
seriesData = [],
@ -23,8 +24,12 @@ const HorizontalBarChart = ({
if (loading) {
return (
<div className="w-full h-[380px] flex items-center justify-center bg-gray-100 rounded-xl">
<span className="text-gray-500 text-sm">Loading chart...</span>
{/* Replace this with a skeleton or spinner if you prefer */}
<div
className="d-flex justify-content-center align-items-center"
style={{ minHeight: "50vh" }}
>
<SpinnerLoader />
</div>
</div>
);
}

View File

@ -1,6 +1,7 @@
import React from "react";
import ReactApexChart from "react-apexcharts";
import PropTypes from "prop-types";
import { SpinnerLoader } from "../common/Loader";
const LineChart = ({
seriesData = [],
@ -9,24 +10,28 @@ const LineChart = ({
loading = false,
lineChartCategoriesDates = [],
}) => {
const hasValidData =
Array.isArray(seriesData) &&
seriesData.length > 0 &&
Array.isArray(categories) &&
categories.length > 0;
const hasValidData =
Array.isArray(seriesData) &&
seriesData.length > 0 &&
Array.isArray(categories) &&
categories.length > 0;
if (loading) {
return (
<div className="flex justify-center items-center h-[350px] text-gray-500">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500 mr-2" />
Loading chart...
</div>
);
}
if (loading) {
return (
<div className="flex justify-center items-center h-[350px] text-gray-500">
<div
className="d-flex justify-content-center align-items-center"
style={{ minHeight: "50vh" }}
>
<SpinnerLoader />
</div>
</div>
);
}
if (!hasValidData) {
return <div className="text-center text-gray-500">No data to display</div>;
}
if (!hasValidData) {
return <div className="text-center text-gray-500">No data to display</div>;
}
const chartOptions = {
chart: {
@ -129,16 +134,16 @@ const LineChart = ({
};
LineChart.propTypes = {
seriesData: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string.isRequired,
data: PropTypes.arrayOf(PropTypes.number).isRequired
})
),
categories: PropTypes.arrayOf(PropTypes.string),
colors: PropTypes.arrayOf(PropTypes.string),
title: PropTypes.string,
loading: PropTypes.bool
seriesData: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string.isRequired,
data: PropTypes.arrayOf(PropTypes.number).isRequired
})
),
categories: PropTypes.arrayOf(PropTypes.string),
colors: PropTypes.arrayOf(PropTypes.string),
title: PropTypes.string,
loading: PropTypes.bool
};
export default LineChart;

View File

@ -1,42 +0,0 @@
import React, { createContext, useState, useContext } from "react";
import ChangePasswordPage from "../../pages/authentication/ChangePassword";
const ChangePasswordContext = createContext();
export const ChangePasswordProvider = ({ children }) => {
const [isChangePasswordOpen, setIsChangePasswordOpen] = useState(false);
const openChangePassword = () => setIsChangePasswordOpen(true);
const closeChangePassword = () => setIsChangePasswordOpen(false);
return (
<ChangePasswordContext.Provider
value={{ isChangePasswordOpen, openChangePassword, closeChangePassword }}
>
{children}
{isChangePasswordOpen && (
<>
{/* This is the main Bootstrap modal container */}
{/* It provides the fixed positioning and high z-index */}
<div
className="modal fade show" // 'fade' for animation, 'show' to make it visible
style={{ display: 'block' }} // Explicitly set display: block for immediate visibility
tabIndex="-1" // Makes the modal focusable
role="dialog" // ARIA role for accessibility
aria-labelledby="changePasswordModalLabel" // Link to a heading for accessibility
aria-modal="true" // Indicate it's a modal dialog
>
{/* The ChangePasswordPage component itself contains the modal-dialog and modal-content */}
<ChangePasswordPage onClose={closeChangePassword} />
</div>
{/* The modal backdrop */}
<div className="modal-backdrop fade show"></div>
</>
)}
</ChangePasswordContext.Provider>
);
};
export const useChangePassword = () => useContext(ChangePasswordContext);

View File

@ -1,5 +1,5 @@
import React, { useState } from "react";
import { useCurrentService, useProjectInfra } from "../../hooks/useProjects";
import { useCurrentService } from "../../hooks/useProjects";
import { useSelectedProject } from "../../slices/apiDataManager";
import { FormProvider, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@ -10,15 +10,14 @@ import {
import { DateRangePicker1 } from "../common/DateRangePicker";
import SelectMultiple from "../common/SelectMultiple";
import { localToUtc } from "../../utils/appUtils";
import { useTaskFilter } from "../../hooks/useTasks";
const TaskReportFilterPanel = ({ handleFilter }) => {
const [resetKey, setResetKey] = useState(0);
const selectedProjec = useSelectedProject();
const selectedProject = useSelectedProject();
const selectedService = useCurrentService();
const { projectInfra, isLoading, error, isFetched } = useProjectInfra(
selectedProjec,
selectedService
);
const { data } = useTaskFilter(selectedProject);
const methods = useForm({
resolver: zodResolver(TaskReportFilterSchema),
defaultValues: TaskReportDefaultValue,
@ -30,58 +29,75 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
handleSubmit,
formState: { errors },
} = methods;
const closePanel = () => {
document.querySelector(".offcanvas.show .btn-close")?.click();
};
const onSubmit = (formData) => {
console.log(formData)
const filterPayload = {
startDate:localToUtc(formData.startDate),
endDate:localToUtc(formData.endDate)
}
const filterPayload = {
...formData,
dateFrom: localToUtc(formData.dateFrom),
dateTo: localToUtc(formData.dateTo),
};
handleFilter(filterPayload);
closePanel();
};
const onClear =()=>{
const onClear = () => {
setResetKey((prev) => prev + 1);
handleFilter(TaskReportDefaultValue)
reset(TaskReportDefaultValue)
}
handleFilter(TaskReportDefaultValue);
reset(TaskReportDefaultValue);
closePanel();
};
return (
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)} className="p-2 text-start">
<div className="mb-3 w-100">
<label className="fw-semibold">Choose Date Range:</label>
<DateRangePicker1
className="w-100"
placeholder="DD-MM-YYYY To DD-MM-YYYY"
startField="startDate"
endField="endDate"
startField="dateFrom"
endField="dateTo"
resetSignal={resetKey}
defaultRange={true}
defaultRange={false}
maxDate={new Date()}
/>
</div>
{/* <div className="row g-2">
<SelectMultiple
name="buildingIds"
label="Projects"
options={projectInfra}
labelKey="buildingName"
valueKey="id"
/>
</div>
<div className="row g-2">
<SelectMultiple
name="floorIds"
label="Floor"
options={projectInfra}
labelKey="floorName"
valueKey="id"
/>
</div> */}
<div className="row mb-2">
<SelectMultiple
name="buildingIds"
label="Building"
options={data?.buildings}
labelKey="name"
valueKey="id"
/>
</div>
<div className="row mb-2">
<SelectMultiple
name="floorIds"
label="Floor"
options={data?.floors}
labelKey="name"
valueKey="id"
/>
</div>
<div className="row mb-2">
<SelectMultiple
name="activityIds"
label="Activities"
options={data?.activities}
labelKey="name"
valueKey="id"
/>
</div>
<div className="d-flex justify-content-end py-3 gap-2">
<button type="button" className="btn btn-label-secondary btn-sm" onClick={onClear}>
<button
type="button"
className="btn btn-label-secondary btn-sm"
onClick={onClear}
>
Clear
</button>
<button type="submit" className="btn btn-primary btn-sm">

View File

@ -29,7 +29,7 @@ const TaskReportList = () => {
const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK);
const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK);
const { service, openModal, closeModal,filter } = useDailyProgrssContext();
const { service, openModal, closeModal, filter } = useDailyProgrssContext();
const selectedProject = useSelectedProject();
const { projectNames } = useProjectName();
@ -37,7 +37,7 @@ const TaskReportList = () => {
selectedProject,
ITEMS_PER_PAGE,
currentPage,
service,filter
service, filter
);
const ProgrssReportColumn = [
@ -192,109 +192,116 @@ const TaskReportList = () => {
if (isLoading) return <TaskReportListSkeleton />;
if (isError) return <div>Loading....</div>;
return (
<div className="mt-2">
<table className="table">
<thead>
<tr>
<th className="text-start">Activity</th>
<th>
<span>
Total Pending{" "}
<HoverPopup
title="Total Pending Task"
content={<p>This shows the total pending tasks for each activity on that date.</p>}
>
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
</HoverPopup>
</span>
</th>
<th>
<span>
Reported/Planned{" "}
<HoverPopup
title="Reported and Planned Task"
content={<p>This shows the reported versus planned tasks for each activity on that date.</p>}
>
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
</HoverPopup>
</span>
</th>
<th>Assign Date</th>
<th>Team</th>
<th className="text-center">Actions</th>
</tr>
</thead>
<tbody>
{groupedTasks.length === 0 && (
<div>
<div className="mt-2 table-responsive text-nowrap">
<table className="table">
<thead>
<tr>
<td colSpan={6} className="text-center align-middle" style={{ height: "200px", borderBottom: "none" }}>
No reports available
</td>
<th className="text-start">Activity</th>
<th>
<span>
Total Pending{" "}
<HoverPopup
id="total_pending_task"
title="Total Pending Task"
content={<p>This shows the total pending tasks for each activity on that date.</p>}
>
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
</HoverPopup>
</span>
</th>
<th>
<span>
Reported/Planned{" "}
<HoverPopup
id="reportes_and_planned_task"
title="Reported and Planned Task"
content={<p>This shows the reported versus planned tasks for each activity on that date.</p>}
>
<i className="bx bx-xs ms-1 bx-info-circle cursor-pointer"></i>
</HoverPopup>
</span>
</th>
<th>Assign Date</th>
<th>Team</th>
<th className="text-center">Actions</th>
</tr>
)}
{groupedTasks.map(({ date, tasks }) => (
<React.Fragment key={date}>
<tr className="table-row-header text-start">
<td colSpan={6}>
<strong>{formatUTCToLocalTime(date)}</strong>
</thead>
<tbody>
{groupedTasks.length === 0 && (
<tr>
<td colSpan={6} className="text-center align-middle" style={{ height: "200px", borderBottom: "none" }}>
No reports available
</td>
</tr>
{tasks.map((task, idx) => (
<tr key={task.id || idx}>
<td className="flex-wrap text-start">
<div>
{task.workItem.activityMaster?.activityName || "No Activity Name"}
</div>
<div className="text-sm py-2">
{task.workItem.workArea?.floor?.building?.name} {" "}
{task.workItem.workArea?.floor?.floorName} {" "}
{task.workItem.workArea?.areaName}
</div>
</td>
<td>
{formatNumber(task.workItem.plannedWork)}
</td>
<td>{`${formatNumber(task.completedTask)} / ${formatNumber(task.plannedTask)}`}</td>
<td>{formatUTCToLocalTime(task.assignmentDate)}</td>
<td className="text-center">{renderTeamMembers(task, idx)}</td>
<td className="text-center">
<div className="d-flex justify-content-end gap-2">
{ReportTaskRights && !task.reportedDate && (
<button className="btn btn-xs btn-primary" onClick={() => openModal("report", task)}>
Report
</button>
)}
{ApprovedTaskRights && task.reportedDate && !task.approvedBy && (
<button
className="btn btn-xs btn-warning"
onClick={() => openModal("comments", { task, isActionAllow: true })}
>
QC
</button>
)}
<button
className="btn btn-xs btn-primary"
onClick={() => openModal("comments", { task, isActionAllow: false })}
>
Comment
</button>
</div>
)}
{groupedTasks.map(({ date, tasks }) => (
<React.Fragment key={date}>
<tr className="table-row-header text-start">
<td colSpan={6}>
<strong>{formatUTCToLocalTime(date)}</strong>
</td>
</tr>
))}
</React.Fragment>
))}
</tbody>
</table>
{data?.data?.length > 0 && (
<Pagination
currentPage={currentPage}
totalPages={data.totalPages}
onPageChange={paginate}
/>
)}
</div>
{tasks.map((task, idx) => (
<tr key={task.id || idx}>
<td className="flex-wrap text-start">
<div>
{task.workItem.activityMaster?.activityName || "No Activity Name"}
</div>
<div className="text-sm py-2">
{task.workItem.workArea?.floor?.building?.name} {" "}
{task.workItem.workArea?.floor?.floorName} {" "}
{task.workItem.workArea?.areaName}
</div>
</td>
<td>
{formatNumber(task.workItem.plannedWork)}
</td>
<td>{`${formatNumber(task.completedTask)} / ${formatNumber(task.plannedTask)}`}</td>
<td>{formatUTCToLocalTime(task.assignmentDate)}</td>
<td className="text-center">{renderTeamMembers(task, idx)}</td>
<td className="text-center">
<div className="d-flex justify-content-end gap-2">
{ReportTaskRights && !task.reportedDate && (
<button className="btn btn-xs btn-primary" onClick={() => openModal("report", task)}>
Report
</button>
)}
{ApprovedTaskRights && task.reportedDate && !task.approvedBy && (
<button
className="btn btn-xs btn-warning"
onClick={() => openModal("comments", { task, isActionAllow: true })}
>
QC
</button>
)}
<button
className="btn btn-xs btn-primary"
onClick={() => openModal("comments", { task, isActionAllow: false })}
>
Comment
</button>
</div>
</td>
</tr>
))}
</React.Fragment>
))}
</tbody>
</table>
</div>
{
data?.data?.length > 0 && (
<Pagination
currentPage={currentPage}
totalPages={data.totalPages}
onPageChange={paginate}
/>
)
}
</div >
);
};

View File

@ -1,15 +1,17 @@
import { z } from "zod";
export const TaskReportFilterSchema = z.object({
// buildingIds: z.array(z.string()).optional(),
// floorIds: z.array(z.string()).optional(),
startDate: z.string().optional(),
endDate: z.string().optional(),
buildingIds: z.array(z.string()).optional(),
floorIds: z.array(z.string()).optional(),
activityIds: z.array(z.string()).optional(),
dateFrom: z.string().optional(),
dateTo: z.string().optional(),
});
export const TaskReportDefaultValue = {
// buildingIds:[],
// floorIds:[],
startDate:null,
endDate:null
buildingIds:[],
floorIds:[],
activityIds:[],
dateFrom:null,
dateTo:null
}

View File

@ -4,6 +4,7 @@ import ReactApexChart from "react-apexcharts";
import { useAttendanceOverviewData } from "../../hooks/useDashboard_Data";
import flatColors from "../Charts/flatColor";
import ChartSkeleton from "../Charts/Skelton";
import { SpinnerLoader } from "../common/Loader";
const formatDate = (dateStr) => {
const date = new Date(dateStr);
@ -99,9 +100,9 @@ const AttendanceOverview = () => {
};
return (
<div className="bg-white p-4 rounded shadow d-flex flex-column">
<div className="bg-white px-4 rounded shadow d-flex flex-column h-100">
{/* Header */}
<div className="d-flex justify-content-between align-items-center mb-3">
<div className="d-flex mt-2 justify-content-between align-items-center mb-3">
<div className="card-title mb-0 text-start">
<h5 className="mb-1 fw-bold">Attendance Overview</h5>
<p className="card-subtitle">Role-wise present count</p>
@ -117,18 +118,16 @@ const AttendanceOverview = () => {
<option value={30}>Last 30 Days</option>
</select>
<button
className={`btn btn-sm p-1 ${
view === "chart" ? "btn-primary" : "btn-outline-primary"
}`}
className={`btn btn-sm p-1 ${view === "chart" ? "btn-primary" : "btn-outline-primary"
}`}
onClick={() => setView("chart")}
title="Chart View"
>
<i className="bx bx-bar-chart-alt-2"></i>
</button>
<button
className={`btn btn-sm p-1 ${
view === "table" ? "btn-primary" : "btn-outline-primary"
}`}
className={`btn btn-sm p-1 ${view === "table" ? "btn-primary" : "btn-outline-primary"
}`}
onClick={() => setView("table")}
title="Table View"
>
@ -138,11 +137,14 @@ const AttendanceOverview = () => {
</div>
{/* Content */}
<div className="flex-grow-1 d-flex align-items-center justify-content-center">
<div className="flex-grow-1 d-flex align-items-center justify-content-center">
{loading ? (
<ChartSkeleton />
<SpinnerLoader />
) : error ? (
<p className="text-danger">{error}</p>
) : attendanceOverviewData.length === 0 ||
attendanceOverviewData.every((item) => item.present === 0) ? (
<div className="text-center text-dark">No data found</div>
) : view === "chart" ? (
<div className="w-100">
<ReactApexChart
@ -163,9 +165,7 @@ const AttendanceOverview = () => {
style={{ position: "sticky", top: 0, zIndex: 1 }}
>
<tr>
<th style={{ background: "#f8f9fa", textTransform: "none" }}>
Role
</th>
<th style={{ background: "#f8f9fa", textTransform: "none" }}>Role</th>
{dates.map((date, idx) => (
<th
key={idx}
@ -176,15 +176,13 @@ const AttendanceOverview = () => {
))}
</tr>
</thead>
<tbody>
{roles.map((role) => (
<tr key={role}>
<td>{role}</td>
{tableData.map((row, idx) => {
const value = row[role];
const cellStyle =
value > 0 ? { backgroundColor: "#d5d5d5" } : {};
const cellStyle = value > 0 ? { backgroundColor: "#d5d5d5" } : {};
return (
<td key={idx} style={cellStyle}>
{value}

View File

@ -12,13 +12,13 @@ import Teams from "./Teams";
import TasksCard from "./Tasks";
import ProjectCompletionChart from "./ProjectCompletionChart";
import ProjectProgressChart from "./ProjectProgressChart";
import ProjectOverview from "../Project/ProjectOverview";
import AttendanceOverview from "./AttendanceChart";
import AttendanceOverview from "./AttendanceOverview";
import ExpenseAnalysis from "./ExpenseAnalysis";
import ExpenseStatus from "./ExpenseStatus";
import ExpenseByProject from "./ExpenseByProject";
import ProjectStatistics from "../Project/ProjectStatistics";
const Dashboard = () => {
const { projectsCardData } = useDashboardProjectsCardData();
const { teamsCardData } = useDashboardTeamsCardData();
const { tasksCardData } = useDashboardTasksCardData();
// Get the selected project ID from Redux store
const projectId = useSelector((store) => store.localVariables.projectId);
@ -29,16 +29,16 @@ const Dashboard = () => {
<div className="row gy-4">
{isAllProjectsSelected && (
<div className="col-sm-6 col-lg-4">
<Projects projectsCardData={projectsCardData} />
<Projects />
</div>
)}
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
<Teams teamsCardData={teamsCardData} />
<Teams />
</div>
<div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6" : "col-sm-6 col-lg-4"}`}>
<TasksCard tasksCardData={tasksCardData} />
<TasksCard />
</div>
{isAllProjectsSelected && (
@ -46,21 +46,34 @@ const Dashboard = () => {
<ProjectCompletionChart />
</div>
)}
{!isAllProjectsSelected && (
<div className="col-xxl-6 col-lg-6">
<ProjectOverview />
</div>
)}
<div className="col-xxl-6 col-lg-6">
<ProjectProgressChart />
</div>
{!isAllProjectsSelected && (
<div className="col-xxl-6 col-lg-6">
<AttendanceOverview /> {/* ✅ Removed unnecessary projectId prop */}
<div className="col-12 col-md-6 mb-sm-0 mb-4">
<AttendanceOverview />
</div>
)}
{!isAllProjectsSelected && (
<div className="col-xxl-4 col-lg-4">
<ProjectStatistics />
</div>
)}
<div className="col-12 col-xl-4 col-md-6">
<div className="card ">
<ExpenseStatus />
</div>
</div>
<div className="col-12 col-xl-8">
<div className="card h-100">
<ExpenseAnalysis />
</div>
</div>
<div className="col-12 col-md-6">
<ExpenseByProject />
</div>
</div>
</div>
);

View File

@ -0,0 +1,110 @@
import React from "react";
const SkeletonLine = ({ height = 20, width = "100%", className = "" }) => (
<div
className={`skeleton ${className}`}
style={{
height,
width,
borderRadius: "4px",
background: "linear-gradient(90deg, #eee, #f5f5f5, #eee)",
backgroundSize: "200% 100%",
animation: "skeleton-loading 1.5s infinite",
}}
></div>
);
const skeletonStyle = `
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
`;
export const ProjectCardSkeleton = () => {
return (
<>
{/* Inject animation CSS once */}
<style>{skeletonStyle}</style>
<div className="card p-3 h-100 text-center d-flex justify-content-between">
{/* Header */}
<div className="d-flex justify-content-start align-items-center mb-3">
<h5 className="fw-bold mb-0 ms-2">
<i className="rounded-circle bx bx-building-house text-primary"></i>{" "}
Projects
</h5>
</div>
{/* Skeleton body */}
<div className="d-flex justify-content-around align-items-start mt-n2 w-100">
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="40px" className="mx-auto" />
</div>
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="40px" className="mx-auto" />
</div>
</div>
</div>
</>
);
};
export const TeamsSkeleton = () => {
return (
<>
<style>{skeletonStyle}</style>
<div className="card p-3 h-100 text-center d-flex justify-content-between">
{/* Header */}
<div className="d-flex justify-content-start align-items-center mb-3">
<h5 className="fw-bold mb-0 ms-2">
<i className="bx bx-group text-warning"></i> Teams
</h5>
</div>
{/* Skeleton Body */}
<div className="d-flex justify-content-around align-items-start mt-n2 w-100">
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="90px" className="mx-auto" />
</div>
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="70px" className="mx-auto" />
</div>
</div>
</div>
</>
);
};
export const TasksSkeleton = () => {
return (
<>
<style>{skeletonStyle}</style>
<div className="card p-3 h-100 text-center d-flex justify-content-between">
{/* Header */}
<div className="d-flex justify-content-start align-items-center mb-3">
<h5 className="fw-bold mb-0 ms-2">
<i className="bx bx-task text-success"></i> Tasks
</h5>
</div>
{/* Skeleton Body */}
<div className="d-flex justify-content-around align-items-start mt-n2 w-100">
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="70px" className="mx-auto" />
</div>
<div className="text-center">
<SkeletonLine height={28} width="60px" className="mx-auto mb-2" />
<SkeletonLine height={14} width="90px" className="mx-auto" />
</div>
</div>
</div>
</>
);
};

Some files were not shown because too many files have changed in this diff Show More