ui-soxo-bootstrap-core 2.6.32-dev.2 → 2.6.32-dev.6

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,6 +1,3 @@
1
- # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
- # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3
-
4
1
  name: Node.js Package
5
2
 
6
3
  on:
@@ -8,26 +5,55 @@ on:
8
5
  types: [created]
9
6
 
10
7
  jobs:
11
- build:
12
- runs-on: ubuntu-latest
13
- steps:
14
- - uses: actions/checkout@v3
15
- - uses: actions/setup-node@v3
16
- with:
17
- node-version: 16
18
- - run: npm i
19
- # - run: npm test
20
-
21
8
  publish-npm:
22
- needs: build
23
9
  runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ id-token: write
24
13
  steps:
25
- - uses: actions/checkout@v3
26
- - uses: actions/setup-node@v3
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-node@v4
27
16
  with:
28
- node-version: 16
17
+ node-version: 20
29
18
  registry-url: https://registry.npmjs.org/
30
- - run: npm i
31
- - run: npm publish
32
- env:
33
- NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
19
+ - run: npm install -g npm@latest
20
+ - run: npm install
21
+
22
+ - name: Determine npm dist-tag
23
+ id: dist_tag
24
+ shell: bash
25
+ run: |
26
+ VERSION=$(node -p "require('./package.json').version")
27
+ echo "package.json version: $VERSION"
28
+ echo "release tag: ${GITHUB_REF_NAME}"
29
+ if [[ "v${VERSION}" != "${GITHUB_REF_NAME}" ]]; then
30
+ echo "::error::Release tag '${GITHUB_REF_NAME}' does not match package.json version 'v${VERSION}'."
31
+ echo "::error::Bump the version with 'npm version' and re-create the release."
32
+ exit 1
33
+ fi
34
+ if [[ "$VERSION" == *-dev* ]]; then
35
+ echo "tag=dev" >> "$GITHUB_OUTPUT"
36
+ echo "Will publish with dist-tag: dev"
37
+ else
38
+ echo "tag=latest" >> "$GITHUB_OUTPUT"
39
+ echo "Will publish with dist-tag: latest"
40
+ fi
41
+
42
+ - name: Diagnose npm + OIDC environment
43
+ shell: bash
44
+ run: |
45
+ echo "--- versions ---"
46
+ node --version
47
+ npm --version
48
+ echo "--- OIDC env presence (must both be 'yes' for trusted publishing) ---"
49
+ echo "ACTIONS_ID_TOKEN_REQUEST_URL set: ${ACTIONS_ID_TOKEN_REQUEST_URL:+yes}"
50
+ echo "ACTIONS_ID_TOKEN_REQUEST_TOKEN set: ${ACTIONS_ID_TOKEN_REQUEST_TOKEN:+yes}"
51
+ echo "--- effective .npmrc (user) ---"
52
+ cat ~/.npmrc 2>/dev/null || echo "(none)"
53
+ echo "--- effective .npmrc (project) ---"
54
+ cat .npmrc 2>/dev/null || echo "(none)"
55
+ echo "--- npm config (auth-related) ---"
56
+ npm config get registry
57
+ npm config get //registry.npmjs.org/:_authToken || true
58
+
59
+ - run: npm publish --provenance --access public --tag ${{ steps.dist_tag.outputs.tag }} --loglevel=verbose
@@ -17,6 +17,7 @@ Incorrect versioning or incorrect tags will break the publish pipeline — follo
17
17
  - Publishing via GitHub Release UI
18
18
  - How GitHub Action Detects Release Type
19
19
  - Summary Table
20
+ - CI/CD Authentication (Trusted Publishing)
20
21
  - Common Mistakes & Fixes
21
22
 
22
23
  ---
@@ -255,17 +256,14 @@ npm publish --tag dev
255
256
 
256
257
  # ⚙️ How GitHub Action Detects Release Type
257
258
 
258
- If version contains `dev`:
259
+ The workflow reads the `version` field from `package.json` at publish time:
259
260
 
260
- ```
261
- npm publish --tag dev
262
- ```
261
+ | Condition | Command | Result |
262
+ | ---------------------------- | ------------------------------------------------------- | ---------------------------------- |
263
+ | Version contains `-dev` | `npm publish --provenance --access public --tag dev` | Publishes to the `dev` dist-tag |
264
+ | Version has no `-dev` suffix | `npm publish --provenance --access public --tag latest` | Publishes to the `latest` dist-tag |
263
265
 
264
- Otherwise:
265
-
266
- ```
267
- npm publish
268
- ```
266
+ The workflow also enforces that the GitHub release tag matches `v<version>` from `package.json` and fails the run immediately if they diverge — this prevents the most common publish failure described below.
269
267
 
270
268
  ---
271
269
 
@@ -283,6 +281,37 @@ npm publish
283
281
 
284
282
  ---
285
283
 
284
+ # 🔐 CI/CD Authentication (Trusted Publishing)
285
+
286
+ As of npm's 2025 policy changes, classic automation tokens (`NPM_TOKEN`) are deprecated. This repo now authenticates to npm via **OIDC Trusted Publishing** — GitHub Actions exchanges a short-lived OIDC token for a publish token at run time, so **no secret is stored in the repository**.
287
+
288
+ ## What this means for developers
289
+
290
+ Nothing. You still follow the same flow: `npm version` → push tag → create GitHub Release. The auth happens transparently in CI.
291
+
292
+ ## What this means for maintainers
293
+
294
+ The first-time setup on npmjs.com must be done once per package:
295
+
296
+ 1. Log in to [npmjs.com](https://www.npmjs.com) → open the package (`ui-soxo-bootstrap-core`) → **Settings**.
297
+ 2. Under **Trusted Publisher**, click **Add trusted publisher** and fill in:
298
+ - Publisher: **GitHub Actions**
299
+ - Organization or user: `soxo-tech`
300
+ - Repository: `bootstrap-core`
301
+ - Workflow filename: `npm-publish.yml`
302
+ - Environment name: *(leave blank)*
303
+ 3. Save. Any old `NPM_TOKEN` repository secret can be removed.
304
+
305
+ ## Runtime requirements
306
+
307
+ The workflow runs on Node 20 and upgrades npm to the latest CLI (`npm install -g npm@latest`) because OIDC trusted publishing requires **npm ≥ 11.5.1**. The `--provenance` flag attaches a verifiable build attestation to every published version, visible on the npmjs.com package page.
308
+
309
+ ## If publish fails with `403 Forbidden` or `ENEEDAUTH`
310
+
311
+ The trusted publisher config on npmjs.com no longer matches the workflow. Check that org, repo, and workflow filename match exactly — including case.
312
+
313
+ ---
314
+
286
315
  # ⚠️ Common Mistakes & Fixes
287
316
 
288
317
  | Mistake | Issue | Fix |
@@ -239,11 +239,11 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
239
239
  {licAlert && licenseData && (
240
240
  <div
241
241
  style={{
242
- // top: 0,
242
+ top: 0,
243
243
  marginTop: '3rem',
244
244
  right: '2%',
245
245
  position: 'absolute',
246
- zIndex: 999,
246
+ zIndex: 1008,
247
247
  }}
248
248
  >
249
249
  <LicenseAlert data={licenseData} />
@@ -58,12 +58,7 @@ const TableComponent = ({ columns, dataSource, loading, fixed, scroll, summary,
58
58
 
59
59
  return (
60
60
  <Table
61
- title={() =>
62
- // <div style={{ fontWeight: 'bold', fontSize: 16, padding: '8px 16px' }}>
63
- title ? title : ''
64
- // </div>
65
- }
66
- // columns={updatedColumns}
61
+ title={title ? () => title : undefined}
67
62
  scroll={scroll}
68
63
  dataSource={dataSource}
69
64
  loading={loading}
@@ -32,7 +32,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
32
32
  // for default branch
33
33
  // const [defaultBranch, setDefaultBranch] = useState(null);
34
34
  //Need to check this condition
35
- const [authentication, setAuthentication] = useState(false);
35
+ const [authentication, setAuthentication] = useState(true);
36
36
 
37
37
  /**To store user values */
38
38
  const [users, setUsers] = useState([]);
@@ -151,8 +151,10 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
151
151
 
152
152
  /**if firmmas.otherdetails - 2FA == OPT, then, 2FA option will be enabled by default and editable */
153
153
  } else if (firmDetails.FA == 'USR') {
154
- setAuthentication(true);
155
-
154
+ /** If user has FA set to false , then disable authentication */
155
+ if (formContent?.FA !== undefined) {
156
+ setAuthentication(formContent?.FA);
157
+ }
156
158
  setDisabled(true);
157
159
 
158
160
  /**if firmmas.otherdetails - 2FA == NAP, then, 2FA option will be disabled by default and read-only*/
@@ -383,7 +385,8 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
383
385
  ...values,
384
386
  auth_type: 'LDAP',
385
387
  mobile: mobileWithCountryCode,
386
- FA: authentication,
388
+ FA: formContent?.FA === false ? false : authentication,
389
+
387
390
  };
388
391
 
389
392
  setLoading(true);
@@ -396,7 +399,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
396
399
  addAllBranches: props.ldap.addAllBranches,
397
400
  auth_user: selectedOption.value,
398
401
  auth_type: 'LDAP',
399
- FA: authentication,
402
+ FA: formContent?.FA === false ? false : authentication,
400
403
  };
401
404
  }
402
405
 
@@ -68,7 +68,7 @@ export default function UserEdit(record) {
68
68
  doctor_code: apiData.doctor_code,
69
69
  staff_code: apiData.staff_id,
70
70
  auth_type: apiData.auth_type,
71
- FA: apiData.FA,
71
+ FA: otherDetails.FA,
72
72
  active: apiData.active ? true : false,
73
73
  };
74
74
  // Set form data state
@@ -292,6 +292,7 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
292
292
  const [steps, setSteps] = useState([]);
293
293
  const [processName, setProcessName] = useState(null);
294
294
  const [activeStep, setActiveStep] = useState(0);
295
+ const [resumableStep, setResumableStep] = useState(null);
295
296
  const [isStepCompleted, setIsStepCompleted] = useState(false);
296
297
 
297
298
  const [nextProcessId, setNextProcessId] = useState(null);
@@ -353,11 +354,46 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
353
354
 
354
355
  setProcessTimings(savedTimings);
355
356
 
357
+ let savedActiveStep = 0;
358
+ try {
359
+ const rawStep = localStorage.getItem(`processActiveStep_${currentProcessId}`);
360
+ const parsedStep = rawStep == null ? NaN : parseInt(rawStep, 10);
361
+ if (Number.isFinite(parsedStep) && parsedStep > 0) {
362
+ savedActiveStep = parsedStep;
363
+ }
364
+ } catch (error) {
365
+ console.warn('Unable to restore active step from local storage.', error);
366
+ }
367
+ setResumableStep(savedActiveStep > 0 ? savedActiveStep : null);
368
+
356
369
  setProcessStartTime(Date.now());
357
370
  setStepStartTime(Date.now());
358
371
  setShowNextProcessAction(false);
359
372
  }, [currentProcessId]);
360
373
 
374
+ /**
375
+ * Persist the active step so an unexpected exit (refresh, tab close, crash)
376
+ * can offer a Resume action on the next mount. Only steps beyond the first
377
+ * are persisted — step 0 is the natural entry point and does not need a
378
+ * resume marker. The marker is cleared on successful process submission and
379
+ * when the user explicitly dismisses the resume banner.
380
+ */
381
+ useEffect(() => {
382
+ if (typeof window === 'undefined' || !window.localStorage || !currentProcessId) {
383
+ return;
384
+ }
385
+
386
+ if (activeStep <= 0) {
387
+ return;
388
+ }
389
+
390
+ try {
391
+ localStorage.setItem(`processActiveStep_${currentProcessId}`, String(activeStep));
392
+ } catch (error) {
393
+ console.warn('Unable to persist active step to local storage.', error);
394
+ }
395
+ }, [activeStep, currentProcessId]);
396
+
361
397
  /**
362
398
  * Sync the loaded process name into the address bar.
363
399
  * - Mirrors `processName` into a `process` query parameter so deep-links and
@@ -599,10 +635,12 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
599
635
  if (response.success) {
600
636
  try {
601
637
  localStorage.removeItem(`processTimings_${currentProcessId}`);
638
+ localStorage.removeItem(`processActiveStep_${currentProcessId}`);
602
639
  } catch (error) {
603
640
  console.warn('Unable to clear process timings from local storage.', error);
604
641
  }
605
642
  setProcessTimings([]);
643
+ setResumableStep(null);
606
644
  return true;
607
645
  }
608
646
  } catch (e) {
@@ -659,6 +697,33 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
659
697
  * - Records timing data for the current step.
660
698
  */
661
699
  const handleTimelineClick = (i) => gotoStep(i);
700
+ /**
701
+ * Resume Handlers
702
+ * - `handleResume` jumps to the persisted step (clamped to current step
703
+ * range) so a returning user picks up where they left off.
704
+ * - `dismissResume` discards the saved marker so the banner does not appear
705
+ * again for this process.
706
+ */
707
+ const handleResume = () => {
708
+ if (resumableStep == null || !steps.length) {
709
+ return;
710
+ }
711
+ const target = Math.max(0, Math.min(resumableStep, steps.length - 1));
712
+ setResumableStep(null);
713
+ if (target !== activeStep) {
714
+ gotoStep(target);
715
+ }
716
+ };
717
+ const dismissResume = () => {
718
+ setResumableStep(null);
719
+ try {
720
+ if (typeof window !== 'undefined' && window.localStorage && currentProcessId) {
721
+ localStorage.removeItem(`processActiveStep_${currentProcessId}`);
722
+ }
723
+ } catch (error) {
724
+ console.warn('Unable to clear resume marker from local storage.', error);
725
+ }
726
+ };
662
727
  /**
663
728
  * Process Completion
664
729
  * - Records final step timing.
@@ -1425,6 +1490,33 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
1425
1490
  </div>
1426
1491
  </div>
1427
1492
 
1493
+ {/*
1494
+ Resume banner.
1495
+ - Renders only when a saved active step is ahead of the current
1496
+ position, so it stays out of the way during normal navigation
1497
+ and only surfaces when the user returns after an unexpected
1498
+ exit (refresh, tab close, navigation away).
1499
+ - Resuming clamps to the current step range; dismissing clears
1500
+ the persisted marker so the banner does not return for this
1501
+ process.
1502
+ */}
1503
+ {resumableStep != null && resumableStep > activeStep && resumableStep < steps.length ? (
1504
+ <div className="steps-resume-banner" role="status">
1505
+ <span className="steps-resume-banner-text">
1506
+ You left at Step {resumableStep + 1}
1507
+ {steps[resumableStep]?.step_name ? ` — ${steps[resumableStep].step_name}` : ''}.
1508
+ </span>
1509
+ <div className="steps-resume-banner-actions">
1510
+ <Button type="primary" size="small" onClick={handleResume}>
1511
+ Resume
1512
+ </Button>
1513
+ <Button type="text" size="small" onClick={dismissResume}>
1514
+ Dismiss
1515
+ </Button>
1516
+ </div>
1517
+ </div>
1518
+ ) : null}
1519
+
1428
1520
  <div className={`steps-content-panel${isStepFullscreen ? ' is-fullscreen' : ''}`}>
1429
1521
  {/*
1430
1522
  Stage body:
@@ -212,6 +212,37 @@
212
212
  height: 3px;
213
213
  }
214
214
 
215
+ /* ── Resume banner ──────────────────────────────────────── */
216
+
217
+ .steps-resume-banner {
218
+ flex: 0 0 auto;
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: space-between;
222
+ gap: 12px;
223
+ padding: 8px 14px;
224
+ background: #fff8e1;
225
+ border-bottom: 1px solid #f5e3a3;
226
+ color: #5b4400;
227
+ font-size: 13px;
228
+ font-weight: 500;
229
+ }
230
+
231
+ .steps-resume-banner-text {
232
+ flex: 1 1 auto;
233
+ min-width: 0;
234
+ overflow: hidden;
235
+ text-overflow: ellipsis;
236
+ white-space: nowrap;
237
+ }
238
+
239
+ .steps-resume-banner-actions {
240
+ flex: 0 0 auto;
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 6px;
244
+ }
245
+
215
246
  .steps-breadcrumb-strip::-webkit-scrollbar-track {
216
247
  background: transparent;
217
248
  }
@@ -474,13 +505,17 @@
474
505
 
475
506
  @media (prefers-reduced-motion: reduce) {
476
507
  .steps-touch-nav {
477
- transition: opacity 120ms linear, visibility 0s linear 120ms;
508
+ transition:
509
+ opacity 120ms linear,
510
+ visibility 0s linear 120ms;
478
511
  transform: translateY(-50%);
479
512
  }
480
513
 
481
514
  .steps-touch-nav.is-visible {
482
515
  transform: translateY(-50%);
483
- transition: opacity 120ms linear, visibility 0s linear 0s;
516
+ transition:
517
+ opacity 120ms linear,
518
+ visibility 0s linear 0s;
484
519
  }
485
520
 
486
521
  .steps-touch-nav.is-visible:not(:disabled)::before {
@@ -635,7 +670,7 @@
635
670
  }
636
671
 
637
672
  .steps-chat-step-component {
638
- margin-top: 10px;
673
+ // margin-top: 10px;
639
674
  display: flex;
640
675
  flex: 1 1 auto;
641
676
  flex-direction: column;
@@ -730,14 +765,14 @@
730
765
 
731
766
  .steps-viewport:fullscreen .steps-stage-body,
732
767
  .steps-viewport:-webkit-full-screen .steps-stage-body {
733
- padding: 6px;
768
+ padding: 3px;
734
769
  }
735
770
 
736
771
  /* ── Small laptops (13" / 1366px and below) ─────────────── */
737
772
 
738
773
  @media (max-width: 1366px) {
739
774
  .steps-top-bar {
740
- padding: 6px 10px;
775
+ // padding: 6px 10px;
741
776
  gap: 6px;
742
777
  }
743
778
 
@@ -768,7 +803,7 @@
768
803
  }
769
804
 
770
805
  .steps-chat-step-card {
771
- padding: 12px 14px;
806
+ padding: 9px;
772
807
  }
773
808
 
774
809
  .steps-title {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.6.32-dev.2",
3
+ "version": "2.6.32-dev.6",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"