• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ProjektAdLer / 2D_3D_AdLer / #1311

12 Aug 2025 12:33PM UTC coverage: 96.454% (-0.02%) from 96.469%
#1311

push

mountler
added display of other available learning spaces to exit modal

2606 of 3061 branches covered (85.14%)

19 of 22 new or added lines in 4 files covered. (86.36%)

1 existing line in 1 file now uncovered.

5766 of 5978 relevant lines covered (96.45%)

50.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.24
/src/Components/Core/Presentation/React/LearningSpaceDisplay/ExitModal/ExitModal.tsx
1
import useObservable from "../../ReactRelated/CustomHooks/useObservable";
2
import useBuilder from "../../ReactRelated/CustomHooks/useBuilder";
3
import ExitModalViewModel from "./ExitModalViewModel";
4
import StyledModal from "../../ReactRelated/ReactBaseComponents/StyledModal";
5
import StyledButton from "../../ReactRelated/ReactBaseComponents/StyledButton";
6
import BUILDER_TYPES from "~DependencyInjection/Builders/BUILDER_TYPES";
7
import IExitModalController from "./IExitModalController";
8
import { useCallback } from "react";
9
import { AdLerUIComponent } from "src/Components/Core/Types/ReactTypes";
10
import tailwindMerge from "../../../Utils/TailwindMerge";
11
import LearningSpaceTO from "src/Components/Core/Application/DataTransferObjects/LearningSpaceTO";
12
import spaceSolved from "../../../../../../Assets/icons/check-solution.svg";
13
import spaceAvailable from "../../../../../../Assets/icons/unlocked.svg";
14
import spaceLocked from "../../../../../../Assets/icons/locked.svg";
15
import { useTranslation } from "react-i18next";
16

17
export default function ExitModal({ className }: AdLerUIComponent<{}>) {
18
  const [viewModel, controller] = useBuilder<
24✔
19
    ExitModalViewModel,
20
    IExitModalController
21
  >(BUILDER_TYPES.IExitModalBuilder);
22
  const [isOpen, setOpen] = useObservable<boolean>(viewModel?.isOpen);
24✔
23

24
  const closeModal = useCallback(() => {
24✔
25
    setOpen(false);
1✔
26
  }, [setOpen]);
27

28
  const { t: translate } = useTranslation(["learningSpace", "helpMenu"]);
24✔
29

30
  if (!viewModel || !controller) return null;
24✔
31
  if (!isOpen) return null;
21✔
32

33
  return (
8✔
34
    <StyledModal
35
      title={translate(viewModel.modalTitle.Value).toString()}
36
      onClose={closeModal}
37
      showModal={isOpen}
38
      className={tailwindMerge(
39
        className,
40
        "flex flex-col justify-center gap-2 rounded-lg p-5",
41
      )}
42
      closeButtonToolTip={translate("closeToolTip").toString()}
43
    >
44
      <StyledButton
45
        disabled={false}
46
        shape="freeFloatCenter"
47
        className="mb-2 flex w-[100%]"
48
        onClick={controller.onExitButtonClicked}
49
      >
50
        {translate(viewModel.exitButtonTitle.Value).toString()}
51
      </StyledButton>
52

53
      {viewModel.isExit.Value && viewModel.availableSpaces.Value.length > 0 && (
11!
54
        <h1 className="text-xl">
55
          <b>
56
            {translate("availableSpaces", {
57
              count: viewModel.availableSpaces.Value.length,
58
            })}
59
          </b>
60
        </h1>
61
      )}
62

63
      {viewModel.isExit.Value &&
11!
64
        viewModel.availableSpaces.Value.length > 0 &&
65
        viewModel.availableSpaces.Value.map((availableSpace) => {
NEW
66
          return createSpaceButton(availableSpace, controller);
×
67
        })}
68

69
      {viewModel.isExit.Value &&
14!
70
        viewModel.successorSpaces.Value.length > 0 &&
71
        viewModel.availableSpaces.Value.length > 0 && (
72
          <h1 className="text-xl">
73
            <b>
74
              {translate("nextLearningSpace", {
75
                count: viewModel.successorSpaces.Value.length,
76
              })}
77
            </b>
78
          </h1>
79
        )}
80

81
      {viewModel.isExit.Value &&
14✔
82
        viewModel.successorSpaces.Value.length > 0 &&
83
        viewModel.successorSpaces.Value.map((successorSpace) => {
84
          return createSpaceButton(successorSpace, controller);
3✔
85
        })}
86
      {!viewModel.isExit.Value &&
16✔
87
        viewModel.precursorSpaces.Value.length > 0 &&
88
        viewModel.precursorSpaces.Value.map(
89
          (precursorSpace: LearningSpaceTO) => {
90
            return createSpaceButton(precursorSpace, controller);
3✔
91
          },
92
        )}
93
    </StyledModal>
94
  );
95
}
96

97
function createSpaceButton(
98
  learningSpaceTO: LearningSpaceTO,
99
  controller: IExitModalController,
100
) {
101
  let icon: string;
102
  if (learningSpaceTO.currentScore >= learningSpaceTO.requiredScore)
6✔
103
    icon = spaceSolved;
2✔
104
  else if (learningSpaceTO.isAvailable) icon = spaceAvailable;
4✔
105
  else icon = spaceLocked;
2✔
106

107
  return (
6✔
108
    <StyledButton
109
      icon={icon}
110
      key={learningSpaceTO.id}
111
      disabled={!learningSpaceTO.isAvailable}
112
      shape="freeFloatCenter"
113
      className="mb-2 flex w-[100%]"
114
      onClick={() =>
115
        controller.onPrecursorOrSuccessorSpaceClicked(learningSpaceTO.id)
×
116
      }
117
    >
118
      {learningSpaceTO.name + " betreten"}
119
    </StyledButton>
120
  );
121
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc