@eintrek/erp-shell 0.1.72 → 0.1.73

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.
@@ -357,6 +357,16 @@ function publishGuideEntry(next, openPanel) {
357
357
  emit();
358
358
  }
359
359
  }
360
+ /**
361
+ * Whether the "?" menu offers "ไกด์เริ่มต้น": a tour whose missions are all
362
+ * hidden for this user (`visible`, e.g. Plannex without an employee record)
363
+ * has nothing to open, and "0/0" led to an empty panel. The entry itself
364
+ * stays published: apps wait on it before they load the facts that make
365
+ * missions visible.
366
+ */
367
+ function guideMenuShown(e) {
368
+ return !!e && e.total > 0;
369
+ }
360
370
  /** Opens the tour's quest panel, when this user has a tour here. */
361
371
  function openGuidePanel() {
362
372
  openPanelImpl?.();
@@ -823,6 +833,7 @@ function enterStep(d, suite, env, mission, index, opts) {
823
833
  const where = placeRoute(steps, i);
824
834
  const away = !!where && !matchRoute(where, env.pathname, env.search, env.code);
825
835
  const bridge = !away && needsBridge(step, env, ctx);
836
+ const nts = !away && !bridge && nothingToSaveHolds(step, env, ctx);
826
837
  setScreen(d, {
827
838
  name: "step",
828
839
  mission: mission.key,
@@ -834,7 +845,9 @@ function enterStep(d, suite, env, mission, index, opts) {
834
845
  confirmExit: false,
835
846
  misses: 0,
836
847
  armed: hiddenAnchorShown(step, env),
837
- nothingToSave: !away && !bridge && nothingToSaveHolds(step, env, ctx),
848
+ nothingToSave: nts,
849
+ status: away || bridge ? null : stepStatus(shownStep(step, nts), ctx),
850
+ replayNext: !away && !bridge && opts.replay && replayCanGoOn(mission, i, env, d.state.progress.context, nts),
838
851
  });
839
852
  if (!opts.replay) {
840
853
  const cp = checkpointAtOrBefore(steps, i);
@@ -877,6 +890,54 @@ function setNothingToSave(d, suite, env, on) {
877
890
  if (stepTargetVisible(step, env, ctx, s.bridge))
878
891
  announceStep(d, suite, env);
879
892
  }
893
+ /** The step's status line (GuideStep.status): trimmed text, or null. */
894
+ function stepStatus(step, ctx) {
895
+ if (!step.status)
896
+ return null;
897
+ const text = safe(() => step.status(ctx), undefined);
898
+ return typeof text === "string" && text.trim() ? text.trim() : null;
899
+ }
900
+ /**
901
+ * In a replay, whether a "select" step may go on with ถัดไป: the next step
902
+ * this person sees has its target on the page already (or there is none).
903
+ * A pick that makes the next step's target (a save shown only once the
904
+ * right value is chosen) is still needed.
905
+ */
906
+ function replayCanGoOn(mission, index, env, context, nothingToSave = false) {
907
+ if (stepKind(shownStep(mission.steps[index], nothingToSave)) !== "select")
908
+ return false;
909
+ for (let i = index + 1; i < mission.steps.length; i++) {
910
+ const next = mission.steps[i];
911
+ const ctx = guideContext(env, context, routeParamsFor(next, env));
912
+ if (next.requires && !safe(() => env.can(next.requires), true))
913
+ continue;
914
+ if (next.skipIf && safe(() => next.skipIf(ctx), false))
915
+ continue;
916
+ if (!next.target)
917
+ return true;
918
+ if (stepTargetVisible(next, env, ctx, false))
919
+ return true;
920
+ // A commit step with nothing to save is read on its saved state.
921
+ return nothingToSaveHolds(next, env, ctx);
922
+ }
923
+ return true;
924
+ }
925
+ /** Keep the status line and a replay's ถัดไป current (the page changed). */
926
+ function syncStatus(d, suite, env) {
927
+ const s = d.state.screen;
928
+ if (s.name !== "step")
929
+ return;
930
+ const mission = findMission(suite, s.mission);
931
+ if (!mission)
932
+ return;
933
+ const step = shownStep(mission.steps[s.index], s.nothingToSave);
934
+ const ctx = guideContext(env, d.state.progress.context, routeParamsFor(step, env));
935
+ const off = s.phase === "away" || s.bridge;
936
+ const status = off ? null : stepStatus(step, ctx);
937
+ const replayNext = !off && s.replay && replayCanGoOn(mission, s.index, env, d.state.progress.context, s.nothingToSave);
938
+ if (status !== s.status || replayNext !== s.replayNext)
939
+ setScreen(d, { ...s, status, replayNext });
940
+ }
880
941
  function markReadyIfVisible(d, suite, env) {
881
942
  const s = d.state.screen;
882
943
  if (s.name !== "step" || s.phase !== "waiting")
@@ -1262,6 +1323,7 @@ function reduceGuide(state, action, suite, env) {
1262
1323
  else if (!cur.bridge && needsBridge(step, env, ctx)) {
1263
1324
  setScreen(d, { ...cur, bridge: true, phase: "waiting" });
1264
1325
  }
1326
+ syncStatus(d, suite, env);
1265
1327
  markReadyIfVisible(d, suite, env);
1266
1328
  break;
1267
1329
  }
@@ -1324,6 +1386,12 @@ function reduceGuide(state, action, suite, env) {
1324
1386
  advance(d, suite, env);
1325
1387
  break;
1326
1388
  }
1389
+ if (kind === "select") {
1390
+ // A replay goes on without a pick when the next step is on the page.
1391
+ if (s.replay && replayCanGoOn(mission, s.index, env, d.state.progress.context, s.nothingToSave))
1392
+ advance(d, suite, env);
1393
+ break;
1394
+ }
1327
1395
  if (kind !== "input")
1328
1396
  break;
1329
1397
  const ctx = guideContext(env, d.state.progress.context, routeParamsFor(step, env));
@@ -2330,6 +2398,7 @@ const GUIDE_CSS = `
2330
2398
  .eg-b-t{margin:0;font-size:16px;font-weight:600;line-height:1.45}
2331
2399
  .eg-b-w{margin:2px 0 0;font-size:13.5px;color:var(--eg-muted)}
2332
2400
  .eg-bubble.eg-err .eg-b-t{color:var(--eg-danger)}
2401
+ .eg-b-s{margin:6px 0 0;font-size:13px;font-weight:600;color:var(--eg-danger)}
2333
2402
  .eg-k{display:inline-block;background:var(--eg-tint);color:var(--eg-strong);border-radius:7px;padding:0 7px;font-weight:600}
2334
2403
  .eg-b-f{display:flex;align-items:center;justify-content:space-between;margin-top:12px;gap:8px}
2335
2404
  .eg-b-f .eg-btn{height:34px;padding:0 14px;font-size:13.5px}
@@ -2563,7 +2632,7 @@ function StepLayer(p) {
2563
2632
  const showBubble = p.phase === "ready" && (!!spot || !p.hasTarget);
2564
2633
  const gap = p.showHand ? 34 : 14;
2565
2634
  const spotKey = spot ? `${spot.x}|${spot.y}|${spot.width}|${spot.height}` : "";
2566
- const contentKey = `${p.bubble.title}|${p.bubble.why}|${p.bubble.error?.title ?? ""}|${p.bubble.showNext}`;
2635
+ const contentKey = `${p.bubble.title}|${p.bubble.why}|${p.bubble.error?.title ?? ""}|${p.bubble.status ?? ""}|${p.bubble.showNext}`;
2567
2636
  // What the bubble sits beside: the target, with an open list over it (the
2568
2637
  // list is what the person is choosing from now). What it must not cover:
2569
2638
  // open lists, and the other anchors the step allows (a map step's
@@ -2686,7 +2755,7 @@ function BubbleBody(p) {
2686
2755
  const b = p.bubble;
2687
2756
  const title = b.error ? b.error.title : b.title;
2688
2757
  const why = b.error ? (b.error.detail ?? "") : b.why;
2689
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "eg-b-top", children: [jsxRuntime.jsxs("span", { className: "eg-mis", children: ["\u0E20\u0E32\u0E23\u0E01\u0E34\u0E08 ", b.missionNo, " \u00B7 ", b.missionTitle, b.replay ? " (ทบทวน)" : ""] }), jsxRuntime.jsxs("span", { className: "eg-cnt", children: ["\u0E02\u0E31\u0E49\u0E19\u0E17\u0E35\u0E48 ", b.index + 1, "/", b.total] })] }), jsxRuntime.jsx("div", { className: "eg-pips", "aria-hidden": "true", children: Array.from({ length: b.total }, (_, i) => (jsxRuntime.jsx("i", { className: i < b.index ? "eg-d" : i === b.index ? "eg-c" : undefined }, i))) }), jsxRuntime.jsx("p", { className: "eg-b-t", id: BUBBLE_TITLE_ID, children: jsxRuntime.jsx(GuideTitle, { text: title }) }), jsxRuntime.jsx("p", { className: "eg-b-w", id: BUBBLE_WHY_ID, hidden: !why, children: jsxRuntime.jsx(GuideTitle, { text: why }) }), jsxRuntime.jsxs("div", { className: "eg-b-f", children: [jsxRuntime.jsx("button", { type: "button", className: "eg-exit", onClick: p.onExit, children: b.replay ? "จบการทบทวน" : "ออกจากไกด์" }), jsxRuntime.jsxs("span", { style: { display: "flex", gap: 6 }, children: [b.showEndWithoutSave ? (jsxRuntime.jsx("button", { type: "button", className: "eg-btn eg-ghost", onClick: p.onEndWithoutSave, children: "\u0E08\u0E1A\u0E42\u0E14\u0E22\u0E44\u0E21\u0E48\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01" })) : null, b.showNext ? (jsxRuntime.jsx("button", { type: "button", className: "eg-btn eg-primary", onClick: p.onNext, children: "\u0E16\u0E31\u0E14\u0E44\u0E1B" })) : null] })] })] }));
2758
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "eg-b-top", children: [jsxRuntime.jsxs("span", { className: "eg-mis", children: ["\u0E20\u0E32\u0E23\u0E01\u0E34\u0E08 ", b.missionNo, " \u00B7 ", b.missionTitle, b.replay ? " (ทบทวน)" : ""] }), jsxRuntime.jsxs("span", { className: "eg-cnt", children: ["\u0E02\u0E31\u0E49\u0E19\u0E17\u0E35\u0E48 ", b.index + 1, "/", b.total] })] }), jsxRuntime.jsx("div", { className: "eg-pips", "aria-hidden": "true", children: Array.from({ length: b.total }, (_, i) => (jsxRuntime.jsx("i", { className: i < b.index ? "eg-d" : i === b.index ? "eg-c" : undefined }, i))) }), jsxRuntime.jsx("p", { className: "eg-b-t", id: BUBBLE_TITLE_ID, children: jsxRuntime.jsx(GuideTitle, { text: title }) }), jsxRuntime.jsx("p", { className: "eg-b-w", id: BUBBLE_WHY_ID, hidden: !why, children: jsxRuntime.jsx(GuideTitle, { text: why }) }), b.status && !b.error ? (jsxRuntime.jsx("p", { className: "eg-b-s", role: "status", "data-eg-status": "", children: b.status })) : null, jsxRuntime.jsxs("div", { className: "eg-b-f", children: [jsxRuntime.jsx("button", { type: "button", className: "eg-exit", onClick: p.onExit, children: b.replay ? "จบการทบทวน" : "ออกจากไกด์" }), jsxRuntime.jsxs("span", { style: { display: "flex", gap: 6 }, children: [b.showEndWithoutSave ? (jsxRuntime.jsx("button", { type: "button", className: "eg-btn eg-ghost", onClick: p.onEndWithoutSave, children: "\u0E08\u0E1A\u0E42\u0E14\u0E22\u0E44\u0E21\u0E48\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01" })) : null, b.showNext ? (jsxRuntime.jsx("button", { type: "button", className: "eg-btn eg-primary", onClick: p.onNext, children: "\u0E16\u0E31\u0E14\u0E44\u0E1B" })) : null] })] })] }));
2690
2759
  }
2691
2760
  /** The fingertip at ≈62% across and down the target; upside down near the bottom. */
2692
2761
  function handPosition(spot, viewportHeight) {
@@ -3838,7 +3907,8 @@ function GuideRuntimeInner({ config, organizationId, code, routing, progress: pr
3838
3907
  title: bridge ? "กดปุ่มเมนู" : resolveText(step.title, stepCtx),
3839
3908
  why: bridge ? `เมนูทั้งหมดของ ${suite.appName} อยู่ในนี้` : resolveText(step.why, stepCtx),
3840
3909
  error: screen.error,
3841
- showNext: !bridge && (kind === "input" || kind === "view"),
3910
+ status: bridge ? null : screen.status,
3911
+ showNext: !bridge && (kind === "input" || kind === "view" || (kind === "select" && screen.replay && screen.replayNext)),
3842
3912
  showEndWithoutSave: !bridge && screen.replay && kind === "commit",
3843
3913
  }, hasTarget: bridge || !!step.target, targetPressable: targetPressable(step, bridge), phase: screen.phase, confirmExit: screen.confirmExit, reopenHint: suite.reopenHint, nudge: state.nudge, moving: moving, onExit: () => dispatch({ type: "exit" }), onNext: () => dispatch({ type: "next" }), onEndWithoutSave: () => dispatch({ type: "endWithoutSave" }), onRetry: () => dispatch({ type: "retry" }), onGoResume: () => dispatch({ type: "goResume" }), onExitConfirm: () => dispatch({ type: "exitConfirm" }), onExitCancel: () => dispatch({ type: "exitCancel" }) }));
3844
3914
  }
@@ -3900,6 +3970,7 @@ exports.apiCallDetail = apiCallDetail;
3900
3970
  exports.emitGuideEvent = emitGuideEvent;
3901
3971
  exports.fillRoute = fillRoute;
3902
3972
  exports.guideAnchor = guideAnchor;
3973
+ exports.guideMenuShown = guideMenuShown;
3903
3974
  exports.loadGuides = loadGuides;
3904
3975
  exports.matchApiPath = matchApiPath;
3905
3976
  exports.matchRoute = matchRoute;
@@ -3908,4 +3979,4 @@ exports.reportApiCall = reportApiCall;
3908
3979
  exports.reportApiEvent = reportApiEvent;
3909
3980
  exports.storeGuides = storeGuides;
3910
3981
  exports.useGuideEntry = useGuideEntry;
3911
- //# sourceMappingURL=guide-runtime-DfnNdrjZ.cjs.map
3982
+ //# sourceMappingURL=guide-runtime-CLVSNkTN.cjs.map