@visns-studio/visns-components 5.6.8 → 5.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,47 +1,176 @@
1
- // Use the Login styles to maintain consistency
2
- @use './Login.module.scss' as login;
1
+ .formItem {
2
+ width: 100%;
3
+ position: relative;
4
+ box-sizing: border-box;
5
+ margin-bottom: 1rem;
3
6
 
4
- // Additional styles specific to the 2FA component
5
- .instructions {
6
- text-align: center;
7
- margin: 0.5rem 0 1.5rem;
8
- color: var(--paragraph-color);
9
- font-size: 0.95rem;
10
- line-height: 1.4;
7
+ .fi__label {
8
+ position: relative;
9
+ display: flex;
10
+ align-items: center;
11
+ }
12
+
13
+ .fi__span {
14
+ position: absolute;
15
+ transition: all 200ms;
16
+ opacity: 0.7;
17
+ left: 0;
18
+ transform-origin: top left;
19
+ cursor: text;
20
+ padding: 0 1rem;
21
+ font-size: 0.9rem;
22
+ color: #6b7280;
23
+ }
24
+
25
+ input[name='verificationCode'] {
26
+ letter-spacing: 0.25rem;
27
+ font-size: 1.2rem;
28
+ text-align: center;
29
+ }
11
30
  }
12
31
 
13
- .login {
14
- // Instead of extending, copy the necessary properties
15
- width: 35rem;
32
+ .fwItem,
33
+ .fwBuilderItem {
34
+ grid-column: 1 / -1;
35
+ width: 100%;
36
+ }
37
+
38
+ .lastItem .btn {
39
+ width: 100%;
40
+ }
41
+
42
+ .loginlogo {
43
+ display: block;
44
+ width: 100%;
45
+ max-width: 150px;
46
+ height: auto;
47
+ margin: 0 auto;
48
+ padding-bottom: 1.5rem;
49
+ }
50
+
51
+ .logincontainer {
52
+ width: 100%;
53
+ min-height: 100vh;
54
+ display: grid;
55
+ place-items: center;
16
56
  padding: 1rem;
57
+ background-color: #f9fafb;
58
+ }
59
+
60
+ .login {
61
+ width: 28rem;
62
+ padding: 2rem;
17
63
  margin: 0;
18
64
  display: grid;
19
65
  grid-template-columns: 1fr;
20
- gap: 0.5rem;
66
+ gap: 1rem;
67
+ background-color: white;
68
+ border-radius: 0.75rem;
69
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
70
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
71
+
72
+ > .formItem {
73
+ padding: 0;
74
+
75
+ small {
76
+ position: absolute;
77
+ width: 18px;
78
+ height: 18px;
79
+ right: 14px;
80
+ top: 14px;
81
+ opacity: 0.4;
82
+ color: #6b7280;
83
+ }
84
+ }
21
85
 
22
86
  h2 {
23
87
  text-align: center;
24
- margin: 0;
25
- color: var(--heading-color);
88
+ margin-bottom: 0.5rem;
89
+ color: #111827;
90
+ font-weight: 600;
91
+ font-size: 1.5rem;
92
+ }
93
+ }
94
+
95
+ .instructions {
96
+ text-align: center;
97
+ margin: 0 0 1.5rem;
98
+ color: var(--paragraph-color, #6b7280);
99
+ font-size: 0.95rem;
100
+ line-height: 1.4;
101
+ }
102
+
103
+ .forgotten span {
104
+ display: block;
105
+ text-align: center;
106
+ color: #6b7280;
107
+ padding: 1rem 0;
108
+ font-size: 0.875rem;
109
+
110
+ a {
111
+ color: var(--primary-color, #4f46e5);
112
+ text-decoration: none;
26
113
  font-weight: 500;
114
+
115
+ &:hover {
116
+ text-decoration: underline;
117
+ }
27
118
  }
28
119
  }
29
120
 
30
- // Style for the verification code input
31
- .formItem {
32
- // Instead of extending, copy the necessary properties
33
- width: 50%;
121
+ .lcontainer {
122
+ min-height: 100vh;
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ background-color: #f9fafb;
127
+ }
128
+
129
+ .lwrap {
130
+ display: grid;
131
+ grid-template-columns: 1fr;
132
+ }
133
+
134
+ .btn {
135
+ width: max-content;
136
+ display: inline-block;
34
137
  position: relative;
35
- box-sizing: border-box;
36
- margin-bottom: 0.5rem;
138
+ padding: 0.75rem 1rem;
139
+ cursor: pointer;
140
+ font-size: 0.875rem;
141
+ font-weight: 500;
142
+ color: white;
143
+ text-decoration: none;
144
+ overflow: hidden;
145
+ background: var(--primary-color, #4f46e5);
146
+ border: 1px solid var(--primary-color, #4f46e5);
147
+ border-radius: 0.5rem;
148
+ outline: none;
149
+ transition: all 0.2s ease;
150
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
37
151
 
38
- input[name='verificationCode'] {
39
- letter-spacing: 0.25rem;
40
- font-size: 1.2rem;
41
- text-align: center;
152
+ &:hover {
153
+ background: #4338ca;
154
+ border-color: #4338ca;
155
+ }
156
+
157
+ &:focus {
158
+ box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.25);
42
159
  }
43
160
  }
44
161
 
162
+ input:not(:placeholder-shown) + .fi__span,
163
+ textarea:not(:placeholder-shown) + .fi__span,
164
+ select:not(:placeholder-shown) + .fi__span {
165
+ transform: translateY(-135%) translateX(10px) scale(0.85);
166
+ opacity: 1;
167
+ padding: 0;
168
+ background: white;
169
+ font-weight: 500;
170
+ transition: all 0.3s ease;
171
+ color: var(--primary-color, #4f46e5) !important;
172
+ }
173
+
45
174
  .rememberMeIndicator {
46
175
  display: flex;
47
176
  align-items: center;
@@ -0,0 +1,207 @@
1
+ import React, { useState } from 'react';
2
+ import TableFilter from '../TableFilter';
3
+
4
+ const FilterExample = () => {
5
+ // Initial filter structure with parent-child relationships
6
+ const initialFilters = [
7
+ {
8
+ id: "personnelLicensesAndRecords",
9
+ label: "Personnel Licenses and Records",
10
+ isParent: true,
11
+ show: false,
12
+ class: "",
13
+ children: [
14
+ {
15
+ id: "completedSiteInduction",
16
+ show: false,
17
+ label: "Completed Site Induction",
18
+ class: ""
19
+ },
20
+ {
21
+ id: "unapprovedWhiteCard",
22
+ show: false,
23
+ label: "Unapproved White Card",
24
+ class: ""
25
+ },
26
+ {
27
+ id: "unapprovedLicense",
28
+ show: false,
29
+ label: "Unapproved License",
30
+ class: ""
31
+ },
32
+ {
33
+ id: "outstandingLicense",
34
+ show: false,
35
+ label: "Outstanding License",
36
+ class: ""
37
+ },
38
+ {
39
+ id: "unapprovedCovid",
40
+ show: false,
41
+ label: "Unapproved COVID",
42
+ class: ""
43
+ }
44
+ ]
45
+ },
46
+ {
47
+ id: "insurance",
48
+ label: "Insurance",
49
+ isParent: true,
50
+ show: false,
51
+ class: "",
52
+ children: [
53
+ {
54
+ id: "allInsurance",
55
+ show: false,
56
+ label: "All Insurance",
57
+ class: ""
58
+ },
59
+ {
60
+ id: "unapprovedInsurance",
61
+ show: false,
62
+ label: "Unapproved Insurance",
63
+ class: ""
64
+ },
65
+ {
66
+ id: "expiredInsurance",
67
+ show: false,
68
+ label: "Expired Insurance",
69
+ class: ""
70
+ },
71
+ {
72
+ id: "outstandingInsurance",
73
+ show: false,
74
+ label: "Outstanding Insurance",
75
+ class: ""
76
+ }
77
+ ]
78
+ },
79
+ {
80
+ id: "workingHours",
81
+ label: "Working Hours",
82
+ isParent: true,
83
+ show: false,
84
+ class: "",
85
+ children: [
86
+ {
87
+ id: "projectTotalHours",
88
+ show: false,
89
+ label: "Project Total Hours",
90
+ class: ""
91
+ },
92
+ {
93
+ id: "contractorTotalHours",
94
+ show: false,
95
+ label: "Contractor Total Hours",
96
+ class: ""
97
+ },
98
+ {
99
+ id: "sitePersonnelWorkHours",
100
+ show: false,
101
+ label: "Site Personnel Work Hours",
102
+ class: ""
103
+ },
104
+ {
105
+ id: "sitePersonnelRecords",
106
+ show: false,
107
+ label: "Site Personnel Records",
108
+ class: ""
109
+ }
110
+ ]
111
+ },
112
+ {
113
+ id: "prequalifications",
114
+ label: "Prequalifications",
115
+ isParent: true,
116
+ show: false,
117
+ class: "",
118
+ children: [
119
+ {
120
+ id: "prequalificationsUnapproved",
121
+ show: false,
122
+ label: "Unapproved Prequalifications",
123
+ class: ""
124
+ },
125
+ {
126
+ id: "prequalificationsExpiring",
127
+ show: false,
128
+ label: "Expiring Prequalifications",
129
+ class: ""
130
+ },
131
+ {
132
+ id: "prequalifiedContractors",
133
+ show: false,
134
+ label: "Prequalified Contractors",
135
+ class: ""
136
+ }
137
+ ]
138
+ }
139
+ ];
140
+
141
+ const [filters, setFilters] = useState(initialFilters);
142
+ const [settings, setSettings] = useState({
143
+ ajaxSetting: {
144
+ where: []
145
+ }
146
+ });
147
+
148
+ // Find the active filter to display content
149
+ const getActiveFilter = () => {
150
+ let activeFilter = null;
151
+
152
+ // Check each parent filter
153
+ filters.forEach(parent => {
154
+ if (parent.children) {
155
+ // Check if any child is active
156
+ parent.children.forEach(child => {
157
+ if (child.show) {
158
+ activeFilter = {
159
+ parent: parent.label,
160
+ child: child.label,
161
+ id: child.id
162
+ };
163
+ }
164
+ });
165
+ }
166
+ });
167
+
168
+ return activeFilter;
169
+ };
170
+
171
+ const activeFilter = getActiveFilter();
172
+
173
+ return (
174
+ <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
175
+ <h1 style={{ marginBottom: '20px' }}>Reports</h1>
176
+
177
+ <div style={{ display: 'flex', gap: '20px' }}>
178
+ <div style={{ width: '300px' }}>
179
+ <TableFilter
180
+ filters={filters}
181
+ setFilters={setFilters}
182
+ setSettings={setSettings}
183
+ />
184
+ </div>
185
+
186
+ <div style={{ flex: 1, padding: '20px', backgroundColor: '#f9f9f9', borderRadius: '8px', minHeight: '300px' }}>
187
+ {activeFilter ? (
188
+ <div>
189
+ <h2>{activeFilter.child}</h2>
190
+ <p>Category: {activeFilter.parent}</p>
191
+ <p>Filter ID: {activeFilter.id}</p>
192
+ <div style={{ marginTop: '20px', padding: '15px', backgroundColor: '#fff', borderRadius: '4px' }}>
193
+ <p>This is the content area that would display data based on the selected filter.</p>
194
+ </div>
195
+ </div>
196
+ ) : (
197
+ <div style={{ textAlign: 'center', color: '#666' }}>
198
+ <p>Please select a filter option to view content</p>
199
+ </div>
200
+ )}
201
+ </div>
202
+ </div>
203
+ </div>
204
+ );
205
+ };
206
+
207
+ export default FilterExample;
@@ -0,0 +1,113 @@
1
+ # TableFilter Component with Parent-Child Structure
2
+
3
+ This document explains how to use the updated TableFilter component with parent-child relationships.
4
+
5
+ ## Structure of Filter Data
6
+
7
+ The TableFilter component now supports a hierarchical structure with parent categories and child filters. Here's how to structure your filter data:
8
+
9
+ ```javascript
10
+ const filters = [
11
+ {
12
+ id: "parentCategoryId",
13
+ label: "Parent Category Name",
14
+ isParent: true, // Important: mark as parent
15
+ show: false,
16
+ class: "",
17
+ children: [
18
+ {
19
+ id: "childFilter1",
20
+ show: false,
21
+ label: "Child Filter 1",
22
+ class: ""
23
+ },
24
+ {
25
+ id: "childFilter2",
26
+ show: false,
27
+ label: "Child Filter 2",
28
+ class: ""
29
+ }
30
+ ]
31
+ },
32
+ // More parent categories...
33
+ ];
34
+ ```
35
+
36
+ ## Key Features
37
+
38
+ 1. **Collapsible Parent Categories**: Click on a parent category to show/hide its children
39
+ 2. **Visual Indicators**: Arrow indicators show whether a parent category is expanded
40
+ 3. **Styling**: Distinct styling for parent categories and child filters
41
+ 4. **Active States**: Visual indication of active filters
42
+ 5. **Mobile Responsive**: Adapts to smaller screens with a toggle menu
43
+
44
+ ## Usage Example
45
+
46
+ ```jsx
47
+ import React, { useState } from 'react';
48
+ import TableFilter from '../TableFilter';
49
+
50
+ function MyComponent() {
51
+ const [filters, setFilters] = useState([
52
+ // Your filter structure here
53
+ ]);
54
+
55
+ const [settings, setSettings] = useState({
56
+ ajaxSetting: {
57
+ where: []
58
+ }
59
+ });
60
+
61
+ return (
62
+ <TableFilter
63
+ filters={filters}
64
+ setFilters={setFilters}
65
+ setSettings={setSettings}
66
+ />
67
+ );
68
+ }
69
+ ```
70
+
71
+ ## How It Works
72
+
73
+ 1. **Parent Categories**: When you click on a parent category, it toggles the visibility of its children
74
+ 2. **Child Filters**: When you click on a child filter:
75
+ - It becomes active (highlighted)
76
+ - The `show` property is set to `true`
77
+ - The `settings` object is updated with the filter's ID and value
78
+ - The content area should update based on the selected filter
79
+
80
+ ## Handling Filter Selection
81
+
82
+ To display content based on the selected filter, check which filter has `show: true`:
83
+
84
+ ```javascript
85
+ // Find the active filter
86
+ const getActiveFilter = () => {
87
+ let activeFilter = null;
88
+
89
+ filters.forEach(parent => {
90
+ if (parent.children) {
91
+ parent.children.forEach(child => {
92
+ if (child.show) {
93
+ activeFilter = {
94
+ parent: parent.label,
95
+ child: child.label,
96
+ id: child.id
97
+ };
98
+ }
99
+ });
100
+ }
101
+ });
102
+
103
+ return activeFilter;
104
+ };
105
+
106
+ // Use the active filter to display content
107
+ const activeFilter = getActiveFilter();
108
+ if (activeFilter) {
109
+ // Display content for the active filter
110
+ }
111
+ ```
112
+
113
+ See the `FilterExample.jsx` file for a complete working example.