label-studio icon indicating copy to clipboard operation
label-studio copied to clipboard

feat: Add duration time in info tab

Open phantrung18072001 opened this issue 1 year ago • 5 comments

PR fulfills these requirements

  • [x] Commit message(s) and PR title follows the format [fix|feat|ci|chore|doc]: TICKET-ID: Short description of change made ex. fix: DEV-XXXX: Removed inconsistent code usage causing intermittent errors
  • [x] Docs have been added/updated (for bug fixes/features)
  • [x] Best efforts were made to ensure docs/code are concise and coherent (checked for spelling/grammatical errors, commented out code, debug logs etc.)
  • [x] Self-reviewed and ran all changes on a local instance (for bug fixes/features)

Change has impacts in these area(s)

(check all that apply)

  • [ ] Product design
  • [ ] Backend (Database)
  • [ ] Backend (API)
  • [x] Frontend

Describe the reason for change

The reason for this change is to add a new TimeBox component to display the duration time between startTime and endTime in the TimeDurationControl component. This update enhances the UI by showing the total duration in a read-only format. image

What is the current behavior?

Currently, the duration between startTime and endTime is not displayed in the TimeDurationControl component.

Does this PR introduce a breaking change?

(check only one)

  • [ ] Yes, and covered entirely by feature flag(s)
  • [ ] Yes, and covered partially by feature flag(s)
  • [x] No
  • [ ] Not sure (briefly explain the situation below)

phantrung18072001 avatar Aug 23 '24 09:08 phantrung18072001

Deploy request for label-studio-docs-new-theme pending review.

Visit the deploys page to approve it

Name Link
Latest commit 42fc949b7ac6f320b68a13ab20ec984b9dbb0186

netlify[bot] avatar Aug 23 '24 09:08 netlify[bot]

Deploy request for heartex-docs pending review.

Visit the deploys page to approve it

Name Link
Latest commit 42fc949b7ac6f320b68a13ab20ec984b9dbb0186

netlify[bot] avatar Aug 23 '24 09:08 netlify[bot]

I don't know how to add title (Start Time, End Time, Duration) to make it clearly for user. Please help me.

phantrung18072001 avatar Aug 26 '24 07:08 phantrung18072001

Thanks for this contribution @phantrung18072001, this makes good sense to add. With regards to differentiating the Start Time, End Time and Duration, that would require a label for each which the TimeBox component currently does not offer directly as a prop which would have to be added as an optional prop and styled in a way that is unobtrusive to the layout of the component.

bmartel avatar Aug 26 '24 13:08 bmartel

As an initial start, you could try updating the code for the Timebox component to reflect the following:

import React, { type FC, useCallback, useEffect, useState } from "react";
import { Block, Elem } from "../../utils/bem";
import { MaskUtil } from "../../utils/InputMask";
import { Label } from "../Label/Label";

import "./TimeBox.styl";

export interface TimerProps {
  sidepanel: boolean;
  value: number;
  readonly?: boolean;
  inverted?: boolean;
  onChange: (value: number) => void;
  label?: string;
}

export const TimeBox: FC<TimerProps> = ({
  sidepanel = false,
  value,
  inverted = false,
  readonly = false,
  onChange,
  label,
  ...props
}) => {
  const inputRef = React.createRef<HTMLInputElement>();
  const [currentInputTime, setCurrentInputTime] = useState<string | number | undefined>(value);

  useEffect(() => {
    if (inputRef.current)
      new MaskUtil(inputRef.current, "11:11:11:111", (data: string) => {
        setCurrentInputTime(data);
      });
  }, []);

  useEffect(() => {
    setCurrentInputTime(formatTime(value || 0, true));
  }, [value]);

  const formatTime = useCallback((time: number, input = false): any => {
    const timeDate = new Date(time * 1000).toISOString();
    let formatted = time > 3600 ? timeDate.substr(11, 8) : `00:${timeDate.substr(14, 5)}`;

    if (input) {
      const isHour = timeDate.substr(11, 2) !== "00";

      formatted = timeDate.substr(isHour ? 11 : 14, isHour ? 12 : 9).replace(".", ":");

      formatted = !isHour ? `00:${formatted}` : formatted;
    }

    return formatted;
  }, []);

  const convertTextToTime = (value: string) => {
    const splittedValue = value.split(":").reverse();
    let totalTime = 0;

    if (value.indexOf("_") >= 0) return;

    const calcs = [(x: number) => x / 1000, (x: number) => x, (x: number) => x * 60, (x: number) => x * 60 * 60];

    splittedValue.forEach((value, index) => {
      totalTime += calcs[index](Number.parseFloat(value));
    });

    onChange(totalTime);
  };

  const handleBlurInput = (e: React.FormEvent<HTMLInputElement>) => {
    const splittedValue = e.currentTarget.value.split(":");

    splittedValue[0] =
      splittedValue[0].toString().length === 1 ? `0${splittedValue[0].toString()}` : `${splittedValue[0]}`;

    convertTextToTime(splittedValue.join(":"));
    setCurrentInputTime(formatTime(value || 0, true));
  };

  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      e.currentTarget?.blur?.();
    }
  };

  const renderInputTime = () => {
    return (
      <Elem
        name={"input-time"}
        maxLength={12}
        tag={"input"}
        ref={inputRef}
        type="text"
        readOnly={readonly}
        value={currentInputTime}
        onKeyDown={handleKeyDown}
        onChange={() => {}}
        onBlur={handleBlurInput}
      />
    );
  };

  const timeBoxContent = (
    <Block name="time-box" mod={{ inverted, sidepanel }} {...props}>
      {renderInputTime()}
    </Block>
  );

  return label ? (
    <Label text={label}>
      {timeBoxContent}
    </Label>
  ) : (
    timeBoxContent
  );
};

Which should provide an optional label. The label might be too large but that can be fixed if needed through a simple style change.

Then you can use the label prop in the in the Timebox component:

      <TimeBox
        sidepanel={isSidepanel}
        readonly={startTimeReadonly}
        value={_currentTime}
        label="Start"
        onChange={handleChangeCurrentTime}
        data-testid="timebox-current-time"
      />
      <TimeBox
        sidepanel={isSidepanel}
        readonly={endTimeReadonly}
        value={endTime}
        label="End"
        onChange={handleChangeEndTime}
        data-testid="timebox-end-time"
        inverted
      />
      <TimeBox
        sidepanel={isSidepanel}
        readonly={true}
        value={endTime - startTime}
        label="Duration"
        onChange={()=> {}}
        data-testid="timebox-duration-time"
        inverted
      />

bmartel avatar Aug 26 '24 14:08 bmartel

Thanks @bmartel, I have a typescript error when importing Label into TimeBox. Is this acceptable ? And it doesn't work as I expeted. image

phantrung18072001 avatar Aug 28 '24 10:08 phantrung18072001

Thanks @bmartel, I have a typescript error when importing Label into TimeBox. Is this acceptable ? And it doesn't work as I expeted. image

The error is due to the Label component being written only in Javascript, so that error can be ignored. The one thing I just pushed up an amendment to is the Label only takes children as props for assigning text values. Try it out now, I'm certain styling will have to be updated but maybe it gets close.

bmartel avatar Aug 28 '24 11:08 bmartel

I fixed the styles for these components so it should now look fairly good 👍.

Screenshot 2024-08-28 at 9 16 33 AM

bmartel avatar Aug 28 '24 14:08 bmartel

Thank you, it's perfect for me. Please merge the PR and go live as soon as possible.

phantrung18072001 avatar Aug 29 '24 03:08 phantrung18072001

/jira create

Workflow run Jira issue TRIAG-836 is created

bmartel avatar Sep 09 '24 13:09 bmartel

@bmartel Can you tell me how to config for showing the duration? image

binhnq94 avatar Oct 01 '24 02:10 binhnq94