Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { inject, observer } from "mobx-react";
import { CheckCircleOutlined, CheckOutlined } from "@ant-design/icons";
 
import Hint from "../Hint/Hint";
import { DraftPanel } from "../Annotations/Annotations";
import styles from "./Controls.module.scss";
import { Button, Tooltip } from "@humansignal/ui";
import { IconInfoOutline } from "@humansignal/icons";
import { cn } from "../../utils/bem";
 
export default inject("store")(
  observer(({ item, store }) => {
    /**
     * Buttons of Controls
     */
    const buttons = {
      skip: "",
      update: "",
      submit: "",
    };
 
    const { userGenerate, sentUserGenerate, versions } = item;
    const { enableHotkeys, enableTooltips } = store.settings;
 
    /**
     * Task information
     */
    let taskInformation;
    const taskInfoClassName = cn("task-info").toClassName();
    const skipButtonClassName = cn("skip-btn").toClassName();
    const submitButtonClassName = cn("submit-btn").toClassName();
    const updateButtonClassName = cn("update-btn").toClassName();
 
    if (store.task) {
      taskInformation = <h4 className={`${styles.task} ${taskInfoClassName}`}>Task ID: {store.task.id}</h4>;
    }
 
    /**
     * Hotkeys
     */
    if (enableHotkeys && enableTooltips) {
      buttons.submit = <Hint> [ Ctrl+Enter ]</Hint>;
      buttons.skip = <Hint> [ Ctrl+Space ]</Hint>;
      buttons.update = <Hint> [ Alt+Enter] </Hint>;
    }
 
    let skipButton;
    let updateButton;
    let submitButton;
    let draftMenu;
 
    /**
     * Check for Predict Menu
     */
    // Manager roles that can force-skip unskippable tasks (OW=Owner, AD=Admin, MA=Manager)
    const MANAGER_ROLES = ["OW", "AD", "MA"];
 
    if (!store.annotationStore.predictSelect || store.explore) {
      const disabled = store.isSubmitting;
      const task = store.task;
      const taskAllowSkip = task?.allow_skip !== false;
      const userRole = window.APP_SETTINGS?.user?.role;
      const hasForceSkipPermission = MANAGER_ROLES.includes(userRole);
      const canSkip = taskAllowSkip || hasForceSkipPermission;
      const skipDisabled = disabled || !canSkip;
 
      const skipTooltip = canSkip ? "Cancel (skip) task: [ Ctrl+Space ]" : "This task cannot be skipped";
 
      const showInfoIcon = !taskAllowSkip && hasForceSkipPermission;
 
      if (store.hasInterface("skip")) {
        skipButton = (
          <>
            {showInfoIcon && (
              <Tooltip title="Annotators and Reviewers will not be able to skip this task">
                <IconInfoOutline width={20} height={20} className="text-neutral-content ml-auto cursor-pointer" />
              </Tooltip>
            )}
            <Button
              disabled={skipDisabled}
              look="danger"
              onClick={canSkip ? store.skipTask : undefined}
              tooltip={skipTooltip}
              className={`${styles.skip} ${skipButtonClassName}`}
            >
              Skip {buttons.skip}
            </Button>
          </>
        );
      }
 
      if ((userGenerate && !sentUserGenerate) || (store.explore && !userGenerate && store.hasInterface("submit"))) {
        submitButton = (
          <Button
            disabled={disabled}
            look="primary"
            icon={<CheckOutlined />}
            onClick={store.submitAnnotation}
            tooltip="Save results: [ Ctrl+Enter ]"
            className={`${styles.submit} ${submitButtonClassName}`}
          >
            Submit {buttons.submit}
          </Button>
        );
      }
 
      if ((userGenerate && sentUserGenerate) || (!userGenerate && store.hasInterface("update"))) {
        updateButton = (
          <Button
            disabled={disabled}
            look="primary"
            icon={<CheckCircleOutlined />}
            onClick={store.updateAnnotation}
            tooltip="Update this task: [ Alt+Enter ]"
            className={updateButtonClassName}
          >
            {sentUserGenerate || versions.result ? "Update" : "Submit"} {buttons.update}
          </Button>
        );
      }
 
      if (!store.hasInterface("annotations:menu")) {
        draftMenu = <DraftPanel item={item} />;
      }
    }
 
    const content = (
      <div className={styles.block}>
        <div className={styles.wrapper}>
          <div className={styles.container}>
            {skipButton}
            {updateButton}
            {submitButton}
            {draftMenu}
          </div>
          {taskInformation}
        </div>
      </div>
    );
 
    return (item.type === "annotation" || store.explore) && content;
  }),
);