ui-soxo-bootstrap-core 2.4.25-dev.8 → 2.4.25
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.
- package/.github/workflows/npm-publish.yml +15 -37
- package/core/components/extra-info/extra-info-details.js +126 -109
- package/core/components/landing-api/landing-api.js +30 -22
- package/core/lib/Store.js +18 -20
- package/core/lib/components/index.js +1 -4
- package/core/lib/components/sidemenu/sidemenu.js +256 -153
- package/core/lib/components/sidemenu/sidemenu.scss +26 -39
- package/core/lib/hooks/index.js +12 -2
- package/core/lib/pages/login/login.js +139 -255
- package/core/lib/pages/login/login.scss +32 -140
- package/core/models/dashboard/dashboard.js +0 -14
- package/core/models/menus/components/menu-add/menu-add.js +268 -230
- package/core/models/menus/components/menu-lists/menu-lists.js +89 -126
- package/core/models/menus/components/menu-lists/menu-lists.scss +0 -9
- package/core/models/menus/menus.js +267 -247
- package/core/models/roles/components/role-add/role-add.js +227 -269
- package/core/models/roles/components/role-list/role-list.js +6 -8
- package/core/models/roles/roles.js +174 -182
- package/core/models/users/components/user-add/user-add.js +1 -69
- package/core/models/users/users.js +0 -57
- package/core/modules/index.js +13 -23
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +1 -1
- package/package.json +2 -2
- package/README.md +0 -260
- package/core/lib/hooks/use-otp-timer.js +0 -99
- package/core/models/staff/components/staff-add/staff-add.js +0 -352
- package/core/models/staff/components/staff-add/staff-add.scss +0 -0
- package/core/modules/steps/action-buttons.js +0 -79
- package/core/modules/steps/steps.js +0 -553
- package/core/modules/steps/steps.scss +0 -158
- package/core/modules/steps/timeline.js +0 -49
package/README.md
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
# 📦 NPM Release Workflow — Task-Based Development → Develop → Master
|
|
2
|
-
|
|
3
|
-
This guide describes the complete workflow for publishing **task-based development builds** and **stable releases** using GitHub Actions, Git tags, and npm versioning.
|
|
4
|
-
|
|
5
|
-
Incorrect versioning or incorrect tags will break the publish pipeline — follow the process exactly.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# 📘 Table of Contents
|
|
10
|
-
- Overview
|
|
11
|
-
- Branch Strategy
|
|
12
|
-
- Task Branch Workflow
|
|
13
|
-
- Versioning Rules
|
|
14
|
-
- Development Release Workflow (`develop`)
|
|
15
|
-
- Promotion Workflow (Dev → Master)
|
|
16
|
-
- Publishing via GitHub Release UI
|
|
17
|
-
- How GitHub Action Detects Release Type
|
|
18
|
-
- Summary Table
|
|
19
|
-
- Common Mistakes & Fixes
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
# 🧩 Overview
|
|
24
|
-
|
|
25
|
-
We maintain two release channels:
|
|
26
|
-
|
|
27
|
-
| Channel | Branch | Tag | Purpose |
|
|
28
|
-
|---------------------|----------|------------------|-----------------------|
|
|
29
|
-
| Stable (`latest`) | master | `vX.Y.Z` | Production release |
|
|
30
|
-
| Development (`dev`) | develop | `vX.Y.Z-dev.N` | QA/internal testing |
|
|
31
|
-
|
|
32
|
-
Releases are triggered using Git tags:
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
npm version
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
# 🌿 Branch Strategy
|
|
41
|
-
|
|
42
|
-
## `master`
|
|
43
|
-
- Production-ready code
|
|
44
|
-
- Publishes **stable** builds
|
|
45
|
-
- Versions never include a `dev` suffix
|
|
46
|
-
|
|
47
|
-
## `develop`
|
|
48
|
-
- Used for QA/internal testing
|
|
49
|
-
- Publishes **development** builds
|
|
50
|
-
- Versions always include `-dev.N`
|
|
51
|
-
|
|
52
|
-
## Task Branches
|
|
53
|
-
- Create from `master`
|
|
54
|
-
- Naming: `task/<JIRA-ID>-<description>`
|
|
55
|
-
- After completing work, merge into `develop`
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
# 🏗️ Task Branch Workflow
|
|
60
|
-
|
|
61
|
-
### 1. Create a task branch
|
|
62
|
-
```bash
|
|
63
|
-
git checkout master
|
|
64
|
-
git pull origin master
|
|
65
|
-
git checkout -b task/<JIRA-ID>-<description>
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### 2. Work on the task
|
|
69
|
-
```bash
|
|
70
|
-
git add .
|
|
71
|
-
git commit -m "TASK-123: Implement XYZ"
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 3. Merge into develop
|
|
75
|
-
```bash
|
|
76
|
-
git checkout develop
|
|
77
|
-
git pull origin develop
|
|
78
|
-
git merge task/<JIRA-ID>-<description>
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
# 🔧 Versioning Rules
|
|
84
|
-
|
|
85
|
-
## Development Versions
|
|
86
|
-
```
|
|
87
|
-
X.Y.Z-dev.N
|
|
88
|
-
```
|
|
89
|
-
Example:
|
|
90
|
-
```
|
|
91
|
-
2.4.30-dev.0
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## Stable Versions
|
|
95
|
-
```
|
|
96
|
-
X.Y.Z
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
## Rules
|
|
100
|
-
- Each version must be unique
|
|
101
|
-
- Dev versions do **not** convert into stable versions
|
|
102
|
-
- Git tag must exactly match `package.json` version
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
# 🟦 Development Release Workflow (`develop`)
|
|
107
|
-
|
|
108
|
-
### 1. Switch to develop
|
|
109
|
-
```bash
|
|
110
|
-
git checkout develop
|
|
111
|
-
git pull origin develop
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### 2. Create a new development version
|
|
115
|
-
```bash
|
|
116
|
-
npm version prerelease --preid=dev
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### 3. Push commit + tag
|
|
120
|
-
```bash
|
|
121
|
-
git push --follow-tags
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### 4. GitHub Action publishes dev build
|
|
125
|
-
```
|
|
126
|
-
npm publish --tag dev
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### 5. Install the dev build
|
|
130
|
-
```bash
|
|
131
|
-
npm install soxo-bootstrap-core@dev
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
# 🟩 Promotion Workflow (Dev → Master)
|
|
137
|
-
|
|
138
|
-
### 1. Switch to master
|
|
139
|
-
```bash
|
|
140
|
-
git checkout master
|
|
141
|
-
git pull origin master
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 2. Merge develop into master
|
|
145
|
-
```bash
|
|
146
|
-
git merge develop
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### 3. Bump the stable version
|
|
150
|
-
```bash
|
|
151
|
-
npm version patch
|
|
152
|
-
# or
|
|
153
|
-
npm version minor
|
|
154
|
-
# or
|
|
155
|
-
npm version major
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### 4. Push with tags
|
|
159
|
-
```bash
|
|
160
|
-
git push --follow-tags
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### 5. GitHub Action publishes stable build
|
|
164
|
-
```
|
|
165
|
-
npm publish
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### 6. Install stable version
|
|
169
|
-
```bash
|
|
170
|
-
npm install soxo-bootstrap-core
|
|
171
|
-
# or
|
|
172
|
-
npm install soxo-bootstrap-core@<version>
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
# 🟧 Publishing via GitHub Release UI
|
|
178
|
-
|
|
179
|
-
### Important
|
|
180
|
-
**The tag must match the `package.json` version exactly**, including the `v` prefix.
|
|
181
|
-
|
|
182
|
-
Example:
|
|
183
|
-
```
|
|
184
|
-
package.json: "version": "2.4.31-dev.0"
|
|
185
|
-
GitHub Tag: v2.4.31-dev.0
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
## Steps
|
|
191
|
-
|
|
192
|
-
### 1. Ensure `develop` branch is up to date
|
|
193
|
-
Push your changes.
|
|
194
|
-
|
|
195
|
-
### 2. Ensure version contains a dev suffix
|
|
196
|
-
If not:
|
|
197
|
-
```bash
|
|
198
|
-
npm version prerelease --preid=dev
|
|
199
|
-
git push --follow-tags
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 3. Open GitHub → Releases → Draft a new release
|
|
203
|
-
|
|
204
|
-
### 4. Create tag matching the version
|
|
205
|
-
Example:
|
|
206
|
-
```
|
|
207
|
-
v2.4.31-dev.0
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### 5. Select target branch = `develop`
|
|
211
|
-
|
|
212
|
-
### 6. Title = tag version
|
|
213
|
-
Description is optional.
|
|
214
|
-
|
|
215
|
-
### 7. Publish the release
|
|
216
|
-
Triggers:
|
|
217
|
-
```
|
|
218
|
-
npm publish --tag dev
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
# ⚙️ How GitHub Action Detects Release Type
|
|
224
|
-
|
|
225
|
-
If version contains `dev`:
|
|
226
|
-
```
|
|
227
|
-
npm publish --tag dev
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
Otherwise:
|
|
231
|
-
```
|
|
232
|
-
npm publish
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
# 📊 Summary Table
|
|
238
|
-
|
|
239
|
-
| Step | Branch | Command | Tag | Publish Type |
|
|
240
|
-
|--------------------------|----------|------------------------------------------|------------------|--------------|
|
|
241
|
-
| Create task branch | master | `git checkout -b task/<ID>` | — | — |
|
|
242
|
-
| Merge task → develop | develop | `git merge task/<ID>` | — | — |
|
|
243
|
-
| Dev version | develop | `npm version prerelease --preid=dev` | `vX.Y.Z-dev.N` | dev |
|
|
244
|
-
| Publish dev build | develop | `git push --follow-tags` | — | dev |
|
|
245
|
-
| Merge develop → master | master | `git merge develop` | — | — |
|
|
246
|
-
| Stable version | master | `npm version patch/minor/major` | `vX.Y.Z` | latest |
|
|
247
|
-
| Publish stable build | master | `git push --follow-tags` | — | latest |
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
# ⚠️ Common Mistakes & Fixes
|
|
252
|
-
|
|
253
|
-
| Mistake | Issue | Fix |
|
|
254
|
-
|-------------------------------|----------------------|---------------------------------|
|
|
255
|
-
| Tag doesn't match version | Publish fails | Always use `npm version` |
|
|
256
|
-
| Manual tag creation | Version mismatch | Avoid manual tagging |
|
|
257
|
-
| Dev publish from master | Wrong channel | Verify branch before tagging |
|
|
258
|
-
| Not pushing tags | Workflow won’t run | Use `git push --follow-tags` |
|
|
259
|
-
| Merging incomplete tasks | Breaks dev builds | Test before merging |
|
|
260
|
-
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useRef } from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Custom hook to manage an OTP (One-Time Password) countdown timer.
|
|
5
|
-
*
|
|
6
|
-
* Features:
|
|
7
|
-
* - Can start a timer directly with seconds.
|
|
8
|
-
* - Can start a timer using an expiry timestamp (from backend).
|
|
9
|
-
* - Provides formatted remaining time (MM:SS).
|
|
10
|
-
* - Tracks whether the timer has expired.
|
|
11
|
-
*
|
|
12
|
-
* @returns {Object} API
|
|
13
|
-
* @returns {number} API.remaining - Remaining seconds.
|
|
14
|
-
* @returns {boolean} API.expired - Whether the timer has expired.
|
|
15
|
-
* @returns {string} API.formatted - Formatted remaining time (MM:SS).
|
|
16
|
-
* @returns {(seconds: number) => void} API.start - Start timer with seconds.
|
|
17
|
-
* @returns {(expirytime: string) => void} API.startFromExpiry - Start timer using expiry string (e.g. "2025-09-04T12:13:09.000Z").
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* const { remaining, expired, formatted, start, startFromExpiry } = useOtpTimer();
|
|
21
|
-
*
|
|
22
|
-
* // Start with 30 seconds
|
|
23
|
-
* useEffect(() => {
|
|
24
|
-
* start(30);
|
|
25
|
-
* }, []);
|
|
26
|
-
*
|
|
27
|
-
* // OR start from backend expiry timestamp
|
|
28
|
-
* useEffect(() => {
|
|
29
|
-
* startFromExpiry("2025-09-04T12:13:09.000Z");
|
|
30
|
-
* }, []);
|
|
31
|
-
*
|
|
32
|
-
* return (
|
|
33
|
-
* <div>
|
|
34
|
-
* <p>Time left: {formatted}</p>
|
|
35
|
-
* {expired && <p>OTP expired!</p>}
|
|
36
|
-
* </div>
|
|
37
|
-
* );
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
// helper to format time
|
|
41
|
-
const formatTime = (seconds) => {
|
|
42
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
43
|
-
const secs = seconds % 60;
|
|
44
|
-
return `${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const useOtpTimer = () => {
|
|
48
|
-
const [remaining, setRemaining] = useState(0);
|
|
49
|
-
const [expired, setExpired] = useState(false);
|
|
50
|
-
const intervalRef = useRef(null);
|
|
51
|
-
|
|
52
|
-
// Start timer by passing expiry string from backend
|
|
53
|
-
const startFromExpiry = (expirytime) => {
|
|
54
|
-
if (!expirytime) {
|
|
55
|
-
setRemaining(0);
|
|
56
|
-
setExpired(true);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Handle backend expiry string
|
|
61
|
-
const expiryTime = new Date(expirytime).getTime();
|
|
62
|
-
const now = Date.now();
|
|
63
|
-
const seconds = Math.max(0, Math.floor((expiryTime - now) / 1000));
|
|
64
|
-
|
|
65
|
-
start(seconds);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Generic start (seconds)
|
|
69
|
-
const start = (seconds) => {
|
|
70
|
-
clearInterval(intervalRef.current);
|
|
71
|
-
|
|
72
|
-
if (!seconds || seconds <= 0) {
|
|
73
|
-
setRemaining(0);
|
|
74
|
-
setExpired(true);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
setRemaining(seconds);
|
|
79
|
-
setExpired(false);
|
|
80
|
-
|
|
81
|
-
intervalRef.current = setInterval(() => {
|
|
82
|
-
setRemaining((prev) => {
|
|
83
|
-
if (prev <= 1) {
|
|
84
|
-
clearInterval(intervalRef.current);
|
|
85
|
-
setExpired(true);
|
|
86
|
-
return 0;
|
|
87
|
-
}
|
|
88
|
-
return prev - 1;
|
|
89
|
-
});
|
|
90
|
-
}, 1000);
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// Cleanup on unmount
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
return () => clearInterval(intervalRef.current);
|
|
96
|
-
}, []);
|
|
97
|
-
|
|
98
|
-
return { remaining, expired, formatted: formatTime(remaining), start, startFromExpiry };
|
|
99
|
-
};
|
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
-
import './staff-add.scss';
|
|
3
|
-
import { Modal, Tabs, Input, Form, Row, Col, message, Checkbox, Select } from 'antd';
|
|
4
|
-
import { useTranslation, Button } from './../../../../lib/';
|
|
5
|
-
import { UsersAPI } from '../../..';
|
|
6
|
-
|
|
7
|
-
const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
|
|
8
|
-
const [form] = Form.useForm();
|
|
9
|
-
const { t } = useTranslation();
|
|
10
|
-
|
|
11
|
-
/** -------------------------------
|
|
12
|
-
* STATE
|
|
13
|
-
* ------------------------------- */
|
|
14
|
-
const [loading, setLoading] = useState(false);
|
|
15
|
-
const [designations, setDesignations] = useState([]);
|
|
16
|
-
const [isSubmitDisabled, setIsSubmitDisabled] = useState(true);
|
|
17
|
-
const [codeStatus, setCodeStatus] = useState({ status: '', message: '' });
|
|
18
|
-
|
|
19
|
-
const nameInputRef = useRef(null);
|
|
20
|
-
|
|
21
|
-
const editMode = Boolean(staffId);
|
|
22
|
-
|
|
23
|
-
/** -------------------------------
|
|
24
|
-
* FORM WATCHERS
|
|
25
|
-
* ------------------------------- */
|
|
26
|
-
const watchedName = Form.useWatch('shortName', form);
|
|
27
|
-
const watchedCode = Form.useWatch('id', form);
|
|
28
|
-
const watchedDesc = Form.useWatch('description', form);
|
|
29
|
-
|
|
30
|
-
/** -------------------------------
|
|
31
|
-
* EFFECTS
|
|
32
|
-
* ------------------------------- */
|
|
33
|
-
|
|
34
|
-
// Auto-focus on open
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (visible) {
|
|
37
|
-
setTimeout(() => nameInputRef.current?.focus(), 200);
|
|
38
|
-
}
|
|
39
|
-
}, [visible]);
|
|
40
|
-
|
|
41
|
-
// Control submit button
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
setIsSubmitDisabled(!(watchedName && watchedCode && watchedDesc));
|
|
44
|
-
}, [watchedName, watchedCode, watchedDesc]);
|
|
45
|
-
|
|
46
|
-
// Load designations when modal opens
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
if (visible) getDesignations();
|
|
49
|
-
}, [visible]);
|
|
50
|
-
|
|
51
|
-
// Reset or fill edit form
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
if (visible) {
|
|
54
|
-
if (editMode && staffData) fillEditForm(staffData);
|
|
55
|
-
} else {
|
|
56
|
-
form.resetFields();
|
|
57
|
-
}
|
|
58
|
-
}, [visible]);
|
|
59
|
-
|
|
60
|
-
/** -------------------------------
|
|
61
|
-
* API CALL – Designations
|
|
62
|
-
* ------------------------------- */
|
|
63
|
-
const getDesignations = () => {
|
|
64
|
-
setLoading(true);
|
|
65
|
-
UsersAPI.getDesignations()
|
|
66
|
-
.then((res) => {
|
|
67
|
-
if (res?.success && Array.isArray(res.result)) {
|
|
68
|
-
const formatted = res.result.map((ele) => ({
|
|
69
|
-
label: ele.dg_desc?.trim() || '',
|
|
70
|
-
value: ele.dg_code,
|
|
71
|
-
}));
|
|
72
|
-
setDesignations(formatted);
|
|
73
|
-
} else {
|
|
74
|
-
setDesignations([]);
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
.catch((err) => {
|
|
78
|
-
console.error('Error fetching designations:', err);
|
|
79
|
-
setDesignations([]);
|
|
80
|
-
})
|
|
81
|
-
.finally(() => setLoading(false));
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
/** -------------------------------
|
|
85
|
-
* VALIDATION – Code
|
|
86
|
-
* ------------------------------- */
|
|
87
|
-
const validateDoctorCode = async (_, value) => {
|
|
88
|
-
if (editMode) return Promise.resolve(); // Skip validation if editing
|
|
89
|
-
|
|
90
|
-
if (!value) {
|
|
91
|
-
setCodeStatus({ status: '', message: '' });
|
|
92
|
-
return Promise.resolve();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const res = await UsersAPI.getStaffCode(value);
|
|
97
|
-
|
|
98
|
-
if (res?.status === 409 || res?.success === false) {
|
|
99
|
-
setCodeStatus({ status: 'error', message: res.message || 'Code already exists' });
|
|
100
|
-
return Promise.reject(new Error(res.message || 'Code already exists'));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (res?.status === 200 && res?.success === true) {
|
|
104
|
-
setCodeStatus({
|
|
105
|
-
status: 'success',
|
|
106
|
-
message: 'The entered code is valid for assigning a staff.',
|
|
107
|
-
});
|
|
108
|
-
return Promise.resolve();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
setCodeStatus({ status: 'error', message: 'Unexpected response' });
|
|
112
|
-
return Promise.reject(new Error('Unexpected response'));
|
|
113
|
-
} catch {
|
|
114
|
-
setCodeStatus({ status: 'error', message: 'Validation failed' });
|
|
115
|
-
return Promise.reject(new Error('Validation failed'));
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
/** -------------------------------
|
|
120
|
-
* UTIL – Enter Key Navigation
|
|
121
|
-
* ------------------------------- */
|
|
122
|
-
const handleEnterKey = (e) => {
|
|
123
|
-
if (e.key === 'Enter') {
|
|
124
|
-
e.preventDefault();
|
|
125
|
-
const fields = Array.from(document.querySelectorAll('.ant-input'));
|
|
126
|
-
const index = fields.indexOf(e.target);
|
|
127
|
-
if (index > -1 && index < fields.length - 1) {
|
|
128
|
-
fields[index + 1].focus();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/** -------------------------------
|
|
134
|
-
* FORM FILL – Edit Mode
|
|
135
|
-
* ------------------------------- */
|
|
136
|
-
const fillEditForm = (data) => {
|
|
137
|
-
form.setFieldsValue({
|
|
138
|
-
id: data.id,
|
|
139
|
-
shortName: data.shortName,
|
|
140
|
-
description: data.description,
|
|
141
|
-
designation: data.designationPtr,
|
|
142
|
-
phone1: data.phone,
|
|
143
|
-
phone2: data.mobile || data.alternateMobile,
|
|
144
|
-
email1: data.email,
|
|
145
|
-
email2: data.alternateEmail,
|
|
146
|
-
slno: data.slNo,
|
|
147
|
-
active: data.active === 'Y' ? 'Y' : 'N',
|
|
148
|
-
remarks: data.remarks,
|
|
149
|
-
address1: data.address1,
|
|
150
|
-
address2: data.address2,
|
|
151
|
-
place: data.place,
|
|
152
|
-
zip: data.zip,
|
|
153
|
-
});
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/** -------------------------------
|
|
157
|
-
* SUBMIT HANDLER
|
|
158
|
-
* ------------------------------- */
|
|
159
|
-
const handleFinish = async (values) => {
|
|
160
|
-
setLoading(true);
|
|
161
|
-
|
|
162
|
-
const payload = {
|
|
163
|
-
id: values.id,
|
|
164
|
-
shortName: values.shortName,
|
|
165
|
-
description: values.description,
|
|
166
|
-
designationPtr: values.designation,
|
|
167
|
-
phone: values.phone1,
|
|
168
|
-
mobile: values.phone2,
|
|
169
|
-
alternateMobile: values.phone2,
|
|
170
|
-
email: values.email1,
|
|
171
|
-
alternateEmail: values.email2,
|
|
172
|
-
remarks: values.remarks,
|
|
173
|
-
slNo: values.slno ? Number(values.slno) : null,
|
|
174
|
-
active: values.active === 'Y' ? 'Y' : 'N',
|
|
175
|
-
address1: values.address1,
|
|
176
|
-
address2: values.address2,
|
|
177
|
-
place: values.place,
|
|
178
|
-
zip: values.zip,
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
const res = editMode ? await UsersAPI.updateStaff(payload, staffData.id) : await UsersAPI.createStaff(payload);
|
|
183
|
-
|
|
184
|
-
if (res?.success) {
|
|
185
|
-
message.success(editMode ? 'Staff updated successfully' : 'Staff created successfully');
|
|
186
|
-
onSuccess?.();
|
|
187
|
-
onCancel();
|
|
188
|
-
} else {
|
|
189
|
-
message.error(res?.message || 'Operation failed');
|
|
190
|
-
}
|
|
191
|
-
} catch {
|
|
192
|
-
message.error('Something went wrong');
|
|
193
|
-
} finally {
|
|
194
|
-
setLoading(false);
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
/** -------------------------------
|
|
199
|
-
* RENDER
|
|
200
|
-
* ------------------------------- */
|
|
201
|
-
return (
|
|
202
|
-
<Modal
|
|
203
|
-
title={editMode ? 'Edit Staff' : 'Add Staff'}
|
|
204
|
-
open={visible}
|
|
205
|
-
onCancel={onCancel}
|
|
206
|
-
width={950}
|
|
207
|
-
footer={null}
|
|
208
|
-
destroyOnClose
|
|
209
|
-
style={{ top: 10 }}
|
|
210
|
-
>
|
|
211
|
-
<Form form={form} layout="vertical" onFinish={handleFinish}>
|
|
212
|
-
{/* TOP SECTION */}
|
|
213
|
-
<Row gutter={16}>
|
|
214
|
-
<Col span={6}>
|
|
215
|
-
<Form.Item
|
|
216
|
-
label="Code"
|
|
217
|
-
name="id"
|
|
218
|
-
validateTrigger="onChange"
|
|
219
|
-
hasFeedback={!editMode}
|
|
220
|
-
rules={[{ required: true }, { validator: validateDoctorCode }]}
|
|
221
|
-
>
|
|
222
|
-
<Input placeholder="Enter Code" ref={nameInputRef} onKeyDown={handleEnterKey} disabled={editMode} />
|
|
223
|
-
</Form.Item>
|
|
224
|
-
</Col>
|
|
225
|
-
|
|
226
|
-
<Col span={6}>
|
|
227
|
-
<Form.Item label="Name" name="shortName" rules={[{ required: true }]}>
|
|
228
|
-
<Input placeholder="Enter Name" onKeyDown={handleEnterKey} />
|
|
229
|
-
</Form.Item>
|
|
230
|
-
</Col>
|
|
231
|
-
|
|
232
|
-
<Col span={12}>
|
|
233
|
-
<Form.Item label="Description" name="description" rules={[{ required: true }]}>
|
|
234
|
-
<Input placeholder="Enter Description" onKeyDown={handleEnterKey} />
|
|
235
|
-
</Form.Item>
|
|
236
|
-
</Col>
|
|
237
|
-
</Row>
|
|
238
|
-
|
|
239
|
-
<Tabs defaultActiveKey="1">
|
|
240
|
-
{/* GENERAL TAB */}
|
|
241
|
-
<Tabs.TabPane tab="GENERAL" key="1">
|
|
242
|
-
<Row gutter={16}>
|
|
243
|
-
<Col span={6}>
|
|
244
|
-
<Form.Item label="Phone Number 1" name="phone1">
|
|
245
|
-
<Input maxLength={10} placeholder="Enter Phone Number" onKeyDown={handleEnterKey} />
|
|
246
|
-
</Form.Item>
|
|
247
|
-
</Col>
|
|
248
|
-
|
|
249
|
-
<Col span={6}>
|
|
250
|
-
<Form.Item label="Phone Number 2" name="phone2">
|
|
251
|
-
<Input maxLength={10} placeholder="Enter Phone Number" onKeyDown={handleEnterKey} />
|
|
252
|
-
</Form.Item>
|
|
253
|
-
</Col>
|
|
254
|
-
|
|
255
|
-
<Col span={6}>
|
|
256
|
-
<Form.Item label="Email ID 1" name="email1">
|
|
257
|
-
<Input placeholder="Enter Email" onKeyDown={handleEnterKey} />
|
|
258
|
-
</Form.Item>
|
|
259
|
-
</Col>
|
|
260
|
-
|
|
261
|
-
<Col span={6}>
|
|
262
|
-
<Form.Item label="Email ID 2" name="email2">
|
|
263
|
-
<Input placeholder="Enter Email" onKeyDown={handleEnterKey} />
|
|
264
|
-
</Form.Item>
|
|
265
|
-
</Col>
|
|
266
|
-
</Row>
|
|
267
|
-
|
|
268
|
-
<Row gutter={16}>
|
|
269
|
-
<Col span={8}>
|
|
270
|
-
<Form.Item name="designation" label="Designation">
|
|
271
|
-
<Select placeholder="Select Designation" options={designations} allowClear showSearch optionFilterProp="label" />
|
|
272
|
-
</Form.Item>
|
|
273
|
-
</Col>
|
|
274
|
-
|
|
275
|
-
<Col span={8}>
|
|
276
|
-
<Form.Item label="Serial Number" name="slno">
|
|
277
|
-
<Input placeholder="Enter Serial Number" onKeyDown={handleEnterKey} />
|
|
278
|
-
</Form.Item>
|
|
279
|
-
</Col>
|
|
280
|
-
|
|
281
|
-
<Col span={8}>
|
|
282
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 30 }}>
|
|
283
|
-
<label>Active</label>
|
|
284
|
-
<Form.Item
|
|
285
|
-
name="active"
|
|
286
|
-
valuePropName="checked"
|
|
287
|
-
getValueFromEvent={(e) => (e.target.checked ? 'Y' : 'N')}
|
|
288
|
-
getValueProps={(value) => ({ checked: value === 'Y' })}
|
|
289
|
-
style={{ marginBottom: 0 }}
|
|
290
|
-
>
|
|
291
|
-
<Checkbox />
|
|
292
|
-
</Form.Item>
|
|
293
|
-
</div>
|
|
294
|
-
</Col>
|
|
295
|
-
</Row>
|
|
296
|
-
|
|
297
|
-
<Row>
|
|
298
|
-
<Col span={24}>
|
|
299
|
-
<Form.Item label="Remarks" name="remarks">
|
|
300
|
-
<Input placeholder="Enter Remarks" onKeyDown={handleEnterKey} />
|
|
301
|
-
</Form.Item>
|
|
302
|
-
</Col>
|
|
303
|
-
</Row>
|
|
304
|
-
</Tabs.TabPane>
|
|
305
|
-
|
|
306
|
-
{/* ADDRESS TAB */}
|
|
307
|
-
<Tabs.TabPane tab="ADDRESS" key="2">
|
|
308
|
-
<Row gutter={16}>
|
|
309
|
-
<Col span={12}>
|
|
310
|
-
<Form.Item label="Address Line 1" name="address1">
|
|
311
|
-
<Input placeholder="Enter Address" onKeyDown={handleEnterKey} />
|
|
312
|
-
</Form.Item>
|
|
313
|
-
</Col>
|
|
314
|
-
|
|
315
|
-
<Col span={12}>
|
|
316
|
-
<Form.Item label="Address Line 2" name="address2">
|
|
317
|
-
<Input placeholder="Enter Address" onKeyDown={handleEnterKey} />
|
|
318
|
-
</Form.Item>
|
|
319
|
-
</Col>
|
|
320
|
-
</Row>
|
|
321
|
-
|
|
322
|
-
<Row gutter={16}>
|
|
323
|
-
<Col span={12}>
|
|
324
|
-
<Form.Item label="Place" name="place">
|
|
325
|
-
<Input placeholder="Enter Place" onKeyDown={handleEnterKey} />
|
|
326
|
-
</Form.Item>
|
|
327
|
-
</Col>
|
|
328
|
-
|
|
329
|
-
<Col span={12}>
|
|
330
|
-
<Form.Item label="Zip Code" name="zip">
|
|
331
|
-
<Input placeholder="Enter Zip Code" maxLength={7} onKeyDown={handleEnterKey} />
|
|
332
|
-
</Form.Item>
|
|
333
|
-
</Col>
|
|
334
|
-
</Row>
|
|
335
|
-
</Tabs.TabPane>
|
|
336
|
-
</Tabs>
|
|
337
|
-
|
|
338
|
-
{/* FOOTER BUTTONS */}
|
|
339
|
-
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
|
|
340
|
-
<Button onClick={onCancel} type="secondary">
|
|
341
|
-
Cancel
|
|
342
|
-
</Button>
|
|
343
|
-
<Button type="primary" htmlType="submit" loading={loading} disabled={isSubmitDisabled}>
|
|
344
|
-
{editMode ? 'Update' : 'Save'}
|
|
345
|
-
</Button>
|
|
346
|
-
</div>
|
|
347
|
-
</Form>
|
|
348
|
-
</Modal>
|
|
349
|
-
);
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
export default StaffAdd;
|
|
File without changes
|