heartwood every commit a ring

Add toast notifications with internationalization support

291e1638 by Isaac Bythewood · 11 months ago

Add toast notifications with internationalization support

- Move toast calls from context reducer to UI components
- Add localized success/error messages for timer actions
- Integrate react-toastify with existing i18n system
- Display notifications for log operations, timer resets, and data export
modified components/HotKeysMapping.js
@@ -2,8 +2,10 @@ import React, { useContext } from "react";import { useRouter } from "next/router";import { GlobalHotKeys, configure } from "react-hotkeys";import PropTypes from "prop-types";import { toast } from "react-toastify";import { Context } from "../components/context";import contextStrings from "../l10n/context";configure({  ignoreTags: [],
@@ -38,7 +40,11 @@ const HotKeysMapping = (props) => {    RESET: () => dispatch({ type: "NEW_TIMER" }),    ADD_LOG: (event) => {      event.preventDefault();      dispatch({ type: "ADD_LOG", note: state.note });      if (state.note.trim()) {        dispatch({ type: "ADD_LOG", note: state.note });        contextStrings.setLanguage(state.language);        toast.success(contextStrings.addedEntry);      }    },    TIMER_PAGE: (event) => {      event.preventDefault();
@@ -47,7 +53,11 @@ const HotKeysMapping = (props) => {    LOG_PAGE: () => router.push("/log"),    ABOUT_PAGE: () => router.push("/about"),    SUMMARY_PAGE: () => router.push("/summary"),    CLEAR_LOG: () => dispatch({ type: "CLEAR_LOG" }),    CLEAR_LOG: () => {      dispatch({ type: "CLEAR_LOG" });      contextStrings.setLanguage(state.language);      toast.error(contextStrings.resetLog);    },    LOG_NEXT: (event) => {      event.preventDefault();      if (
@@ -76,8 +86,13 @@ const HotKeysMapping = (props) => {    LOG_DELETE_SINGLE: (event) => {      event.preventDefault();      if (!state.logSelectedEntry) return;      if (window.location.href.substr(window.location.href.length - 3) == "log")      if (        window.location.href.substr(window.location.href.length - 3) == "log"      ) {        dispatch({ type: "REMOVE_LOG" });        contextStrings.setLanguage(state.language);        toast.error(contextStrings.deletedEntry);      }    },  };
modified components/context.js
@@ -1,7 +1,6 @@import React, { useEffect } from "react";import { useReducer, createContext } from "react";import PropTypes from "prop-types";import { toast } from "react-toastify";import localForage from "localforage";import { v4 as uuid } from "uuid";
@@ -26,7 +25,6 @@ const reducer = (state, action) => {  switch (action.type) {    case "LOCALDATA_READY":      strings.setLanguage(action.localdata.language);      toast.info(strings.loaded);      return { ...action.localdata };    case "SET_LANGUAGE":      newState = {
@@ -71,7 +69,6 @@ const reducer = (state, action) => {        note: "",      };      localForage.setItem("context", newState);      toast.success(strings.addedEntry);      return newState;    case "EDIT_LOG":      newState = {
@@ -102,7 +99,6 @@ const reducer = (state, action) => {        };      }      localForage.setItem("context", newState);      toast.error(strings.deletedEntry);      return newState;    case "CLEAR_LOG":      newState = {
@@ -112,7 +108,6 @@ const reducer = (state, action) => {        edit: false,      };      localForage.setItem("context", newState);      toast.error(strings.resetLog);      return newState;    case "CLEAR_TAG":      newState = {
@@ -120,10 +115,9 @@ const reducer = (state, action) => {        log: [...state.log.filter((entry) => !entry.tags.includes(action.tag))],      };      localForage.setItem("context", newState);      toast.error(strings.deletedEntry);      return newState;    case "NEXT_LOG_ITEM":      if (state.log.length === 0) return;      if (state.log.length === 0) return state;      if (!state.logSelectedEntry) {        newState = { ...state, logSelectedEntry: state.log[0].id };      } else {
@@ -137,7 +131,7 @@ const reducer = (state, action) => {      localForage.setItem("context", newState);      return newState;    case "PREVIOUS_LOG_ITEM":      if (state.log.length === 0) return;      if (state.log.length === 0) return state;      if (!state.logSelectedEntry) {        newState = { ...state, logSelectedEntry: state.log[0].id };      } else {
@@ -157,7 +151,6 @@ const reducer = (state, action) => {    case "TOGGLE_EDITION":      newState = { ...state, edit: action.edit };      localForage.setItem("context", newState);      action.submited ? toast.success(strings.editedEntry) : "";      return newState;    default:      return state;
modified components/sidebar.js
@@ -16,18 +16,18 @@ const Sidebar = ({ router }) => {      <Title>{strings.name}</Title>      <Pages>        <Link href="/" passHref>          <Page active={router.pathname === "/"} aria-label={strings.timer}>          <Page $active={router.pathname === "/"} aria-label={strings.timer}>            <TimerIcon />          </Page>        </Link>        <Link href="/log" passHref>          <Page active={router.pathname === "/log"} aria-label={strings.log}>          <Page $active={router.pathname === "/log"} aria-label={strings.log}>            <LogIcon />          </Page>        </Link>        <Link href="/summary" passHref>          <Page            active={router.pathname === "/summary"}            $active={router.pathname === "/summary"}            aria-label={strings.summary}          >            <SummaryIcon />
@@ -108,11 +108,14 @@ const Page = styled.a`  position: relative;  display: flex;  margin-bottom: 1rem;  transition: color 300ms, font-size 300ms, transform 300ms;  transition:    color 300ms,    font-size 300ms,    transform 300ms;  font-size: 2em;  font-weight: 100;  ${(props) =>    props.active ? "color: rgba(0, 0, 0, 1);" : "color: rgba(0, 0, 0, 0.5);"}    props.$active ? "color: rgba(0, 0, 0, 1);" : "color: rgba(0, 0, 0, 0.5);"}  &:hover {    transform: scale(1.5);
modified components/timer.js
@@ -1,8 +1,10 @@import React, { useState, useEffect, useContext, useRef } from "react";import styled from "styled-components";import { toast } from "react-toastify";import { Context } from "./context";import strings from "../l10n/timer";import contextStrings from "../l10n/context";import { timeString, timeDiff } from "../utils/time";const Timer = () => {
@@ -31,8 +33,12 @@ const Timer = () => {  const submitForm = (e) => {    e.preventDefault();    dispatch({ type: "ADD_LOG", note: state.note });    dispatch({ type: "NOTE_UPDATED", note: "" });    if (state.note.trim()) {      dispatch({ type: "ADD_LOG", note: state.note });      dispatch({ type: "NOTE_UPDATED", note: "" });      contextStrings.setLanguage(state.language);      toast.success(contextStrings.addedEntry);    }  };  return (
@@ -96,7 +102,9 @@ const Note = styled.input`  font-size: 1.6em;  border: none;  transform: scale(1);  transition: transform 250ms, background 250ms;  transition:    transform 250ms,    background 250ms;  &:hover,  &:focus {
modified next.config.js
@@ -1,21 +1,17 @@const withPlugins = require("next-compose-plugins");const withPWA = require("next-pwa");const runtimeCaching = require("next-pwa/cache");const withPWA = require("next-pwa")({  dest: "public",  disable: process.env.NODE_ENV === "development",  runtimeCaching: require("next-pwa/cache"),});const withBundleAnalyzer = require("@next/bundle-analyzer")({  enabled: process.env.NODE_ENV !== "development",});module.exports = withPlugins([  [    withPWA,    {      pwa: {        dest: "public",        disable: process.env.NODE_ENV === "development",        runtimeCaching,      },    },  ],  [withBundleAnalyzer],]);/** @type {import('next').NextConfig} */const nextConfig = {  reactStrictMode: true,  swcMinify: true,};module.exports = withBundleAnalyzer(withPWA(nextConfig));
modified package.json
@@ -22,15 +22,16 @@    "react-localization": "^1.0.16",    "react-toastify": "^9.0.4",    "react-transition-group": "^4.2.1",    "styled-components": "^5.3.5",    "uuid": "^8.3.2"    "styled-components": "^6.1.18",    "uuid": "^9.0.1"  },  "devDependencies": {    "eslint": "^8.17.0",    "eslint-config-prettier": "^8.1.0",    "eslint-plugin-prettier": "^4.0.0",    "eslint-plugin-react": "^7.23.1",    "prettier": "^2.2.1",    "babel-plugin-styled-components": "^2.1.4",    "eslint": "^8.57.1",    "eslint-config-prettier": "^9.1.0",    "eslint-plugin-prettier": "^5.4.1",    "eslint-plugin-react": "^7.37.5",    "prettier": "^3.5.3",    "webpack": "^5.76.0"  }}
modified pages/about.js
@@ -26,7 +26,7 @@ const About = () => {            className="bi bi-github"            viewBox="0 0 16 16"          >            <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>            <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z" />          </svg>        </GitHubLogo>      </GitHubLink>
modified pages/index.js
@@ -37,7 +37,8 @@ const Background = styled.div`  right: 0;  bottom: 0;  left: 0;  background-image: linear-gradient(#0d0221, transparent),  background-image:    linear-gradient(#0d0221, transparent),    linear-gradient(to top left, #580215, transparent),    linear-gradient(to top right, #210d00, transparent);  background-blend-mode: screen;
modified pages/log.js
@@ -2,11 +2,13 @@ import React, { useContext, useState } from "react";import { TransitionGroup, CSSTransition } from "react-transition-group";import styled from "styled-components";import { CSVLink } from "react-csv";import { toast } from "react-toastify";import Page from "../components/page";import { Context } from "../components/context";import Entry from "../components/entry";import strings from "../l10n/log";import contextStrings from "../l10n/context";import { timeString } from "../utils/time";const Log = () => {
@@ -49,6 +51,8 @@ const Log = () => {      setFilter({ type: "SHOW_ALL" });    }    dispatch({ type: "REMOVE_LOG", id: id });    contextStrings.setLanguage(state.language);    toast.error(contextStrings.deletedEntry);  };  return (
@@ -102,12 +106,20 @@ const Log = () => {                  onClick={() => {                    dispatch({ type: "CLEAR_TAG", tag: filter.tag });                    setFilter({ type: "SHOW_ALL" });                    contextStrings.setLanguage(state.language);                    toast.error(contextStrings.deletedEntry);                  }}                >                  {strings.clear} {filter.tag}                </Reset>              ) : (                <Reset onClick={() => dispatch({ type: "CLEAR_LOG" })}>                <Reset                  onClick={() => {                    dispatch({ type: "CLEAR_LOG" });                    contextStrings.setLanguage(state.language);                    toast.error(contextStrings.resetLog);                  }}                >                  {strings.clear}                </Reset>              )}
modified pages/summary.js
@@ -7,7 +7,7 @@ import { Context } from "../components/context";import strings from "../l10n/summary";const Summary = () => {  const { state, dispatch } = useContext(Context);  const { state } = useContext(Context);  strings.setLanguage(state.language);  const canvasRef = useRef(null);
modified yarn.lock
@@ -124,7 +124,7 @@    "@babel/traverse" "^7.27.1"    "@babel/types" "^7.27.1""@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.22.5", "@babel/helper-module-imports@^7.27.1":"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.22.5", "@babel/helper-module-imports@^7.27.1":  version "7.27.1"  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204"  integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==
@@ -776,7 +776,7 @@    "@babel/parser" "^7.27.2"    "@babel/types" "^7.27.1""@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.27.4", "@babel/traverse@^7.4.5":"@babel/traverse@^7.27.1", "@babel/traverse@^7.27.3", "@babel/traverse@^7.27.4":  version "7.27.4"  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.4.tgz#b0045ac7023c8472c3d35effd7cc9ebd638da6ea"  integrity sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==
@@ -797,27 +797,22 @@    "@babel/helper-string-parser" "^7.27.1"    "@babel/helper-validator-identifier" "^7.27.1""@emotion/is-prop-valid@^1.1.0":  version "1.3.1"  resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz#8d5cf1132f836d7adbe42cf0b49df7816fc88240"  integrity sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw=="@emotion/is-prop-valid@1.2.2":  version "1.2.2"  resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337"  integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==  dependencies:    "@emotion/memoize" "^0.9.0""@emotion/memoize@^0.9.0":  version "0.9.0"  resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102"  integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==    "@emotion/memoize" "^0.8.1""@emotion/stylis@^0.8.4":  version "0.8.5"  resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"  integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="@emotion/memoize@^0.8.1":  version "0.8.1"  resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"  integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="@emotion/unitless@^0.7.4":  version "0.7.5"  resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"  integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="@emotion/unitless@0.8.1":  version "0.8.1"  resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3"  integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="@eslint-community/eslint-utils@^4.2.0":  version "4.7.0"
@@ -1008,6 +1003,11 @@    "@nodelib/fs.scandir" "2.1.5"    fastq "^1.6.0""@pkgr/core@^0.2.4":  version "0.2.4"  resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c"  integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw=="@polka/url@^1.0.0-next.20":  version "1.0.0-next.29"  resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.29.tgz#5a40109a1ab5f84d6fd8fc928b19f367cbe7e7b1"
@@ -1125,6 +1125,11 @@  dependencies:    "@types/node" "*""@types/stylis@4.2.5":  version "4.2.5"  resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.5.tgz#1daa6456f40959d06157698a653a9ab0a70281df"  integrity sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw=="@types/trusted-types@^2.0.2":  version "2.0.7"  resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
@@ -1488,7 +1493,7 @@ babel-plugin-polyfill-regenerator@^0.6.1:  dependencies:    "@babel/helper-define-polyfill-provider" "^0.6.4""babel-plugin-styled-components@>= 1.12.0":babel-plugin-styled-components@^2.1.4:  version "2.1.4"  resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz#9a1f37c7f32ef927b4b008b529feb4a2c82b1092"  integrity sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==
@@ -1690,7 +1695,7 @@ css-color-keywords@^1.0.0:  resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"  integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==css-to-react-native@^3.0.0:css-to-react-native@3.2.0:  version "3.2.0"  resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32"  integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==
@@ -1699,7 +1704,7 @@ css-to-react-native@^3.0.0:    css-color-keywords "^1.0.0"    postcss-value-parser "^4.0.2"csstype@^3.0.2:csstype@3.1.3, csstype@^3.0.2:  version "3.1.3"  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"  integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
@@ -1987,19 +1992,20 @@ escape-string-regexp@^4.0.0:  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==eslint-config-prettier@^8.1.0:  version "8.10.0"  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11"  integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==eslint-config-prettier@^9.1.0:  version "9.1.0"  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f"  integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==eslint-plugin-prettier@^4.0.0:  version "4.2.1"  resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"  integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==eslint-plugin-prettier@^5.4.1:  version "5.4.1"  resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz#99b55d7dd70047886b2222fdd853665f180b36af"  integrity sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==  dependencies:    prettier-linter-helpers "^1.0.0"    synckit "^0.11.7"eslint-plugin-react@^7.23.1:eslint-plugin-react@^7.37.5:  version "7.37.5"  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065"  integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==
@@ -2044,7 +2050,7 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"  integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==eslint@^8.17.0:eslint@^8.57.1:  version "8.57.1"  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"  integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
@@ -2432,11 +2438,6 @@ has-bigints@^1.0.2:  resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe"  integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==has-flag@^3.0.0:  version "3.0.0"  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==has-flag@^4.0.0:  version "4.0.0"  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
@@ -2475,13 +2476,6 @@ hasown@^2.0.2:  dependencies:    function-bind "^1.1.2"hoist-non-react-statics@^3.0.0:  version "3.3.2"  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"  integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==  dependencies:    react-is "^16.7.0"idb@^7.0.1:  version "7.1.1"  resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
@@ -3063,7 +3057,7 @@ ms@^2.1.3:  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==nanoid@^3.3.4:nanoid@^3.3.4, nanoid@^3.3.7:  version "3.3.11"  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"  integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
@@ -3349,6 +3343,15 @@ postcss@8.4.14:    picocolors "^1.0.0"    source-map-js "^1.0.2"postcss@8.4.49:  version "8.4.49"  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19"  integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==  dependencies:    nanoid "^3.3.7"    picocolors "^1.1.1"    source-map-js "^1.2.1"prelude-ls@^1.2.1:  version "1.2.1"  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -3361,10 +3364,10 @@ prettier-linter-helpers@^1.0.0:  dependencies:    fast-diff "^1.1.2"prettier@^2.2.1:  version "2.8.8"  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"  integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==prettier@^3.5.3:  version "3.5.3"  resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5"  integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:  version "5.6.0"
@@ -3422,7 +3425,7 @@ react-hotkeys@^2.0.0:  dependencies:    prop-types "^15.6.1"react-is@^16.13.1, react-is@^16.7.0:react-is@^16.13.1:  version "16.13.1"  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -3705,7 +3708,7 @@ set-proto@^1.0.0:    es-errors "^1.3.0"    es-object-atoms "^1.0.0"shallowequal@^1.1.0:shallowequal@1.1.0:  version "1.1.0"  resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"  integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
@@ -3781,7 +3784,7 @@ source-list-map@^2.0.0:  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"  integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==source-map-js@^1.0.2:source-map-js@^1.0.2, source-map-js@^1.2.1:  version "1.2.1"  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
@@ -3904,33 +3907,30 @@ strip-json-comments@^3.1.1:  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==styled-components@^5.3.5:  version "5.3.11"  resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.11.tgz#9fda7bf1108e39bf3f3e612fcc18170dedcd57a8"  integrity sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==  dependencies:    "@babel/helper-module-imports" "^7.0.0"    "@babel/traverse" "^7.4.5"    "@emotion/is-prop-valid" "^1.1.0"    "@emotion/stylis" "^0.8.4"    "@emotion/unitless" "^0.7.4"    babel-plugin-styled-components ">= 1.12.0"    css-to-react-native "^3.0.0"    hoist-non-react-statics "^3.0.0"    shallowequal "^1.1.0"    supports-color "^5.5.0"styled-components@^6.1.18:  version "6.1.18"  resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.1.18.tgz#9647497a92326ba9d758051c914f15004d524bb9"  integrity sha512-Mvf3gJFzZCkhjY2Y/Fx9z1m3dxbza0uI9H1CbNZm/jSHCojzJhQ0R7bByrlFJINnMzz/gPulpoFFGymNwrsMcw==  dependencies:    "@emotion/is-prop-valid" "1.2.2"    "@emotion/unitless" "0.8.1"    "@types/stylis" "4.2.5"    css-to-react-native "3.2.0"    csstype "3.1.3"    postcss "8.4.49"    shallowequal "1.1.0"    stylis "4.3.2"    tslib "2.6.2"styled-jsx@5.0.7:  version "5.0.7"  resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.7.tgz#be44afc53771b983769ac654d355ca8d019dff48"  integrity sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==supports-color@^5.5.0:  version "5.5.0"  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==  dependencies:    has-flag "^3.0.0"stylis@4.3.2:  version "4.3.2"  resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444"  integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==supports-color@^7.0.0, supports-color@^7.1.0:  version "7.2.0"
@@ -3951,6 +3951,13 @@ supports-preserve-symlinks-flag@^1.0.0:  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==synckit@^0.11.7:  version "0.11.8"  resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457"  integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==  dependencies:    "@pkgr/core" "^0.2.4"tapable@^2.1.1, tapable@^2.2.0:  version "2.2.2"  resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872"
@@ -4016,6 +4023,11 @@ tr46@^1.0.1:  dependencies:    punycode "^2.1.0"tslib@2.6.2:  version "2.6.2"  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==tslib@^2.4.0:  version "2.8.1"  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
@@ -4158,10 +4170,10 @@ use-sync-external-store@1.2.0:  resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"  integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==uuid@^8.3.2:  version "8.3.2"  resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"  integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==uuid@^9.0.1:  version "9.0.1"  resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"  integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==watchpack@^2.4.1:  version "2.4.4"