<div id="root"></div>
body {
  height: 100vh;
  width: 100vw;
  margin: 0;
  background: #cd5d7d;
}

.flex {
  display: flex;
}

.justify-center {
  justify-content: center;
}

.justify-between {
  justify-content: space-between;
}

.justify-around {
  justify-content: space-around;
}

.align-center {
  align-items: center;
}

.w-full {
  width: 100vw;
}

.w-max {
  width: 100%;
}

.w-threefourth {
  width: 75%;
}

.w-half {
  width: 50%;
}

.w-third {
  width: 33%;
}

.h-100 {
  height: 100px;
}

.h-400 {
  height: 400px;
}

.h-full {
  height: 100vh;
}

.h-max {
  height: 100%;
}

.flex-row {
  display: flex;
  flex-direction: row;
}

.flex-col {
  display: flex;
  flex-direction: column;
}

.conditional-row-to-col {
  display: flex;

  @media (max-width: 480px) {
    flex-direction: column;
  }
}

.conditional-text-center {
  @media (max-width: 480px) {
    text-align: center;
  }
}

.border {
  border: 0.05rem solid;
}

.my-10 {
  margin: 100px, 10px;
}

.input {
  height: 1rem;
  min-width: 50px;
}

.bg-white {
  background: white;
}

.bg-red {
  background: red;
}

.rounded-m {
  border-radius: 8px;
}

/* dropdown button */
.dropdown {
  position: relative;
  display: inline-block;
  margin: 0 0 0 30px;

  @media (max-width: 480px) {
    margin: 0;
  }
}

.dropbtn {
  background-color: #4caf50;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
  padding: 0 15px;
}

.dropdown-content {
  display: block;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
  list-style: none;
  margin: 0;
  padding: 0 0 0 10px;
  cursor: pointer;
}

.dropdown:hover .dropbtn {
  background-color: #3e8e41;
}

.dropdown-content li {
  transition: background-color 0.3s ease;
}

.dropdown-content li:hover {
  color: #3e8e41;
}

/* dropdown-button-end */
View Compiled
import React, {
  ChangeEvent,
  useState,
  useEffect,
  useRef
} from "https://cdn.skypack.dev/react";
import ReactDOM from "https://cdn.skypack.dev/react-dom";

interface DropdownProps {
  value: any;
  setValue: (value: any) => void;
}

const Dropdown: React.FC<DropdownProps> = ({ value, setValue, options }) => {
  const [isOpen, setIsOpen] = useState(false);

  const handleCurrencyOption = (option: string) => {
    setValue(option);
    setIsOpen(!isOpen);
  };

  return (
    <div aria-label="dropdown" className="dropdown">
      <div className="dropbtn" onClick={() => setIsOpen(!isOpen)}>
        <h2>{value.toUpperCase() || "Select a currency"}&#9660;</h2>
      </div>
      {isOpen && (
        <ul className="dropdown-content">
          {options.map((option, index) => (
            <li key={index} onClick={() => handleCurrencyOption(option)}>
              {option.toUpperCase()}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
};

const formatNum = (number: number) => {
  return Number(number.toFixed(2)).toLocaleString();
};

const App = () => {
  const options = ["usd", "eur", "gbp", "cny", "jpy"];

  const [amount, setAmount] = useState("");
  const [wucRate, setWUCRate] = useState(0);
  const [previousRate, setPreviousRate] = useState(0);
  const interimRate = useRef(0);
  const [currency, setCurrency] = useState(options[0]);
  const [debouncedAmount, setDebouncedAmount] = useState(amount);

  const fetchData = async (currency: string) => {
    try {
      const response = await fetch(
        `https://api.frontendeval.com/fake/crypto/${currency}`
      );
      const data = await response.json();
      setWUCRate(data.value);
      interimRate.current = data.value;
    } catch (error: any) {
      throw new Error(error.message);
    }
  };

  const onChangeAmount = (event: ChangeEvent<HTMLInputElement>) => {
    setAmount(event.target.value);
  };

  useEffect(() => {
    const debounceTimer = setTimeout(() => {
      setDebouncedAmount(amount);
    }, 500);

    return () => {
      clearTimeout(debounceTimer);
    };
  }, [amount]);

  useEffect(() => {
    const resetRates = () => {
      setWUCRate(0);
      setPreviousRate(0);
      interimRate.current = 0;
    };

    const fetchDataWithDebounce = async () => {
      setPreviousRate(interimRate.current);
      await fetchData(currency);
    };

    if (amount && currency) {
      resetRates();
      fetchDataWithDebounce();
      const intervalId = setInterval(async () => {
        fetchDataWithDebounce();
      }, 10000);

      return () => {
        clearInterval(intervalId);
      };
    }
  }, [debouncedAmount, currency]);

  const numAmount = Number(debouncedAmount);
  const convertedAmount = wucRate * numAmount;
  const previousAmount = previousRate * numAmount;
  const amountDiff = convertedAmount - previousAmount;

  const amountDiffCondition = (first: string, second: string, third: string) =>
    amountDiff > 0 ? first : amountDiff < 0 ? second : third;

  return (
    <div className="flex-col align-center justify-center h-full">
      <div className="bg-white h-400 flex-col w-half align-center justify-center rounded-m">
        <h1 className="flex conditional-text-center">WUC exchange</h1>
        <div className="flex-col align-center bg-white w-max">
          <div className=" conditional-row-to-col justify-center align-center h-100">
            <input
              type="number"
              aria-label="amount"
              name="amount"
              onChange={onChangeAmount}
              className="input"
              min="0"
            />
            <Dropdown
              value={currency}
              setValue={setCurrency}
              options={options}
            />
          </div>
          <div className="conditional-row-to-col align-center w-threefourth justify-around">
            <p aria-label="converted-amount">{formatNum(convertedAmount)}</p>
            <p>WUC</p>
            <p
              aria-label="amount-diff"
              style={{
                color: `${amountDiffCondition("green", "red", "black")}`
              }}
            >
              {amountDiffCondition("↑", "↓", "")}
              {formatNum(amountDiff)}
            </p>
          </div>
        </div>
      </div>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.