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.
Files changed (31) hide show
  1. package/.github/workflows/npm-publish.yml +15 -37
  2. package/core/components/extra-info/extra-info-details.js +126 -109
  3. package/core/components/landing-api/landing-api.js +30 -22
  4. package/core/lib/Store.js +18 -20
  5. package/core/lib/components/index.js +1 -4
  6. package/core/lib/components/sidemenu/sidemenu.js +256 -153
  7. package/core/lib/components/sidemenu/sidemenu.scss +26 -39
  8. package/core/lib/hooks/index.js +12 -2
  9. package/core/lib/pages/login/login.js +139 -255
  10. package/core/lib/pages/login/login.scss +32 -140
  11. package/core/models/dashboard/dashboard.js +0 -14
  12. package/core/models/menus/components/menu-add/menu-add.js +268 -230
  13. package/core/models/menus/components/menu-lists/menu-lists.js +89 -126
  14. package/core/models/menus/components/menu-lists/menu-lists.scss +0 -9
  15. package/core/models/menus/menus.js +267 -247
  16. package/core/models/roles/components/role-add/role-add.js +227 -269
  17. package/core/models/roles/components/role-list/role-list.js +6 -8
  18. package/core/models/roles/roles.js +174 -182
  19. package/core/models/users/components/user-add/user-add.js +1 -69
  20. package/core/models/users/users.js +0 -57
  21. package/core/modules/index.js +13 -23
  22. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +1 -1
  23. package/package.json +2 -2
  24. package/README.md +0 -260
  25. package/core/lib/hooks/use-otp-timer.js +0 -99
  26. package/core/models/staff/components/staff-add/staff-add.js +0 -352
  27. package/core/models/staff/components/staff-add/staff-add.scss +0 -0
  28. package/core/modules/steps/action-buttons.js +0 -79
  29. package/core/modules/steps/steps.js +0 -553
  30. package/core/modules/steps/steps.scss +0 -158
  31. 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;