Repository: isucon/isucon11-qualify (language: go)

Issue:
[frontend]読み込み中の表示
今は`Now Loading`って表示してるだけだからいい感じにしたい


Discussion / hints:
pagingでも

Fix patch:
diff --git a/bench/scenario/assets.go b/bench/scenario/assets.go
index 71fc75ea4..1ce6b1b6d 100644
--- a/bench/scenario/assets.go
+++ b/bench/scenario/assets.go
@@ -4,20 +4,20 @@ package scenario
 var (
 	resourcesHash = map[string]string{ 
 		"/assets/favicon.d0f5f504.svg": "3697a1b6c6d164ec4a5e4db6f7789f05",
-		"/assets/index.4689f22a.css": "ce71cd45e2ba7d0809b71196f12dd3a4",
-		"/assets/index.f7314141.js": "1bf0dcb2fed93f75364eabb86f49deb9",
+		"/assets/index.b1cd63b1.js": "a647f2bc82f4ecbe7411ff058d89920b",
+		"/assets/index.ee7b4942.css": "8713a887e3d6d5d7685d3ef977a20e02",
 		"/assets/logo_orange.974bf3e6.svg": "10e2dfa0ff27ec4325b781973c646737",
 		"/assets/logo_white.98008342.svg": "f81e35a6821af44ea5b3a77346bcf6ad",
-		"/assets/vendor.e20fa6ba.js": "0fba45a67211ef351dd61811cf25cd66",
-		"/index.html": "18b9abb95c64ad7bdd9ccc7c6e17e618",
+		"/assets/vendor.519e803f.js": "0fba45a67211ef351dd61811cf25cd66",
+		"/index.html": "398b8c905fbd0282d0669b609015c780",
 	}
 	resourcesMap = map[string]string{ 
 		"/assets/favicon.svg": "/assets/favicon.d0f5f504.svg",
-		"/assets/index.css": "/assets/index.4689f22a.css",
-		"/assets/index.js": "/assets/index.f7314141.js",
+		"/assets/index.css": "/assets/index.ee7b4942.css",
+		"/assets/index.js": "/assets/index.b1cd63b1.js",
 		"/assets/logo_orange.svg": "/assets/logo_orange.974bf3e6.svg",
 		"/assets/logo_white.svg": "/assets/logo_white.98008342.svg",
-		"/assets/vendor.js": "/assets/vendor.e20fa6ba.js",
+		"/assets/vendor.js": "/assets/vendor.519e803f.js",
 		"/index.html": "/index.html",
 	}
 )
diff --git a/webapp/frontend/.eslintrc.js b/webapp/frontend/.eslintrc.js
index bab85c0c9..be5937637 100644
--- a/webapp/frontend/.eslintrc.js
+++ b/webapp/frontend/.eslintrc.js
@@ -29,6 +29,7 @@ module.exports = {
     'react/prop-types': 'off',
     'react/react-in-jsx-scope': 'off',
     'no-console': 'warn',
-    '@typescript-eslint/explicit-module-boundary-types': 'off'
+    '@typescript-eslint/explicit-module-boundary-types': 'off',
+    'no-restricted-imports': ['error', { patterns: ['../*'] }]
   }
 }
diff --git a/webapp/frontend/src/components/Condition/ConditionDetail.tsx b/webapp/frontend/src/components/Condition/ConditionDetail.tsx
index 13e41dbf3..33662ee9a 100644
--- a/webapp/frontend/src/components/Condition/ConditionDetail.tsx
+++ b/webapp/frontend/src/components/Condition/ConditionDetail.tsx
@@ -1,6 +1,6 @@
-import { Condition } from '../../lib/apis'
-import { getConditionTime } from '../../lib/date'
-import Tip from '../UI/Tip'
+import { Condition } from '/@/lib/apis'
+import { getConditionTime } from '/@/lib/date'
+import Tip from '/@/components/UI/Tip'
 import ConditionIcons from './ConditionIcons'
 
 interface Props {
diff --git a/webapp/frontend/src/components/Condition/ConditionNavigator.tsx b/webapp/frontend/src/components/Condition/ConditionNavigator.tsx
index 8daaea23f..f2750a643 100644
--- a/webapp/frontend/src/components/Condition/ConditionNavigator.tsx
+++ b/webapp/frontend/src/components/Condition/ConditionNavigator.tsx
@@ -1,6 +1,6 @@
 import { IoIosArrowBack, IoIosArrowForward } from 'react-icons/io'
-import { Condition, DEFAULT_CONDITION_LIMIT } from '../../lib/apis'
-import IconButton from '../UI/IconButton'
+import { Condition, DEFAULT_CONDITION_LIMIT } from '/@/lib/apis'
+import IconButton from '/@/components/UI/IconButton'
 
 interface Props {
   conditions: Condition[]
diff --git a/webapp/frontend/src/components/Condition/Conditions.tsx b/webapp/frontend/src/components/Condition/Conditions.tsx
index 3874db0f8..7cff3f266 100644
--- a/webapp/frontend/src/components/Condition/Conditions.tsx
+++ b/webapp/frontend/src/components/Condition/Conditions.tsx
@@ -1,4 +1,4 @@
-import { Condition } from '../../lib/apis'
+import { Condition } from '/@/lib/apis'
 import ConditionDetail from './ConditionDetail'
 
 interface Props {
diff --git a/webapp/frontend/src/components/Condition/IsuConditionCardContent.tsx b/webapp/frontend/src/components/Condition/IsuConditionCardContent.tsx
index 2a0322752..2d39f48e3 100644
--- a/webapp/frontend/src/components/Condition/IsuConditionCardContent.tsx
+++ b/webapp/frontend/src/components/Condition/IsuConditionCardContent.tsx
@@ -1,17 +1,23 @@
 import { useCallback } from 'react'
-import apis, { ConditionRequest, Isu } from '../../lib/apis'
+import apis, { ConditionRequest, Isu } from '/@/lib/apis'
 import ConditionNavigator from './ConditionNavigator'
 import Conditions from './Conditions'
 import SearchInputs from './SearchInputs'
 import usePaging from './use/paging'
+import { useState } from 'react'
+import NowLoadingOverlay from '/@/components/UI/NowLoadingOverlay'
 
 interface Props {
   isu: Isu
 }
 const IsuConditionCardContent = ({ isu }: Props) => {
+  const [isLoading, setIsLoading] = useState(false)
   const getConditions = useCallback(
-    (params: ConditionRequest) => {
-      return apis.getIsuConditions(isu.jia_isu_uuid, params)
+    async (params: ConditionRequest) => {
+      setIsLoading(true)
+      const res = await apis.getIsuConditions(isu.jia_isu_uuid, params)
+      setIsLoading(false)
+      return res
     },
     [isu]
   )
@@ -22,7 +28,10 @@ const IsuConditionCardContent = ({ isu }: Props) => {
     <div className="flex flex-col gap-2">
       <SearchInputs query={query} times={times} search={search} />
       <div className="flex flex-col gap-4 items-center">
-        <Conditions conditions={conditions} />
+        <div className="relative w-full">
+          <Conditions conditions={conditions} />
+          {isLoading ? <NowLoadingOverlay /> : null}
+        </div>
         <ConditionNavigator
           conditions={conditions}
           page={page}
diff --git a/webapp/frontend/src/components/Condition/SearchInputs.tsx b/webapp/frontend/src/components/Condition/SearchInputs.tsx
index 55b162b40..57b6394f4 100644
--- a/webapp/frontend/src/components/Condition/SearchInputs.tsx
+++ b/webapp/frontend/src/components/Condition/SearchInputs.tsx
@@ -1,6 +1,6 @@
 import { useState } from 'react'
-import Button from '../UI/Button'
-import Input from '../UI/Input'
+import Button from '/@/components/UI/Button'
+import Input from '/@/components/UI/Input'
 import TimeInputs from './TimeInputs'
 
 interface Props {
diff --git a/webapp/frontend/src/components/Condition/use/paging.ts b/webapp/frontend/src/components/Condition/use/paging.ts
index d3bac2e8b..317c0a8e5 100644
--- a/webapp/frontend/src/components/Condition/use/paging.ts
+++ b/webapp/frontend/src/components/Condition/use/paging.ts
@@ -3,8 +3,8 @@ import {
   Condition,
   ConditionRequest,
   DEFAULT_CONDITION_LIMIT
-} from '../../../lib/apis'
-import { getNowDate } from '../../../lib/date'
+} from '/@/lib/apis'
+import { getNowDate } from '/@/lib/date'
 
 const usePaging = (
   getConditions: (req: ConditionRequest) => Promise<Condition[]>
diff --git a/webapp/frontend/src/components/Home/Auth.tsx b/webapp/frontend/src/components/Home/Auth.tsx
index cb07fbe72..660b8b2b8 100644
--- a/webapp/frontend/src/components/Home/Auth.tsx
+++ b/webapp/frontend/src/components/Home/Auth.tsx
@@ -1,4 +1,4 @@
-import Card from '../UI/Card'
+import Card from '/@/components/UI/Card'
 import logo_orange from '/@/assets/logo_orange.svg'
 
 const Auth = () => {
diff --git a/webapp/frontend/src/components/Home/Isus.tsx b/webapp/frontend/src/components/Home/Isus.tsx
index 75e7a5a8e..391587049 100644
--- a/webapp/frontend/src/components/Home/Isus.tsx
+++ b/webapp/frontend/src/components/Home/Isus.tsx
@@ -1,7 +1,7 @@
 import { useEffect } from 'react'
 import { useState } from 'react'
-import apis, { GetIsuListResponse } from '../../lib/apis'
-import IsuList from '../UI/IsuList'
+import apis, { GetIsuListResponse } from '/@/lib/apis'
+import IsuList from '/@/components/UI/IsuList'
 
 const LIMIT = 4
 
diff --git a/webapp/frontend/src/components/Home/Trend.tsx b/webapp/frontend/src/components/Home/Trend.tsx
index 5288a984c..2dc3ad0a6 100644
--- a/webapp/frontend/src/components/Home/Trend.tsx
+++ b/webapp/frontend/src/components/Home/Trend.tsx
@@ -1,5 +1,5 @@
-import { Trend } from '../../lib/apis'
-import { getConditionTime } from '../../lib/date'
+import { Trend } from '/@/lib/apis'
+import { getConditionTime } from '/@/lib/date'
 import TrendGraph from './TrendGraph'
 
 interface Props {
diff --git a/webapp/frontend/src/components/Home/TrendGraph.tsx b/webapp/frontend/src/components/Home/TrendGraph.tsx
index bb2f1c458..a1bab3daf 100644
--- a/webapp/frontend/src/components/Home/TrendGraph.tsx
+++ b/webapp/frontend/src/components/Home/TrendGraph.tsx
@@ -1,4 +1,4 @@
-import { Trend } from '../../lib/apis'
+import { Trend } from '/@/lib/apis'
 
 interface Props {
   trend: Trend
diff --git a/webapp/frontend/src/components/Home/TrendHeader.tsx b/webapp/frontend/src/components/Home/TrendHeader.tsx
index bb4d95417..48b3ec512 100644
--- a/webapp/frontend/src/components/Home/TrendHeader.tsx
+++ b/webapp/frontend/src/components/Home/TrendHeader.tsx
@@ -1,4 +1,4 @@
-import Tip from '../UI/Tip'
+import Tip from '/@/components/UI/Tip'
 
 const TrendHeadeer = () => {
   return (
diff --git a/webapp/frontend/src/components/Home/TrendList.tsx b/webapp/frontend/src/components/Home/TrendList.tsx
index 41124397a..41496f4d8 100644
--- a/webapp/frontend/src/components/Home/TrendList.tsx
+++ b/webapp/frontend/src/components/Home/TrendList.tsx
@@ -1,7 +1,7 @@
 import { useEffect } from 'react'
 import { useState } from 'react'
-import apis, { TrendResponse, Trend } from '../../lib/apis'
-import NowLoading from '../UI/NowLoading'
+import apis, { TrendResponse, Trend } from '/@/lib/apis'
+import NowLoading from '/@/components/UI/NowLoading'
 import TrendElement from './Trend'
 import TrendHeadeer from './TrendHeader'
 
@@ -32,9 +32,8 @@ const TrendList = () => {
     update()
   }, [])
 
-  if (trends.length === 0) {
-    return <NowLoading />
-  }
+  if (trends.length === 0) return <NowLoading />
+
   return (
     <div>
       <h2 className="mb-6 text-xl font-bold">みんなのISU</h2>
diff --git a/webapp/frontend/src/components/Isu/SubHeader.tsx b/webapp/frontend/src/components/Isu/SubHeader.tsx
index a539d4bab..4bdfb3756 100644
--- a/webapp/frontend/src/components/Isu/SubHeader.tsx
+++ b/webapp/frontend/src/components/Isu/SubHeader.tsx
@@ -1,4 +1,4 @@
-import { Isu } from '../../lib/apis'
+import { Isu } from '/@/lib/apis'
 import Tab from './Tab'
 
 const SubHeader = ({ isu }: { isu: Isu }) => {
diff --git a/webapp/frontend/src/components/IsuDetail/IsuIcon.tsx b/webapp/frontend/src/components/IsuDetail/IsuIcon.tsx
index 36f26ba31..42ab50632 100644
--- a/webapp/frontend/src/components/IsuDetail/IsuIcon.tsx
+++ b/webapp/frontend/src/components/IsuDetail/IsuIcon.tsx
@@ -1,4 +1,4 @@
-import { Isu } from '../../lib/apis'
+import { Isu } from '/@/lib/apis'
 
 interface Props {
   isu: Isu
diff --git a/webapp/frontend/src/components/IsuDetail/MainInfo.tsx b/webapp/frontend/src/components/IsuDetail/MainInfo.tsx
index 5f628af64..38955f1fe 100644
--- a/webapp/frontend/src/components/IsuDetail/MainInfo.tsx
+++ b/webapp/frontend/src/components/IsuDetail/MainInfo.tsx
@@ -1,4 +1,4 @@
-import { Isu } from '../../lib/apis'
+import { Isu } from '/@/lib/apis'
 import IsuIcon from './IsuIcon'
 
 interface Props {
diff --git a/webapp/frontend/src/components/IsuGraph/GraphNavigator.tsx b/webapp/frontend/src/components/IsuGraph/GraphNavigator.tsx
index 6eb3b8482..8eb53e072 100644
--- a/webapp/frontend/src/components/IsuGraph/GraphNavigator.tsx
+++ b/webapp/frontend/src/components/IsuGraph/GraphNavigator.tsx
@@ -1,5 +1,5 @@
 import { IoIosArrowBack, IoIosArrowForward } from 'react-icons/io'
-import IconButton from '../UI/IconButton'
+import IconButton from '/@/components/UI/IconButton'
 import DateInput from './DateInput'
 
 interface Props {
diff --git a/webapp/frontend/src/components/IsuGraph/IsuGraphCardContent.tsx b/webapp/frontend/src/components/IsuGraph/IsuGraphCardContent.tsx
index 097477734..edf376df1 100644
--- a/webapp/frontend/src/components/IsuGraph/IsuGraphCardContent.tsx
+++ b/webapp/frontend/src/components/IsuGraph/IsuGraphCardContent.tsx
@@ -1,19 +1,25 @@
-import apis, { Isu, GraphRequest } from '../../lib/apis'
+import apis, { Isu, GraphRequest } from '/@/lib/apis'
 import { useCallback } from 'react'
-import NowLoading from '../UI/NowLoading'
+import NowLoading from '/@/components/UI/NowLoading'
 import TransitionGraph from './TransitionGraph'
 import SittingGraph from './SittingGraph'
 import useGraph from './use/graph'
 import GraphNavigator from './GraphNavigator'
+import { useState } from 'react'
+import NowLoadingOverlay from '/@/components/UI/NowLoadingOverlay'
 
 interface Props {
   isu: Isu
 }
 
 const IsuGraphCardContent = ({ isu }: Props) => {
+  const [isLoading, setIsLoading] = useState(false)
   const getGraphs = useCallback(
-    (params: GraphRequest) => {
-      return apis.getIsuGraphs(isu.jia_isu_uuid, params)
+    async (params: GraphRequest) => {
+      setIsLoading(true)
+      const res = await apis.getIsuGraphs(isu.jia_isu_uuid, params)
+      setIsLoading(false)
+      return res
     },
     [isu.jia_isu_uuid]
   )
@@ -30,9 +36,8 @@ const IsuGraphCardContent = ({ isu }: Props) => {
     next
   } = useGraph(getGraphs)
 
-  if (graphs.length === 0) {
-    return <NowLoading />
-  }
+  if (graphs.length === 0) return <NowLoading />
+
   return (
     <div className="flex flex-col gap-12">
       <div className="flex justify-center w-full">
@@ -58,6 +63,7 @@ const IsuGraphCardContent = ({ isu }: Props) => {
             timeCategories={timeCategories}
           />
         </div>
+        {isLoading ? <NowLoadingOverlay /> : null}
       </div>
     </div>
   )
diff --git a/webapp/frontend/src/components/IsuGraph/use/graph.ts b/webapp/frontend/src/components/IsuGraph/use/graph.ts
index ae97cb541..9363b5f11 100644
--- a/webapp/frontend/src/components/IsuGraph/use/graph.ts
+++ b/webapp/frontend/src/components/IsuGraph/use/graph.ts
@@ -1,6 +1,6 @@
 import { useEffect, useState } from 'react'
-import { GraphRequest, Graph } from '../../../lib/apis'
-import { getNextDate, getPrevDate, getTodayDate } from '../../../lib/date'
+import { GraphRequest, Graph } from '/@/lib/apis'
+import { getNextDate, getPrevDate, getTodayDate } from '/@/lib/date'
 
 export interface Tooltip {
   score: string
diff --git a/webapp/frontend/src/components/PageHeader/Controls.tsx b/webapp/frontend/src/components/PageHeader/Controls.tsx
index bf6f987d0..e93cb896b 100644
--- a/webapp/frontend/src/components/PageHeader/Controls.tsx
+++ b/webapp/frontend/src/components/PageHeader/Controls.tsx
@@ -1,4 +1,4 @@
-import { useStateContext } from '../../context/state'
+import { useStateContext } from '/@/context/state'
 import { MdAccountCircle } from 'react-icons/md'
 import { useState } from 'react'
 
diff --git a/webapp/frontend/src/components/PageHeader/UserControlModal.tsx b/webapp/frontend/src/components/PageHeader/UserControlModal.tsx
index 00d263eb8..ba7336431 100644
--- a/webapp/frontend/src/components/PageHeader/UserControlModal.tsx
+++ b/webapp/frontend/src/components/PageHeader/UserControlModal.tsx
@@ -1,8 +1,8 @@
 import Modal from 'react-modal'
 import { IoMdLogOut } from 'react-icons/io'
 import { Link } from 'react-router-dom'
-import { useDispatchContext } from '../../context/state'
-import apis from '../../lib/apis'
+import { useDispatchContext } from '/@/context/state'
+import apis from '/@/lib/apis'
 
 interface Props {
   isOpen: boolean
diff --git a/webapp/frontend/src/components/UI/Isu.tsx b/webapp/frontend/src/components/UI/Isu.tsx
index f48f96e46..55c79f1c4 100644
--- a/webapp/frontend/src/components/UI/Isu.tsx
+++ b/webapp/frontend/src/components/UI/Isu.tsx
@@ -1,5 +1,5 @@
 import { Link } from 'react-router-dom'
-import { Isu as IIsu } from '../../lib/apis'
+import { Isu as IIsu } from '/@/lib/apis'
 
 interface Props {
   isu: IIsu
diff --git a/webapp/frontend/src/components/UI/IsuList.tsx b/webapp/frontend/src/components/UI/IsuList.tsx
index ab54083eb..fbf0371b1 100644
--- a/webapp/frontend/src/components/UI/IsuList.tsx
+++ b/webapp/frontend/src/components/UI/IsuList.tsx
@@ -1,5 +1,5 @@
-import { GetIsuListResponse } from '../../lib/apis'
-import { getConditionTime } from '../../lib/date'
+import { GetIsuListResponse } from '/@/lib/apis'
+import { getConditionTime } from '/@/lib/date'
 import Isu from './Isu'
 import Tip from './Tip'
 
diff --git a/webapp/frontend/src/components/UI/NowLoading.tsx b/webapp/frontend/src/components/UI/NowLoading.tsx
index 502bdc264..af308cd32 100644
--- a/webapp/frontend/src/components/UI/NowLoading.tsx
+++ b/webapp/frontend/src/components/UI/NowLoading.tsx
@@ -1,5 +1,17 @@
 const NowLoading = () => {
-  return <div>now loading</div>
+  const base =
+    'bg-accent-primary opacity-80 w-4 h-35 m-2 rounded-md inline-block'
+  return (
+    <div className="h-100 flex items-center justify-center">
+      <span>
+        <span className={base + ' animate-loader0'} />
+        <span className={base + ' animate-loader1'} />
+        <span className={base + ' animate-loader2'} />
+        <span className={base + ' animate-loader3'} />
+        <span className={base + ' animate-loader4'} />
+      </span>
+    </div>
+  )
 }
 
 export default NowLoading
diff --git a/webapp/frontend/src/components/UI/NowLoadingOverlay.tsx b/webapp/frontend/src/components/UI/NowLoadingOverlay.tsx
new file mode 100644
index 000000000..ac8d8cf4e
--- /dev/null
+++ b/webapp/frontend/src/components/UI/NowLoadingOverlay.tsx
@@ -0,0 +1,11 @@
+import NowLoading from './NowLoading'
+
+const NowLoadingOverlay = () => {
+  return (
+    <div className="absolute top-0 flex items-center justify-center w-full h-full bg-gray-500 bg-opacity-25">
+      <NowLoading />
+    </div>
+  )
+}
+
+export default NowLoadingOverlay
diff --git a/webapp/frontend/src/components/UI/PagingNavigator.tsx b/webapp/frontend/src/components/UI/PagingNavigator.tsx
index 9397f4bb4..c4ebd600d 100644
--- a/webapp/frontend/src/components/UI/PagingNavigator.tsx
+++ b/webapp/frontend/src/components/UI/PagingNavigator.tsx
@@ -1,5 +1,5 @@
 import { IoIosArrowBack, IoIosArrowForward } from 'react-icons/io'
-import IconButton from '../UI/IconButton'
+import IconButton from '/@/components/UI/IconButton'
 
 interface Props {
   length: number
diff --git a/webapp/frontend/src/context/state.tsx b/webapp/frontend/src/context/state.tsx
index 60cb21f22..f13808a83 100644
--- a/webapp/frontend/src/context/state.tsx
+++ b/webapp/frontend/src/context/state.tsx
@@ -1,5 +1,5 @@
 import { createContext, Dispatch, useContext, useReducer } from 'react'
-import { User } from '../lib/apis'
+import { User } from '/@/lib/apis'
 
 export interface State {
   me: User | null
diff --git a/webapp/frontend/src/lib/login.ts b/webapp/frontend/src/lib/login.ts
index 6c068175f..5dfefecad 100644
--- a/webapp/frontend/src/lib/login.ts
+++ b/webapp/frontend/src/lib/login.ts
@@ -1,6 +1,6 @@
 import { CancelToken } from 'axios'
 import { useCallback, useState } from 'react'
-import { useDispatchContext, useStateContext } from '../context/state'
+import { useDispatchContext, useStateContext } from '/@/context/state'
 import apis, { User } from './apis'
 
 const useLogin = () => {
diff --git a/webapp/frontend/src/pages/Home.tsx b/webapp/frontend/src/pages/Home.tsx
index fb78cc8d4..15dc15536 100644
--- a/webapp/frontend/src/pages/Home.tsx
+++ b/webapp/frontend/src/pages/Home.tsx
@@ -1,8 +1,8 @@
-import Auth from '../components/Home/Auth'
-import Isus from '../components/Home/Isus'
-import TrendList from '../components/Home/TrendList'
-import Card from '../components/UI/Card'
-import { useStateContext } from '../context/state'
+import Auth from '/@/components/Home/Auth'
+import Isus from '/@/components/Home/Isus'
+import TrendList from '/@/components/Home/TrendList'
+import Card from '/@/components/UI/Card'
+import { useStateContext } from '/@/context/state'
 
 const Home = () => {
   const state = useStateContext()
diff --git a/webapp/frontend/src/pages/IsuCondition.tsx b/webapp/frontend/src/pages/IsuCondition.tsx
index 3af1779ef..d3e3e7c3b 100644
--- a/webapp/frontend/src/pages/IsuCondition.tsx
+++ b/webapp/frontend/src/pages/IsuCondition.tsx
@@ -1,16 +1,15 @@
-import IsuConditionCardContent from '../components/Condition/IsuConditionCardContent'
-import Card from '../components/UI/Card'
-import NowLoading from '../components/UI/NowLoading'
-import { Isu } from '../lib/apis'
+import IsuConditionCardContent from '/@/components/Condition/IsuConditionCardContent'
+import Card from '/@/components/UI/Card'
+import NowLoading from '/@/components/UI/NowLoading'
+import { Isu } from '/@/lib/apis'
 
 interface Props {
   isu: Isu
 }
 
 const IsuCondition = ({ isu }: Props) => {
-  if (!isu) {
-    return <NowLoading />
-  }
+  if (!isu) return <NowLoading />
+
   return (
     <div className="flex flex-col gap-10 items-center">
       <Card>
diff --git a/webapp/frontend/src/pages/IsuDetail.tsx b/webapp/frontend/src/pages/IsuDetail.tsx
index f93efcb03..6e2ac496a 100644
--- a/webapp/frontend/src/pages/IsuDetail.tsx
+++ b/webapp/frontend/src/pages/IsuDetail.tsx
@@ -1,16 +1,15 @@
-import MainInfo from '../components/IsuDetail/MainInfo'
-import Card from '../components/UI/Card'
-import NowLoading from '../components/UI/NowLoading'
-import { Isu } from '../lib/apis'
+import MainInfo from '/@/components/IsuDetail/MainInfo'
+import Card from '/@/components/UI/Card'
+import NowLoading from '/@/components/UI/NowLoading'
+import { Isu } from '/@/lib/apis'
 
 interface Props {
   isu: Isu
 }
 
 const IsuDetail = ({ isu }: Props) => {
-  if (!isu) {
-    return <NowLoading />
-  }
+  if (!isu) return <NowLoading />
+
   return (
     <div className="flex flex-col gap-10 items-center">
       <Card>
diff --git a/webapp/frontend/src/pages/IsuGraph.tsx b/webapp/frontend/src/pages/IsuGraph.tsx
index df8f43045..6463e6a74 100644
--- a/webapp/frontend/src/pages/IsuGraph.tsx
+++ b/webapp/frontend/src/pages/IsuGraph.tsx
@@ -1,7 +1,7 @@
-import Card from '../components/UI/Card'
-import NowLoading from '../components/UI/NowLoading'
-import { Isu } from '../lib/apis'
-import IsuGraphCardContent from '../components/IsuGraph/IsuGraphCardContent'
+import Card from '/@/components/UI/Card'
+import NowLoading from '/@/components/UI/NowLoading'
+import { Isu } from '/@/lib/apis'
+import IsuGraphCardContent from '/@/components/IsuGraph/IsuGraphCardContent'
 
 interface Props {
   isu: Isu
diff --git a/webapp/frontend/src/pages/IsuRoot.tsx b/webapp/frontend/src/pages/IsuRoot.tsx
index 231073e24..134c98bc9 100644
--- a/webapp/frontend/src/pages/IsuRoot.tsx
+++ b/webapp/frontend/src/pages/IsuRoot.tsx
@@ -1,9 +1,9 @@
 import { useEffect, useState } from 'react'
 import { Redirect, Switch, useParams } from 'react-router-dom'
-import SubHeader from '../components/Isu/SubHeader'
-import NowLoading from '../components/UI/NowLoading'
-import apis, { Isu } from '../lib/apis'
-import GuardedRoute from '../router/GuardedRoute'
+import SubHeader from '/@/components/Isu/SubHeader'
+import NowLoading from '/@/components/UI/NowLoading'
+import apis, { Isu } from '/@/lib/apis'
+import GuardedRoute from '/@/router/GuardedRoute'
 import IsuCondition from './IsuCondition'
 import IsuDetail from './IsuDetail'
 import IsuGraph from './IsuGraph'
@@ -32,6 +32,7 @@ const IsuRoot = () => {
     }
     return <NowLoading />
   }
+
   return (
     <div>
       <SubHeader isu={isu} />
diff --git a/webapp/frontend/src/pages/Register.tsx b/webapp/frontend/src/pages/Register.tsx
index 182ff037d..29ed0caa9 100644
--- a/webapp/frontend/src/pages/Register.tsx
+++ b/webapp/frontend/src/pages/Register.tsx
@@ -1,9 +1,9 @@
 import { useState } from 'react'
 import { useHistory } from 'react-router-dom'
-import Card from '../components/UI/Card'
-import IconInput from '../components/UI/IconInput'
-import Input from '../components/UI/Input'
-import apis, { PostIsuRequest } from '../lib/apis'
+import Card from '/@/components/UI/Card'
+import IconInput from '/@/components/UI/IconInput'
+import Input from '/@/components/UI/Input'
+import apis, { PostIsuRequest } from '/@/lib/apis'
 
 const Register = () => {
   const [id, setId] = useState('')
diff --git a/webapp/frontend/src/router/GuardedRoute.tsx b/webapp/frontend/src/router/GuardedRoute.tsx
index 4b796e1fe..b0a2d4c7f 100644
--- a/webapp/frontend/src/router/GuardedRoute.tsx
+++ b/webapp/frontend/src/router/GuardedRoute.tsx
@@ -1,8 +1,8 @@
 import axios from 'axios'
 import { useEffect } from 'react'
 import { Route, Redirect } from 'react-router-dom'
-import NowLoading from '../components/UI/NowLoading'
-import useLogin from '../lib/login'
+import NowLoading from '/@/components/UI/NowLoading'
+import useLogin from '/@/lib/login'
 
 const GuardedRoute = <T extends { path: string; children?: JSX.Element }>(
   props: T
@@ -15,9 +15,7 @@ const GuardedRoute = <T extends { path: string; children?: JSX.Element }>(
     return () => source.cancel()
   }, [login])
 
-  if (isTryLogin) {
-    return <NowLoading />
-  }
+  if (isTryLogin) return <NowLoading />
 
   if (!state.me) {
     if (props.path === '/login') {
diff --git a/webapp/frontend/tailwind.config.js b/webapp/frontend/tailwind.config.js
index 8e57b42c7..39c6c2db8 100644
--- a/webapp/frontend/tailwind.config.js
+++ b/webapp/frontend/tailwind.config.js
@@ -36,6 +36,20 @@ module.exports = {
       gridTemplateColumns: {
         isus: 'repeat(auto-fill,minmax(10rem,1fr))',
         trend: '10rem 1fr'
+      },
+      keyframes: {
+        scale: {
+          '0%': { transform: 'scaley(1.0)' },
+          '50%': { transform: 'scaley(0.4)' },
+          '100%': { transform: 'scaley(1.0)' }
+        }
+      },
+      animation: {
+        loader0: 'scale 1s infinite cubic-bezier(0.2, 0.68, 0.18, 1.08)',
+        loader1: 'scale 1s 0.1s infinite cubic-bezier(0.2, 0.68, 0.18, 1.08)',
+        loader2: 'scale 1s 0.2s infinite cubic-bezier(0.2, 0.68, 0.18, 1.08)',
+        loader3: 'scale 1s 0.3s infinite cubic-bezier(0.2, 0.68, 0.18, 1.08)',
+        loader4: 'scale 1s 0.4s infinite cubic-bezier(0.2, 0.68, 0.18, 1.08)'
       }
     }
   }
diff --git a/webapp/frontend/tsconfig.json b/webapp/frontend/tsconfig.json
index e99fc002a..cc58b6f98 100644
--- a/webapp/frontend/tsconfig.json
+++ b/webapp/frontend/tsconfig.json
@@ -13,10 +13,11 @@
     "resolveJsonModule": true,
     "isolatedModules": true,
     "noEmit": true,
-    "jsx": "react-jsx"
+    "jsx": "react-jsx",
+    "baseUrl": ".",
+    "paths": {
+      "/@/*": ["src/*"]
+    }
   },
-  "paths": {
-    "/@/*": ["src/*"]
-  },
-  "include": ["./src"]
+  "include": ["src"]
 }
diff --git a/webapp/public/assets/index.4689f22a.css b/webapp/public/assets/index.4689f22a.css
deleted file mode 100644
index 13c65219c..000000000
--- a/webapp/public/assets/index.4689f22a.css
+++ /dev/null
@@ -1,1 +0,0 @@
-#root,body,html{margin:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;height:100%;min-width:100%}code{font-family:source-code-pro,Menlo,Monaco,Consolas,'Courier New',monospace}a,a:active,a:visited{text-decoration:none;color:inherit}input{padding:0;border:none;border-radius:0;outline:0;background:0 0}*,::after,::before{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}:-moz-focusring{outline:1px dotted ButtonText}:-moz-ui-invalid{box-shadow:none}::moz-focus-inner{border-style:none;padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}body{margin:0;font-family:inherit;line-height:inherit}button{text-transform:none;background-color:transparent;background-image:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}[role=button],button{cursor:pointer}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}h2,h3{margin:0;font-size:inherit;font-weight:inherit}button,input{font-family:inherit;font-size:100%;line-height:1.15;margin:0;padding:0;line-height:inherit;color:inherit}img{border-style:solid;display:block;vertical-align:middle;max-width:100%;height:auto}input::placeholder{opacity:1;color:#9ca3af}input::webkit-input-placeholder{opacity:1;color:#9ca3af}input::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder{opacity:1;color:#9ca3af}input::-ms-input-placeholder{opacity:1;color:#9ca3af}.bg-primary{--tw-bg-opacity:1;background-color:rgba(243,244,245,var(--tw-bg-opacity))}.bg-teritary{--tw-bg-opacity:1;background-color:rgba(241,241,241,var(--tw-bg-opacity))}.bg-button{--tw-bg-opacity:1;background-color:rgba(33,57,75,var(--tw-bg-opacity))}.bg-status-info{--tw-bg-opacity:1;background-color:rgba(148,239,188,var(--tw-bg-opacity))}.bg-status-warning{--tw-bg-opacity:1;background-color:rgba(255,239,91,var(--tw-bg-opacity))}.bg-status-critical{--tw-bg-opacity:1;background-color:rgba(246,152,152,var(--tw-bg-opacity))}.bg-secondary{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-accent-primary{--tw-bg-opacity:1;background-color:rgba(255,100,51,var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.bg-status-sitting{--tw-bg-opacity:1;background-color:rgba(255,188,126,var(--tw-bg-opacity))}.border-outline{--tw-border-opacity:1;border-color:rgba(225,229,230,var(--tw-border-opacity))}.border-accent-primary{--tw-border-opacity:1;border-color:rgba(255,100,51,var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.rounded{border-radius:.25rem}.rounded-3xl{border-radius:1.5rem}.rounded-2xl{border-radius:1rem}.border-solid{border-style:solid}.border{border-width:1px}.border-2{border-width:2px}.border-b-0{border-bottom-width:0}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-1{border-left-width:1px}.cursor-pointer{cursor:pointer}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.flex{display:-webkit-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.grid{display:-ms-grid;display:grid}.flex-row{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;-webkit-flex-direction:row;flex-direction:row}.flex-col{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;-webkit-flex-direction:column;flex-direction:column}.flex-wrap{-ms-flex-wrap:wrap;-webkit-flex-wrap:wrap;flex-wrap:wrap}.items-center{-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.justify-items-center{justify-items:center}.justify-center{-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center}.justify-between{-webkit-box-pack:justify;-ms-flex-pack:justify;-webkit-justify-content:space-between;justify-content:space-between}.flex-1{-webkit-box-flex:1;-ms-flex:1 1 0%;-webkit-flex:1 1 0%;flex:1 1 0%}.font-bold{font-weight:700}.font-medium{font-weight:500}.h-12{height:3rem}.h-4{height:1rem}.h-full{height:100%}.h-48{height:12rem}.h-11{height:2.75rem}.h-8{height:2rem}.h-6{height:1.5rem}.h-18{height:4.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-2xl{font-size:1.5rem;line-height:2rem}.leading-4{line-height:1rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mr-auto{margin-right:auto}.mt-4{margin-top:1rem}.mt-16{margin-top:4rem}.mt-10{margin-top:2.5rem}.mb-6{margin-bottom:1.5rem}.mt-8{margin-top:2rem}.mb-3{margin-bottom:.75rem}.ml-6{margin-left:1.5rem}.mr-4{margin-right:1rem}.ml-1{margin-left:.25rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mb-8{margin-bottom:2rem}.mt-6{margin-top:1.5rem}.max-w-sm{max-width:24rem}.max-w-4xl{max-width:56rem}.max-w-2xl{max-width:42rem}.min-h-full{min-height:100%}.object-contain{-o-object-fit:contain;object-fit:contain}.disabled\:opacity-25:disabled{opacity:.25}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.p-4{padding:1rem}.p-2{padding:.5rem}.p-10{padding:2.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-16{padding-left:4rem;padding-right:4rem}.py-12{padding-top:3rem;padding-bottom:3rem}.px-4{padding-left:1rem;padding-right:1rem}.pb-0{padding-bottom:0}.pl-12{padding-left:3rem}.pt-8{padding-top:2rem}.pl-8{padding-left:2rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-8{padding-right:2rem}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.top-0{top:0}.top-18{top:4.5rem}.right-0{right:0}.text-center{text-align:center}.text-primary{--tw-text-opacity:1;color:rgba(36,30,18,var(--tw-text-opacity))}.text-secondary{--tw-text-opacity:1;color:rgba(107,105,101,var(--tw-text-opacity))}.text-teritary{--tw-text-opacity:1;color:rgba(204,204,204,var(--tw-text-opacity))}.text-white-primary{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-accent-primary{--tw-text-opacity:1;color:rgba(255,100,51,var(--tw-text-opacity))}.text-status-info{--tw-text-opacity:1;color:rgba(34,98,62,var(--tw-text-opacity))}.text-status-warning{--tw-text-opacity:1;color:rgba(96,89,16,var(--tw-text-opacity))}.text-status-sitting{--tw-text-opacity:1;color:rgba(96,58,24,var(--tw-text-opacity))}.text-status-critical{--tw-text-opacity:1;color:rgba(81,36,36,var(--tw-text-opacity))}.content-type{content:"type"}.align-middle{vertical-align:middle}.w-24{width:6rem}.w-full{width:100%}.w-40{width:10rem}.w-20{width:5rem}.w-48{width:12rem}.w-52{width:13rem}.w-25{width:6.25rem}.z-10{z-index:10}.gap-4{grid-gap:1rem;gap:1rem}.gap-8{grid-gap:2rem;gap:2rem}.gap-2{grid-gap:.5rem;gap:.5rem}.gap-6{grid-gap:1.5rem;gap:1.5rem}.gap-12{grid-gap:3rem;gap:3rem}.gap-16{grid-gap:4rem;gap:4rem}.gap-10{grid-gap:2.5rem;gap:2.5rem}.grid-cols-trend{grid-template-columns:10rem 1fr}.grid-cols-isus{grid-template-columns:repeat(auto-fill,minmax(10rem,1fr))}
\ No newline at end of file
diff --git a/webapp/public/assets/index.b1cd63b1.js b/webapp/public/assets/index.b1cd63b1.js
new file mode 100644
index 000000000..8a3d284c0
--- /dev/null
+++ b/webapp/public/assets/index.b1cd63b1.js
@@ -0,0 +1,1 @@
+var e=Object.defineProperty,t=Object.defineProperties,a=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,r=(t,a,n)=>a in t?e(t,a,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[a]=n,i=(e,t)=>{for(var a in t||(t={}))l.call(t,a)&&r(e,a,t[a]);if(n)for(var a of n(t))s.call(t,a)&&r(e,a,t[a]);return e},c=(e,n)=>t(e,a(n)),o=(e,t)=>{var a={};for(var r in e)l.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(null!=e&&n)for(var r of n(e))t.indexOf(r)<0&&s.call(e,r)&&(a[r]=e[r]);return a};import{r as m,R as u,a as d,M as p,L as E,I as f,T as g,b as x,u as v,c as h,d as y,e as b,f as w,g as N,F as _,G as C,_ as D,h as S,i as j,S as k,j as $,B as I,k as O}from"./vendor.519e803f.js";const T={me:null},P=m.exports.createContext(T),q=m.exports.createContext(null),z=(e,t)=>{switch(t.type){case"login":return c(i({},e),{me:t.user});case"logout":return c(i({},e),{me:null});default:throw new Error("invalid action")}},U=({children:e})=>{const[t,a]=m.exports.useReducer(z,T);return u.createElement(P.Provider,{value:t},u.createElement(q.Provider,{value:a},e))},G=()=>{const e=m.exports.useContext(P);if(!e)throw new Error("require wrapped by stateContext");return e},L=()=>{const e=m.exports.useContext(q);if(!e)throw new Error("require wrapped by stateContext");return e},M=({children:e})=>u.createElement("div",{className:"text-white-primary font-bold"},e),A=()=>new Date,F=e=>Math.floor(e.getTime()/1e3),V=e=>new Date(1e3*e),B=e=>`${e.getFullYear()}/${R(e.getMonth()+1)}/${R(e.getDate())} ${R(e.getHours())}:${R(e.getMinutes())}:${R(e.getSeconds())}`,R=e=>("0"+e).slice(-2);const Z=new class{async postAuth(e,t){await d.post("/api/auth",{},i({headers:{Authorization:`Bearer ${e}`}},t))}async postSignout(e){await d.post("/api/signout",e)}async getUserMe(e){const{data:t}=await d.get("/api/user/me",e);return t}async getIsus(e,t){const{data:a}=await d.get("/api/isu",i({params:e},t)),n=[];for(const l of a)n.push(c(i({},l),{latest_isu_condition:l.latest_isu_condition?c(i({},l.latest_isu_condition),{date:V(l.latest_isu_condition.timestamp)}):null}));return n}async postIsu(e,t){const a=new FormData;a.append("jia_isu_uuid",e.jia_isu_uuid),a.append("isu_name",e.isu_name),e.image&&a.append("image",e.image,e.image.name),await d.post("/api/isu",a,i({headers:{"content-type":"multipart/form-data"}},t))}async getIsu(e,t){const{data:a}=await d.get(`/api/isu/${e}`,t);return a}async deleteIsu(e,t){await d.delete(`/api/isu/${e}`,t)}async getIsuGraphs(e,t,a){const n={datetime:F(t.date)},{data:l}=await d.get(`/api/isu/${e}/graph`,i({params:n},a)),s=[];return l.forEach((e=>{s.push(c(i({},e),{start_at:V(e.start_at),end_at:V(e.end_at)}))})),s}async getIsuConditions(e,t,a){const n=c(i({},t),{start_time:t.start_time?F(t.start_time):void 0,end_time:F(t.end_time)}),{data:l}=await d.get(`/api/condition/${e}`,i({params:n},a)),s=[];return l.forEach((e=>{s.push(c(i({},e),{date:V(e.timestamp)}))})),s}async getTrend(e){const{data:t}=await d.get("/api/trend",i({},e)),a=[];return t.forEach((e=>{const t=[],n=[],l=[];e.info.forEach((e=>{t.push(c(i({},e),{date:V(e.timestamp)}))})),e.warning.forEach((e=>{n.push(c(i({},e),{date:V(e.timestamp)}))})),e.critical.forEach((e=>{l.push(c(i({},e),{date:V(e.timestamp)}))})),a.push(c(i({},e),{info:t,warning:n,critical:l}))})),a}},J=e=>{const t=L();return u.createElement(p,{isOpen:e.isOpen,onRequestClose:e.toggle,className:"top-18 bg-gray-50 absolute right-0 flex justify-items-center px-6 py-3 border border-outline rounded",overlayClassName:"fixed inset-0",shouldCloseOnOverlayClick:!0},u.createElement(E,{to:"/",onClick:async()=>{await Z.postSignout(),t({type:"logout"})},className:"flex items-center text-primary"},u.createElement(f,{className:"mr-1",size:16}),u.createElement("div",null,"logout")))},H=e=>u.createElement(M,null,u.createElement(E,{to:e.to,className:"flex items-center"},e.icon,u.createElement("div",{className:"ml-1"},e.label))),K=()=>{const[e,t]=m.exports.useState(!1),a=()=>{t(!e)},n=G().me;return n?u.createElement("div",{className:"w-52 flex items-center justify-between ml-auto"},u.createElement(H,{to:"/register",label:"ISUの登録",icon:u.createElement(g,null)}),u.createElement("div",{className:"border-l-1 pl-4 border-white"},u.createElement(M,null,u.createElement("div",{className:"flex items-center cursor-pointer",onClick:a},u.createElement(x,null),u.createElement("div",{className:"ml-1"},n.jia_user_id)))),u.createElement(J,{isOpen:e,toggle:a})):u.createElement(u.Fragment,null)};const X=()=>u.createElement("header",{className:"h-18 flex items-center pl-6 pr-8 bg-accent-primary"},u.createElement(E,{to:"/"},u.createElement("img",{src:"/assets/logo_white.98008342.svg",alt:"isucondition",className:"h-11 cursor-pointer"})),u.createElement(K,null)),Y=({children:e})=>u.createElement("div",{className:"px-16 py-12 w-full max-w-4xl bg-secondary border border-outline rounded"},e);const Q=()=>u.createElement(Y,null,u.createElement("div",{className:"flex flex-col items-center w-full"},u.createElement("img",{src:"/assets/logo_orange.974bf3e6.svg",alt:"isucondition",className:"my-2 max-w-sm"}),u.createElement("div",{className:"mt-4 text-lg"},"ISUとつくる新しい明日"),u.createElement("div",{className:"mt-16 w-full border-b border-outline"}),u.createElement("button",{className:"mt-10 px-5 py-2 h-12 text-white-primary font-bold bg-button rounded-3xl",onClick:async()=>{location.href=`${location.protocol}//${document.domain}:5000?callback=${location.protocol}//${location.host}`}},"JIAのアカウントでログイン"))),W=({isu:e})=>u.createElement(E,{to:`/isu/${e.jia_isu_uuid}`,className:"flex flex-col items-center"},u.createElement("img",{src:`/api/isu/${e.jia_isu_uuid}/icon`,className:"w-48 h-48 object-contain",key:e.jia_isu_uuid}),u.createElement("h3",null,e.name)),ee=({variant:e,label:t})=>{const a=`h-6 rounded-2xl px-4 font-medium text-center ${(()=>{switch(e){case"info":return"bg-status-info text-status-info";case"warning":return"bg-status-warning text-status-warning";case"sitting":return"bg-status-sitting text-status-sitting";case"critical":return"bg-status-critical text-status-critical"}})()}`;return u.createElement("div",{className:a},null!=t?t:e)},te=({isus:e})=>u.createElement("div",{className:"grid gap-8 grid-cols-isus w-full"},e.map((e=>u.createElement("div",{key:e.jia_isu_uuid,className:"flex flex-col items-center"},u.createElement(W,{isu:e}),e.latest_isu_condition?u.createElement("div",null,B(e.latest_isu_condition.date)):null,e.latest_isu_condition?u.createElement(ee,{variant:e.latest_isu_condition.condition_level}):null)))),ae=()=>{const[e,t]=m.exports.useState([]);return m.exports.useEffect((()=>{(async()=>{t(await Z.getIsus({limit:4}))})()}),[t]),u.createElement("div",null,u.createElement("h2",{className:"mb-6 text-xl font-bold"},"ISU"),u.createElement(te,{isus:e}),u.createElement("div",{className:"flex gap-12 items-center justify-center mt-8"}))},ne=()=>{const e="bg-accent-primary opacity-80 w-4 h-35 m-2 rounded-md inline-block";return u.createElement("div",{className:"h-100 flex items-center justify-center"},u.createElement("span",null,u.createElement("span",{className:e+" animate-loader0"}),u.createElement("span",{className:e+" animate-loader1"}),u.createElement("span",{className:e+" animate-loader2"}),u.createElement("span",{className:e+" animate-loader3"}),u.createElement("span",{className:e+" animate-loader4"})))},le=(e,t)=>`${Math.round(e/t*100)}%`,se=({trend:e,maxConditionCount:t})=>u.createElement("div",{className:"flex items-center w-full h-4"},u.createElement("div",{className:"h-full bg-status-info",style:{width:le(e.info.length,t)}}),u.createElement("div",{className:"h-full bg-status-warning",style:{width:le(e.warning.length,t)}}),u.createElement("div",{className:"h-full bg-status-critical",style:{width:le(e.critical.length,t)}})),re=({trend:e,maxConditionCount:t})=>u.createElement("div",{className:"grid grid-cols-trend p-2"},u.createElement("div",{className:"flex flex-col"},u.createElement("div",null,e.character),u.createElement("div",{className:"text-secondary"},(e=>{let t=null;for(const a of[e.info,e.warning,e.critical].flat())(!t||t<a.date)&&(t=a.date);return t?B(t):"no data"})(e))),u.createElement("div",{className:"flex items-center"},u.createElement(se,{trend:e,maxConditionCount:t}))),ie=()=>u.createElement("div",{className:"grid grid-cols-trend p-2"},u.createElement("div",{className:"flex justify-center"},u.createElement("div",null,"せいかく")),u.createElement("div",{className:"flex gap-4 items-center justify-center"},u.createElement(ee,{variant:"info",label:"バッチリ"}),u.createElement(ee,{variant:"warning",label:"ぼちぼち"}),u.createElement(ee,{variant:"critical",label:"こわれた"}))),ce=e=>e.info.length+e.warning.length+e.critical.length,oe=()=>{const[e,t]=m.exports.useState([]),[a,n]=m.exports.useState(0);return m.exports.useEffect((()=>{(async()=>{const e=await Z.getTrend();e.sort(((e,t)=>ce(t)-ce(e))),t(e);let a=0;e.forEach((e=>{const t=ce(e);t>a&&(a=t)})),n(a)})()}),[]),0===e.length?u.createElement(ne,null):u.createElement("div",null,u.createElement("h2",{className:"mb-6 text-xl font-bold"},"みんなのISU"),u.createElement(ie,null),e.map((e=>u.createElement(re,{key:e.character,trend:e,maxConditionCount:a}))))},me=()=>G().me?u.createElement(de,null):u.createElement(ue,null),ue=()=>u.createElement("div",{className:"flex flex-col gap-10 items-center p-10"},u.createElement(Q,null),u.createElement(Y,null,u.createElement(oe,null))),de=()=>u.createElement("div",{className:"flex flex-col gap-10 items-center p-10"},u.createElement(Y,null,u.createElement(ae,null))),pe=({to:e,label:t})=>{const{pathname:a}=v(),n=a===e;return u.createElement("div",{className:"w-20 flex justify-center "+(n?"border-b-2 text-accent-primary border-accent-primary font-bold":"text-secondary")},u.createElement(E,{to:e},t))},Ee=({id:e})=>u.createElement("div",{className:"flex"},u.createElement(pe,{to:`/isu/${e}`,label:"詳細"}),u.createElement(pe,{to:`/isu/${e}/condition`,label:"状態"}),u.createElement(pe,{to:`/isu/${e}/graph`,label:"グラフ"})),fe=({isu:e})=>u.createElement("header",{className:"pb-0 pl-12 pt-8 bg-secondary"},u.createElement("h2",{className:"mb-3 text-2xl font-bold"},e.name),u.createElement("div",{className:"ml-6"},u.createElement(Ee,{id:e.jia_isu_uuid}))),ge=e=>{const{isTryLogin:t,login:a,state:n}=(()=>{const e=G(),t=L(),[a,n]=m.exports.useState(!0),l=m.exports.useCallback((e=>{t({type:"login",user:e}),n(!1)}),[t]);return{isTryLogin:a,login:m.exports.useCallback((async t=>{if(e.me)n(!1);else try{n(!0);try{const e=await Z.getUserMe({cancelToken:t});l(e)}catch{const e=new URL(location.href),a=e.searchParams.get("jwt");if(!a)return void n(!1);e.searchParams.delete("jwt"),history.replaceState(null,"",e.href),await Z.postAuth(a,{cancelToken:t});const s=await Z.getUserMe({cancelToken:t});l(s)}}catch{n(!1)}}),[e.me,l]),state:e}})();return m.exports.useEffect((()=>{const e=d.CancelToken.source();return a(e.token),()=>e.cancel()}),[a]),t?u.createElement(ne,null):n.me?u.createElement(y,i({},e)):"/login"===e.path?u.createElement(h,{to:"/login"}):"/"===e.path?u.createElement(y,i({},e)):u.createElement(h,{to:"/"})},xe=e=>{var t=e,{children:a,onClick:n}=t,l=o(t,["children","onClick"]);return u.createElement("button",i({className:"flex items-center focus:outline-none disabled:opacity-25 disabled:cursor-not-allowed",onClick:n},l),a)},ve=({conditions:e,next:t,prev:a,page:n})=>{const l=20===e.length,s=n>1;return u.createElement("div",{className:"center flex gap-8"},u.createElement(xe,{disabled:!s,onClick:a},u.createElement(b,{size:24})),u.createElement("div",{className:"align-middle text-xl"},n),u.createElement(xe,{disabled:!l,onClick:t},u.createElement(w,{size:24})))},he=({name:e,status:t})=>{const a=(()=>{switch(e){case"is_dirty":return u.createElement(C,{size:20});case"is_overweight":return u.createElement(_,{size:20});case"is_broken":return u.createElement(N,{size:20})}})(),n=`${(()=>{switch(t){case!0:return"text-primary";case!1:return"text-teritary"}})()}`;return u.createElement("div",{className:n},a)},ye=({conditionCSV:e})=>{const t=e.split(","),a=[];return t.forEach((e=>{const t=e.split("=");if(2!==t.length)return;const n="true"===t[1];a.push([t[0],n])})),u.createElement("div",{className:"flex"},a.map((e=>u.createElement("div",{key:e[0],className:"px-1"},u.createElement(he,{name:e[0],status:e[1]})))))},be=({condition:e})=>u.createElement("div",{className:"flex flex-wrap gap-4 items-center p-4"},u.createElement("div",{className:"mr-auto"},u.createElement("div",null,e.isu_name),u.createElement("div",{className:"text-secondary"},e.message)),u.createElement("div",null,u.createElement(ye,{conditionCSV:e.condition})),u.createElement("div",{className:"flex justify-center w-24"},e.is_sitting?u.createElement(ee,{variant:"sitting"}):null),u.createElement("div",{className:"flex justify-center w-24"},u.createElement(ee,{variant:e.condition_level})),u.createElement("div",null,u.createElement("div",null,B(e.date)))),we=({conditions:e})=>u.createElement("div",{className:"flex flex-col gap-4 items-center w-full"},u.createElement("div",{className:"w-full border border-b-0 border-outline"},e.map(((e,t)=>u.createElement("div",{className:"border-b border-outline",key:t},u.createElement(be,{condition:e})))))),Ne=e=>{var t=e,{label:a,classname:n}=t,l=o(t,["label","classname"]);return u.createElement("button",i({className:"px-3 py-1 h-8 leading-4 border border-outline rounded focus:outline-none "+n},l),a)},_e=({label:e,value:t,setValue:a,classname:n,inputProps:l})=>u.createElement("label",{className:"flex flex-col "+n},e,u.createElement("input",c(i({},l),{className:"px-2 py-1 bg-teritary border border-solid border-outline rounded",value:t,onChange:e=>a(e.target.value)}))),Ce=({times:e,setTimes:t})=>u.createElement("label",{className:"flex flex-col"},"時間指定",u.createElement("div",{className:"flex items-center"},u.createElement("input",{className:"px-2 py-1 w-40 bg-teritary border-2 border-outline rounded",value:e[0],onChange:a=>t([a.target.value,e[1]]),placeholder:"2020/01/01 11:11:11"}),u.createElement("div",{className:"text-xl"},"~"),u.createElement("input",{className:"px-2 py-1 w-40 bg-teritary border-2 border-outline rounded",value:e[1],onChange:a=>t([e[0],a.target.value]),placeholder:"2020/01/01 11:11:11"}))),De=({query:e,times:t,search:a})=>{const[n,l]=m.exports.useState(e),[s,r]=m.exports.useState(t);return u.createElement("div",{className:"flex flex-wrap gap-6 items-center"},u.createElement(_e,{label:"検索条件",value:n,setValue:l,classname:"flex-1"}),u.createElement(Ce,{times:s,setTimes:r}),u.createElement(Ne,{label:"検索",onClick:()=>a({times:s,query:n})}))},Se=e=>{const t=e.split(",");for(const a of t)if(!["critical","warning","info"].includes(a))return alert("検索条件には critical,warning,info のいずれか一つ以上をカンマ区切りで入力してください"),!1;return!0},je=e=>{const t=new Date(e);return!isNaN(t.getTime())&&t},ke=()=>u.createElement("div",{className:"absolute top-0 flex items-center justify-center w-full h-full bg-gray-500 bg-opacity-25"},u.createElement(ne,null)),$e=({isu:e})=>{const[t,a]=m.exports.useState(!1),n=m.exports.useCallback((async t=>{a(!0);const n=await Z.getIsuConditions(e.jia_isu_uuid,t);return a(!1),n}),[e]),{conditions:l,query:s,times:r,search:i,next:c,prev:o,page:d}=(e=>{const[t,a]=m.exports.useState([]);m.exports.useEffect((()=>{(async()=>{a(await e({end_time:A(),condition_level:"critical,warning,info"}))})()}),[e,a]);const[n,l]=m.exports.useState("critical,warning,info"),[s,r]=m.exports.useState(["",""]),[i,c]=m.exports.useState([[]]),[o,u]=m.exports.useState(1),d=()=>{const e=s[0]?new Date(s[0]+"Z"):new Date(0);return{end_time:s[1]?new Date(s[1]+"Z"):t[19].date,start_time:e,condition_level:n}};return{conditions:t,query:n,times:s,search:async t=>{if(t.query&&!Se(t.query))return;let n,s;if(t.times[0]){const e=je(t.times[0]+"Z");if(!e)return void alert("時間指定の下限が不正です");n=e}else n=new Date(0);if(t.times[1]){const e=je(t.times[1]+"Z");if(!e)return void alert("時間指定の上限が不正です");s=e}else s=A();l(t.query),r(t.times);const i={start_time:n,end_time:s,condition_level:t.query};a(await e(i)),u(1),c([[]])},page:o,next:async()=>{i[o]||(i[o]=t,c(i));const n=d();n&&(a(await e(n)),u(o+1))},prev:async()=>{a(i[o-1]),u(o-1)}}})(n);return u.createElement("div",{className:"flex flex-col gap-2"},u.createElement(De,{query:s,times:r,search:i}),u.createElement("div",{className:"flex flex-col gap-4 items-center"},u.createElement("div",{className:"relative w-full"},u.createElement(we,{conditions:l}),t?u.createElement(ke,null):null),u.createElement(ve,{conditions:l,page:d,next:c,prev:o})))},Ie=({isu:e})=>e?u.createElement("div",{className:"flex flex-col gap-10 items-center"},u.createElement(Y,null,u.createElement($e,{isu:e}))):u.createElement(ne,null),Oe=({isu:e,reloadKey:t})=>u.createElement("img",{src:`/api/isu/${e.jia_isu_uuid}/icon`,className:"w-48 h-48 object-contain",key:t}),Te=({isu:e})=>u.createElement("div",{className:"flex flex-wrap gap-16 justify-center"},u.createElement(Oe,{isu:e}),u.createElement("div",{className:"flex flex-col min-h-full"},u.createElement("div",{className:"text-xl font-bold"},e.name),u.createElement("div",{className:"flex flex-1 mt-4 pl-8"},u.createElement("div",{className:"mr-4"},"せいかく"),u.createElement("div",{className:"flex-1"},e.character)))),Pe=({isu:e})=>e?u.createElement("div",{className:"flex flex-col gap-10 items-center"},u.createElement(Y,null,u.createElement(Te,{isu:e}))):u.createElement(ne,null),qe=({transitionData:e,timeCategories:t,tooltipData:a})=>{const n={chart:{toolbar:{show:!1}},grid:{yaxis:{lines:{show:!1}}},colors:["#008FFB"],series:[{type:"line",data:e}],xaxis:{categories:t},yaxis:{min:0},tooltip:{custom:({dataPointIndex:e})=>ze(a[e])}};return u.createElement("div",null,u.createElement(D,{type:"line",options:n,series:n.series}))},ze=e=>`\n  <div class="flex flex-col px-3 py-1 text-primary">\n    <div class="flex flex-row">\n      <div class="w-25">score</div>\n      <div>${e.score}</div>\n    </div>\n    <div class="flex flex-row">\n      <div class="w-25">is_dirty</div>\n      <div>${e.is_dirty}</div>\n    </div>\n    <div class="flex flex-row">\n      <div class="w-25">is_overweight</div>\n      <div>${e.is_overweight}</div>\n    </div>\n    <div class="flex flex-row">\n      <div class="w-25">is_broken</div>\n      <div>${e.is_broken}</div>\n    </div>\n  </div>`,Ue=({sittingData:e,timeCategories:t})=>{const a={chart:{toolbar:{show:!1}},colors:["#ff6433"],series:[{type:"heatmap",data:e,name:""}],xaxis:{categories:t,labels:{show:!1}},plotOptions:{heatmap:{colorScale:{ranges:[{from:0,to:20,color:"#d1d1d1"}]}}}};return u.createElement("div",{style:{transform:"translateX(13px) translateY(-32px) scaleX(0.993)"}},u.createElement(D,{type:"heatmap",options:a,series:a.series}))},Ge=e=>{const[t,a]=m.exports.useState({graphs:[],transitionData:[],sittingData:[],timeCategories:[],day:"",tooltipData:[]}),[n,l]=m.exports.useState((()=>{const e=A();return e.setHours(0,0,0,0),e})());m.exports.useEffect((()=>{(async()=>{const t=await e({date:n}),l=Le(t);a((e=>c(i({},e),{graphs:t,transitionData:l.transitionData,sittingData:l.sittingData,timeCategories:l.timeCategories,day:n.toLocaleDateString(),tooltipData:l.tooltipData})))})()}),[e,a,n]);const s=async()=>{const t=await e({date:n}),l=Le(t);a((e=>c(i({},e),{loading:!1,graphs:t,transitionData:l.transitionData,sittingData:l.sittingData,timeCategories:l.timeCategories,day:n.toLocaleTimeString(),tooltipData:l.tooltipData})))};return c(i({},t),{fetchGraphs:async e=>{const t=new Date(e.day);isNaN(t.getTime())?alert("日時の指定が不正です"):(l(t),s())},prev:async()=>{l((e=>new Date(e.setDate(e.getDate()-1)))(n)),s()},next:async()=>{l((e=>new Date(e.setDate(e.getDate()+1)))(n)),s()}})},Le=e=>{const t=[],a=[],n=[],l=[];return e.forEach((e=>{e.data?(t.push(e.data.score),a.push(e.data.percentage.sitting),l.push({score:e.data.score.toString(),is_dirty:`${e.data.percentage.is_dirty}%`,is_overweight:`${e.data.percentage.is_overweight}%`,is_broken:`${e.data.percentage.is_broken}%`})):(t.push(0),a.push(0),l.push({score:"-",is_dirty:"-",is_overweight:"-",is_broken:"-"})),n.push(e.start_at.toLocaleTimeString("ja-JP",{hour:"2-digit",minute:"2-digit"}))})),{transitionData:t,sittingData:a,timeCategories:n,tooltipData:l}},Me=({day:e,fetchGraphs:t})=>{const[a,n]=m.exports.useState(e);return m.exports.useEffect((()=>{n(e)}),[e,n]),u.createElement("div",{className:"flex gap-8 w-full"},u.createElement(S,{value:a,onChange:e=>n(e.target.value),onKeyPress:e=>{"Enter"===e.key&&(e.preventDefault(),t({day:a}))}}))},Ae=({next:e,prev:t,day:a,fetchGraphs:n})=>u.createElement("div",{className:"flex gap-8"},u.createElement(xe,{onClick:t},u.createElement(b,{size:24})),u.createElement(Me,{day:a,fetchGraphs:n}),u.createElement(xe,{onClick:e},u.createElement(w,{size:24}))),Fe=({isu:e})=>{const[t,a]=m.exports.useState(!1),n=m.exports.useCallback((async t=>{a(!0);const n=await Z.getIsuGraphs(e.jia_isu_uuid,t);return a(!1),n}),[e.jia_isu_uuid]),{graphs:l,transitionData:s,sittingData:r,timeCategories:i,day:c,tooltipData:o,fetchGraphs:d,prev:p,next:E}=Ge(n);return 0===l.length?u.createElement(ne,null):u.createElement("div",{className:"flex flex-col gap-12"},u.createElement("div",{className:"flex justify-center w-full"},u.createElement(Ae,{prev:p,next:E,day:c,fetchGraphs:d})),u.createElement("div",{className:"relative flex flex-col gap-8"},u.createElement("div",{className:"z-10"},u.createElement(qe,{transitionData:s,timeCategories:i,tooltipData:o})),u.createElement("div",{className:"absolute top-0 w-full"},u.createElement(Ue,{sittingData:r,timeCategories:i})),t?u.createElement(ke,null):null))},Ve=({isu:e})=>e?u.createElement("div",{className:"flex flex-col gap-10 items-center"},u.createElement(Y,null,u.createElement(Fe,{isu:e}))):u.createElement(ne,null),Be=()=>{const[e,t]=m.exports.useState(null),{id:a}=j(),[n,l]=m.exports.useState(!1);return m.exports.useEffect((()=>{(async()=>{try{t(await Z.getIsu(a))}catch(e){404===e.response.status&&l(!0)}})()}),[a,l]),e?u.createElement("div",null,u.createElement(fe,{isu:e}),u.createElement("div",{className:"p-10"},u.createElement(Re,{isu:e}))):n?u.createElement(h,{to:"/"}):u.createElement(ne,null)},Re=({isu:e})=>u.createElement(k,null,u.createElement(ge,{path:"/isu/:id",exact:!0},u.createElement(Pe,{isu:e})),u.createElement(ge,{path:"/isu/:id/condition",exact:!0},u.createElement(Ie,{isu:e})),u.createElement(ge,{path:"/isu/:id/graph",exact:!0},u.createElement(Ve,{isu:e}))),Ze=({putIsuIcon:e})=>{const{startSelect:t,destroy:a}=(e=>{const t=document.createElement("input");t.type="file",t.accept="image/jpeg";const a=()=>{t.files&&t.files[0]&&e(t.files[0])};return t.addEventListener("change",a),{startSelect:()=>{t.click()},destroy:()=>{t.removeEventListener("change",a)}}})(e);return m.exports.useEffect((()=>a)),u.createElement("button",{className:"px-3 py-1 border border-outline rounded",onClick:t},"画像をアップロード")},Je=()=>{const[e,t]=m.exports.useState(""),[a,n]=m.exports.useState(""),[l,s]=m.exports.useState(null),r=$();return u.createElement("div",{className:"flex justify-center p-10"},u.createElement("div",{className:"flex justify-center w-full max-w-2xl"},u.createElement(Y,null,u.createElement("div",{className:"w-full"},u.createElement("h2",{className:"mb-8 text-xl font-bold"},"ISUを登録"),u.createElement("div",{className:"flex flex-col gap-4"},u.createElement(_e,{label:"JIAのIsuID",value:e,setValue:t}),u.createElement(_e,{label:"ISUの名前",value:a,setValue:n}),u.createElement("div",{className:"flex flex-col gap-8 items-center mt-6"},u.createElement(Ze,{putIsuIcon:s}),u.createElement("button",{onClick:async()=>{try{const t={jia_isu_uuid:e,isu_name:a};l&&(t.image=l),await Z.postIsu(t),r.push(`/isu/${e}`)}catch(t){409===t.response.status?r.push(`/isu/${e}`):alert(t.response.data)}},className:"px-3 py-1 w-20 h-8 text-white-primary font-bold bg-button rounded-2xl focus:outline-none"},"登録")))))))},He=()=>u.createElement("div",{className:"flex flex-col min-h-full text-primary"},u.createElement(I,null,u.createElement(X,null),u.createElement("div",{className:"flex-1 bg-primary"},u.createElement(k,null,u.createElement(ge,{path:"/",exact:!0},u.createElement(me,null)),u.createElement(ge,{path:"/isu/:id"},u.createElement(Be,null)),u.createElement(ge,{path:"/register",exact:!0},u.createElement(Je,null)),u.createElement(y,null,u.createElement(h,{to:"/"}))))));O.render(u.createElement(m.exports.StrictMode,null,u.createElement(U,null,u.createElement(He,null))),document.getElementById("root")),p.setAppElement("#root");
diff --git a/webapp/public/assets/index.ee7b4942.css b/webapp/public/assets/index.ee7b4942.css
new file mode 100644
index 000000000..33d437f68
--- /dev/null
+++ b/webapp/public/assets/index.ee7b4942.css
@@ -0,0 +1,1 @@
+#root,body,html{margin:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;height:100%;min-width:100%}code{font-family:source-code-pro,Menlo,Monaco,Consolas,'Courier New',monospace}a,a:active,a:visited{text-decoration:none;color:inherit}input{padding:0;border:none;border-radius:0;outline:0;background:0 0}*,::after,::before{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}*{--tw-ring-inset:var(--tw-empty, );/*!*//*!*/--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59, 130, 246, 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000}:root{-moz-tab-size:4;-o-tab-size:4;tab-size:4}:-moz-focusring{outline:1px dotted ButtonText}:-moz-ui-invalid{box-shadow:none}::moz-focus-inner{border-style:none;padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}body{margin:0;font-family:inherit;line-height:inherit}button{text-transform:none;background-color:transparent;background-image:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}[role=button],button{cursor:pointer}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";line-height:1.5}h2,h3{margin:0;font-size:inherit;font-weight:inherit}button,input{font-family:inherit;font-size:100%;line-height:1.15;margin:0;padding:0;line-height:inherit;color:inherit}img{border-style:solid;display:block;vertical-align:middle;max-width:100%;height:auto}input::placeholder{opacity:1;color:#9ca3af}input::webkit-input-placeholder{opacity:1;color:#9ca3af}input::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder{opacity:1;color:#9ca3af}input::-ms-input-placeholder{opacity:1;color:#9ca3af}.bg-primary{--tw-bg-opacity:1;background-color:rgba(243,244,245,var(--tw-bg-opacity))}.bg-teritary{--tw-bg-opacity:1;background-color:rgba(241,241,241,var(--tw-bg-opacity))}.bg-button{--tw-bg-opacity:1;background-color:rgba(33,57,75,var(--tw-bg-opacity))}.bg-status-info{--tw-bg-opacity:1;background-color:rgba(148,239,188,var(--tw-bg-opacity))}.bg-status-warning{--tw-bg-opacity:1;background-color:rgba(255,239,91,var(--tw-bg-opacity))}.bg-status-critical{--tw-bg-opacity:1;background-color:rgba(246,152,152,var(--tw-bg-opacity))}.bg-secondary{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-accent-primary{--tw-bg-opacity:1;background-color:rgba(255,100,51,var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgba(107,114,128,var(--tw-bg-opacity))}.bg-status-sitting{--tw-bg-opacity:1;background-color:rgba(255,188,126,var(--tw-bg-opacity))}.bg-opacity-25{--tw-bg-opacity:0.25}.border-outline{--tw-border-opacity:1;border-color:rgba(225,229,230,var(--tw-border-opacity))}.border-accent-primary{--tw-border-opacity:1;border-color:rgba(255,100,51,var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.rounded{border-radius:.25rem}.rounded-3xl{border-radius:1.5rem}.rounded-md{border-radius:.375rem}.rounded-2xl{border-radius:1rem}.border-solid{border-style:solid}.border{border-width:1px}.border-2{border-width:2px}.border-b-0{border-bottom-width:0}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-1{border-left-width:1px}.cursor-pointer{cursor:pointer}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.inline-block{display:inline-block}.flex{display:-webkit-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.grid{display:-ms-grid;display:grid}.flex-row{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;-webkit-flex-direction:row;flex-direction:row}.flex-col{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;-webkit-flex-direction:column;flex-direction:column}.flex-wrap{-ms-flex-wrap:wrap;-webkit-flex-wrap:wrap;flex-wrap:wrap}.items-center{-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.justify-items-center{justify-items:center}.justify-center{-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center}.justify-between{-webkit-box-pack:justify;-ms-flex-pack:justify;-webkit-justify-content:space-between;justify-content:space-between}.flex-1{-webkit-box-flex:1;-ms-flex:1 1 0%;-webkit-flex:1 1 0%;flex:1 1 0%}.font-bold{font-weight:700}.font-medium{font-weight:500}.h-12{height:3rem}.h-4{height:1rem}.h-full{height:100%}.h-48{height:12rem}.h-11{height:2.75rem}.h-8{height:2rem}.h-6{height:1.5rem}.h-18{height:4.5rem}.h-35{height:8.75rem}.h-100{height:25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-2xl{font-size:1.5rem;line-height:2rem}.leading-4{line-height:1rem}.m-2{margin:.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mr-auto{margin-right:auto}.mt-4{margin-top:1rem}.mt-16{margin-top:4rem}.mt-10{margin-top:2.5rem}.mb-6{margin-bottom:1.5rem}.mt-8{margin-top:2rem}.mb-3{margin-bottom:.75rem}.ml-6{margin-left:1.5rem}.mr-4{margin-right:1rem}.ml-1{margin-left:.25rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mb-8{margin-bottom:2rem}.mt-6{margin-top:1.5rem}.max-w-sm{max-width:24rem}.max-w-4xl{max-width:56rem}.max-w-2xl{max-width:42rem}.min-h-full{min-height:100%}.object-contain{-o-object-fit:contain;object-fit:contain}.disabled\:opacity-25:disabled{opacity:.25}.opacity-80{opacity:.8}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.p-4{padding:1rem}.p-2{padding:.5rem}.p-10{padding:2.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.px-16{padding-left:4rem;padding-right:4rem}.py-12{padding-top:3rem;padding-bottom:3rem}.px-4{padding-left:1rem;padding-right:1rem}.pb-0{padding-bottom:0}.pl-12{padding-left:3rem}.pt-8{padding-top:2rem}.pl-8{padding-left:2rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-8{padding-right:2rem}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.top-0{top:0}.top-18{top:4.5rem}.right-0{right:0}.text-center{text-align:center}.text-primary{--tw-text-opacity:1;color:rgba(36,30,18,var(--tw-text-opacity))}.text-secondary{--tw-text-opacity:1;color:rgba(107,105,101,var(--tw-text-opacity))}.text-teritary{--tw-text-opacity:1;color:rgba(204,204,204,var(--tw-text-opacity))}.text-white-primary{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-accent-primary{--tw-text-opacity:1;color:rgba(255,100,51,var(--tw-text-opacity))}.text-status-info{--tw-text-opacity:1;color:rgba(34,98,62,var(--tw-text-opacity))}.text-status-warning{--tw-text-opacity:1;color:rgba(96,89,16,var(--tw-text-opacity))}.text-status-sitting{--tw-text-opacity:1;color:rgba(96,58,24,var(--tw-text-opacity))}.text-status-critical{--tw-text-opacity:1;color:rgba(81,36,36,var(--tw-text-opacity))}.content-type{content:"type"}.align-middle{vertical-align:middle}.w-24{width:6rem}.w-full{width:100%}.w-40{width:10rem}.w-20{width:5rem}.w-48{width:12rem}.w-52{width:13rem}.w-4{width:1rem}.w-25{width:6.25rem}.z-10{z-index:10}.gap-4{grid-gap:1rem;gap:1rem}.gap-8{grid-gap:2rem;gap:2rem}.gap-2{grid-gap:.5rem;gap:.5rem}.gap-6{grid-gap:1.5rem;gap:1.5rem}.gap-12{grid-gap:3rem;gap:3rem}.gap-16{grid-gap:4rem;gap:4rem}.gap-10{grid-gap:2.5rem;gap:2.5rem}.grid-cols-trend{grid-template-columns:10rem 1fr}.grid-cols-isus{grid-template-columns:repeat(auto-fill,minmax(10rem,1fr))}@keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@-webkit-keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@-webkit-keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@-webkit-keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@-webkit-keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@-webkit-keyframes scale{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(.4);transform:scaley(.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}.animate-loader0{-webkit-animation:scale 1s infinite cubic-bezier(.2,.68,.18,1.08);animation:scale 1s infinite cubic-bezier(.2,.68,.18,1.08)}.animate-loader1{-webkit-animation:scale 1s .1s infinite cubic-bezier(.2,.68,.18,1.08);animation:scale 1s .1s infinite cubic-bezier(.2,.68,.18,1.08)}.animate-loader2{-webkit-animation:scale 1s .2s infinite cubic-bezier(.2,.68,.18,1.08);animation:scale 1s .2s infinite cubic-bezier(.2,.68,.18,1.08)}.animate-loader3{-webkit-animation:scale 1s .3s infinite cubic-bezier(.2,.68,.18,1.08);animation:scale 1s .3s infinite cubic-bezier(.2,.68,.18,1.08)}.animate-loader4{-webkit-animation:scale 1s .4s infinite cubic-bezier(.2,.68,.18,1.08);animation:scale 1s .4s infinite cubic-bezier(.2,.68,.18,1.08)}
\ No newline at end of file
diff --git a/webapp/public/assets/index.f7314141.js b/webapp/public/assets/index.f7314141.js
deleted file mode 100644
index 963649c88..000000000
--- a/webapp/public/assets/index.f7314141.js
+++ /dev/null
@@ -1,1 +0,0 @@
-var e=Object.defineProperty,t=Object.defineProperties,a=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,r=(t,a,n)=>a in t?e(t,a,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[a]=n,i=(e,t)=>{for(var a in t||(t={}))l.call(t,a)&&r(e,a,t[a]);if(n)for(var a of n(t))s.call(t,a)&&r(e,a,t[a]);return e},c=(e,n)=>t(e,a(n)),o=(e,t)=>{var a={};for(var r in e)l.call(e,r)&&t.indexOf(r)<0&&(a[r]=e[r]);if(null!=e&&n)for(var r of n(e))t.indexOf(r)<0&&s.call(e,r)&&(a[r]=e[r]);return a};import{r as m,R as u,a as d,M as p,L as E,I as f,T as g,b as x,u as v,c as h,d as y,e as b,f as w,g as N,F as _,G as C,_ as D,h as S,i as j,S as k,j as $,B as I,k as O}from"./vendor.e20fa6ba.js";const T={me:null},P=m.exports.createContext(T),q=m.exports.createContext(null),z=(e,t)=>{switch(t.type){case"login":return c(i({},e),{me:t.user});case"logout":return c(i({},e),{me:null});default:throw new Error("invalid action")}},U=({children:e})=>{const[t,a]=m.exports.useReducer(z,T);return u.createElement(P.Provider,{value:t},u.createElement(q.Provider,{value:a},e))},G=()=>{const e=m.exports.useContext(P);if(!e)throw new Error("require wrapped by stateContext");return e},L=()=>{const e=m.exports.useContext(q);if(!e)throw new Error("require wrapped by stateContext");return e},M=({children:e})=>u.createElement("div",{className:"text-white-primary font-bold"},e),A=()=>new Date,F=e=>Math.floor(e.getTime()/1e3),V=e=>new Date(1e3*e),B=e=>`${e.getFullYear()}/${R(e.getMonth()+1)}/${R(e.getDate())} ${R(e.getHours())}:${R(e.getMinutes())}:${R(e.getSeconds())}`,R=e=>("0"+e).slice(-2);const Z=new class{async postAuth(e,t){await d.post("/api/auth",{},i({headers:{Authorization:`Bearer ${e}`}},t))}async postSignout(e){await d.post("/api/signout",e)}async getUserMe(e){const{data:t}=await d.get("/api/user/me",e);return t}async getIsus(e,t){const{data:a}=await d.get("/api/isu",i({params:e},t)),n=[];for(const l of a)n.push(c(i({},l),{latest_isu_condition:l.latest_isu_condition?c(i({},l.latest_isu_condition),{date:V(l.latest_isu_condition.timestamp)}):null}));return n}async postIsu(e,t){const a=new FormData;a.append("jia_isu_uuid",e.jia_isu_uuid),a.append("isu_name",e.isu_name),e.image&&a.append("image",e.image,e.image.name),await d.post("/api/isu",a,i({headers:{"content-type":"multipart/form-data"}},t))}async getIsu(e,t){const{data:a}=await d.get(`/api/isu/${e}`,t);return a}async deleteIsu(e,t){await d.delete(`/api/isu/${e}`,t)}async getIsuGraphs(e,t,a){const n={datetime:F(t.date)},{data:l}=await d.get(`/api/isu/${e}/graph`,i({params:n},a)),s=[];return l.forEach((e=>{s.push(c(i({},e),{start_at:V(e.start_at),end_at:V(e.end_at)}))})),s}async getIsuConditions(e,t,a){const n=c(i({},t),{start_time:t.start_time?F(t.start_time):void 0,end_time:F(t.end_time)}),{data:l}=await d.get(`/api/condition/${e}`,i({params:n},a)),s=[];return l.forEach((e=>{s.push(c(i({},e),{date:V(e.timestamp)}))})),s}async getTrend(e){const{data:t}=await d.get("/api/trend",i({},e)),a=[];return t.forEach((e=>{const t=[],n=[],l=[];e.info.forEach((e=>{t.push(c(i({},e),{date:V(e.timestamp)}))})),e.warning.forEach((e=>{n.push(c(i({},e),{date:V(e.timestamp)}))})),e.critical.forEach((e=>{l.push(c(i({},e),{date:V(e.timestamp)}))})),a.push(c(i({},e),{info:t,warning:n,critical:l}))})),a}},J=e=>{const t=L();return u.createElement(p,{isOpen:e.isOpen,onRequestClose:e.toggle,className:"top-18 bg-gray-50 absolute right-0 flex justify-items-center px-6 py-3 border border-outline rounded",overlayClassName:"fixed inset-0",shouldCloseOnOverlayClick:!0},u.createElement(E,{to:"/",onClick:async()=>{await Z.postSignout(),t({type:"logout"})},className:"flex items-center text-primary"},u.createElement(f,{className:"mr-1",size:16}),u.createElement("div",null,"logout")))},H=e=>u.createElement(M,null,u.createElement(E,{to:e.to,className:"flex items-center"},e.icon,u.createElement("div",{className:"ml-1"},e.label))),K=()=>{const[e,t]=m.exports.useState(!1),a=()=>{t(!e)},n=G().me;return n?u.createElement("div",{className:"w-52 flex items-center justify-between ml-auto"},u.createElement(H,{to:"/register",label:"ISUの登録",icon:u.createElement(g,null)}),u.createElement("div",{className:"border-l-1 pl-4 border-white"},u.createElement(M,null,u.createElement("div",{className:"flex items-center cursor-pointer",onClick:a},u.createElement(x,null),u.createElement("div",{className:"ml-1"},n.jia_user_id)))),u.createElement(J,{isOpen:e,toggle:a})):u.createElement(u.Fragment,null)};const X=()=>u.createElement("header",{className:"h-18 flex items-center pl-6 pr-8 bg-accent-primary"},u.createElement(E,{to:"/"},u.createElement("img",{src:"/assets/logo_white.98008342.svg",alt:"isucondition",className:"h-11 cursor-pointer"})),u.createElement(K,null)),Y=({children:e})=>u.createElement("div",{className:"px-16 py-12 w-full max-w-4xl bg-secondary border border-outline rounded"},e);const Q=()=>u.createElement(Y,null,u.createElement("div",{className:"flex flex-col items-center w-full"},u.createElement("img",{src:"/assets/logo_orange.974bf3e6.svg",alt:"isucondition",className:"my-2 max-w-sm"}),u.createElement("div",{className:"mt-4 text-lg"},"ISUとつくる新しい明日"),u.createElement("div",{className:"mt-16 w-full border-b border-outline"}),u.createElement("button",{className:"mt-10 px-5 py-2 h-12 text-white-primary font-bold bg-button rounded-3xl",onClick:async()=>{location.href=`${location.protocol}//${document.domain}:5000?callback=${location.protocol}//${location.host}`}},"JIAのアカウントでログイン"))),W=({isu:e})=>u.createElement(E,{to:`/isu/${e.jia_isu_uuid}`,className:"flex flex-col items-center"},u.createElement("img",{src:`/api/isu/${e.jia_isu_uuid}/icon`,className:"w-48 h-48 object-contain",key:e.jia_isu_uuid}),u.createElement("h3",null,e.name)),ee=({variant:e,label:t})=>{const a=`h-6 rounded-2xl px-4 font-medium text-center ${(()=>{switch(e){case"info":return"bg-status-info text-status-info";case"warning":return"bg-status-warning text-status-warning";case"sitting":return"bg-status-sitting text-status-sitting";case"critical":return"bg-status-critical text-status-critical"}})()}`;return u.createElement("div",{className:a},null!=t?t:e)},te=({isus:e})=>u.createElement("div",{className:"grid gap-8 grid-cols-isus w-full"},e.map((e=>u.createElement("div",{key:e.jia_isu_uuid,className:"flex flex-col items-center"},u.createElement(W,{isu:e}),e.latest_isu_condition?u.createElement("div",null,B(e.latest_isu_condition.date)):null,e.latest_isu_condition?u.createElement(ee,{variant:e.latest_isu_condition.condition_level}):null)))),ae=()=>{const[e,t]=m.exports.useState([]);return m.exports.useEffect((()=>{(async()=>{t(await Z.getIsus({limit:4}))})()}),[t]),u.createElement("div",null,u.createElement("h2",{className:"mb-6 text-xl font-bold"},"ISU"),u.createElement(te,{isus:e}),u.createElement("div",{className:"flex gap-12 items-center justify-center mt-8"}))},ne=()=>u.createElement("div",null,"now loading"),le=(e,t)=>`${Math.round(e/t*100)}%`,se=({trend:e,maxConditionCount:t})=>u.createElement("div",{className:"flex items-center w-full h-4"},u.createElement("div",{className:"h-full bg-status-info",style:{width:le(e.info.length,t)}}),u.createElement("div",{className:"h-full bg-status-warning",style:{width:le(e.warning.length,t)}}),u.createElement("div",{className:"h-full bg-status-critical",style:{width:le(e.critical.length,t)}})),re=({trend:e,maxConditionCount:t})=>u.createElement("div",{className:"grid grid-cols-trend p-2"},u.createElement("div",{className:"flex flex-col"},u.createElement("div",null,e.character),u.createElement("div",{className:"text-secondary"},(e=>{let t=null;for(const a of[e.info,e.warning,e.critical].flat())(!t||t<a.date)&&(t=a.date);return t?B(t):"no data"})(e))),u.createElement("div",{className:"flex items-center"},u.createElement(se,{trend:e,maxConditionCount:t}))),ie=()=>u.createElement("div",{className:"grid grid-cols-trend p-2"},u.createElement("div",{className:"flex justify-center"},u.createElement("div",null,"せいかく")),u.createElement("div",{className:"flex gap-4 items-center justify-center"},u.createElement(ee,{variant:"info",label:"バッチリ"}),u.createElement(ee,{variant:"warning",label:"ぼちぼち"}),u.createElement(ee,{variant:"critical",label:"こわれた"}))),ce=e=>e.info.length+e.warning.length+e.critical.length,oe=()=>{const[e,t]=m.exports.useState([]),[a,n]=m.exports.useState(0);return m.exports.useEffect((()=>{(async()=>{const e=await Z.getTrend();e.sort(((e,t)=>ce(t)-ce(e))),t(e);let a=0;e.forEach((e=>{const t=ce(e);t>a&&(a=t)})),n(a)})()}),[]),0===e.length?u.createElement(ne,null):u.createElement("div",null,u.createElement("h2",{className:"mb-6 text-xl font-bold"},"みんなのISU"),u.createElement(ie,null),e.map((e=>u.createElement(re,{key:e.character,trend:e,maxConditionCount:a}))))},me=()=>G().me?u.createElement(de,null):u.createElement(ue,null),ue=()=>u.createElement("div",{className:"flex flex-col gap-10 items-center p-10"},u.createElement(Q,null),u.createElement(Y,null,u.createElement(oe,null))),de=()=>u.createElement("div",{className:"flex flex-col gap-10 items-center p-10"},u.createElement(Y,null,u.createElement(ae,null))),pe=({to:e,label:t})=>{const{pathname:a}=v(),n=a===e;return u.createElement("div",{className:"w-20 flex justify-center "+(n?"border-b-2 text-accent-primary border-accent-primary font-bold":"text-secondary")},u.createElement(E,{to:e},t))},Ee=({id:e})=>u.createElement("div",{className:"flex"},u.createElement(pe,{to:`/isu/${e}`,label:"詳細"}),u.createElement(pe,{to:`/isu/${e}/condition`,label:"状態"}),u.createElement(pe,{to:`/isu/${e}/graph`,label:"グラフ"})),fe=({isu:e})=>u.createElement("header",{className:"pb-0 pl-12 pt-8 bg-secondary"},u.createElement("h2",{className:"mb-3 text-2xl font-bold"},e.name),u.createElement("div",{className:"ml-6"},u.createElement(Ee,{id:e.jia_isu_uuid}))),ge=e=>{const{isTryLogin:t,login:a,state:n}=(()=>{const e=G(),t=L(),[a,n]=m.exports.useState(!0),l=m.exports.useCallback((e=>{t({type:"login",user:e}),n(!1)}),[t]);return{isTryLogin:a,login:m.exports.useCallback((async t=>{if(e.me)n(!1);else try{n(!0);try{const e=await Z.getUserMe({cancelToken:t});l(e)}catch{const e=new URL(location.href),a=e.searchParams.get("jwt");if(!a)return void n(!1);e.searchParams.delete("jwt"),history.replaceState(null,"",e.href),await Z.postAuth(a,{cancelToken:t});const s=await Z.getUserMe({cancelToken:t});l(s)}}catch{n(!1)}}),[e.me,l]),state:e}})();return m.exports.useEffect((()=>{const e=d.CancelToken.source();return a(e.token),()=>e.cancel()}),[a]),t?u.createElement(ne,null):n.me?u.createElement(y,i({},e)):"/login"===e.path?u.createElement(h,{to:"/login"}):"/"===e.path?u.createElement(y,i({},e)):u.createElement(h,{to:"/"})},xe=e=>{var t=e,{children:a,onClick:n}=t,l=o(t,["children","onClick"]);return u.createElement("button",i({className:"flex items-center focus:outline-none disabled:opacity-25 disabled:cursor-not-allowed",onClick:n},l),a)},ve=({conditions:e,next:t,prev:a,page:n})=>{const l=20===e.length,s=n>1;return u.createElement("div",{className:"center flex gap-8"},u.createElement(xe,{disabled:!s,onClick:a},u.createElement(b,{size:24})),u.createElement("div",{className:"align-middle text-xl"},n),u.createElement(xe,{disabled:!l,onClick:t},u.createElement(w,{size:24})))},he=({name:e,status:t})=>{const a=(()=>{switch(e){case"is_dirty":return u.createElement(C,{size:20});case"is_overweight":return u.createElement(_,{size:20});case"is_broken":return u.createElement(N,{size:20})}})(),n=`${(()=>{switch(t){case!0:return"text-primary";case!1:return"text-teritary"}})()}`;return u.createElement("div",{className:n},a)},ye=({conditionCSV:e})=>{const t=e.split(","),a=[];return t.forEach((e=>{const t=e.split("=");if(2!==t.length)return;const n="true"===t[1];a.push([t[0],n])})),u.createElement("div",{className:"flex"},a.map((e=>u.createElement("div",{key:e[0],className:"px-1"},u.createElement(he,{name:e[0],status:e[1]})))))},be=({condition:e})=>u.createElement("div",{className:"flex flex-wrap gap-4 items-center p-4"},u.createElement("div",{className:"mr-auto"},u.createElement("div",null,e.isu_name),u.createElement("div",{className:"text-secondary"},e.message)),u.createElement("div",null,u.createElement(ye,{conditionCSV:e.condition})),u.createElement("div",{className:"flex justify-center w-24"},e.is_sitting?u.createElement(ee,{variant:"sitting"}):null),u.createElement("div",{className:"flex justify-center w-24"},u.createElement(ee,{variant:e.condition_level})),u.createElement("div",null,u.createElement("div",null,B(e.date)))),we=({conditions:e})=>u.createElement("div",{className:"flex flex-col gap-4 items-center w-full"},u.createElement("div",{className:"w-full border border-b-0 border-outline"},e.map(((e,t)=>u.createElement("div",{className:"border-b border-outline",key:t},u.createElement(be,{condition:e})))))),Ne=e=>{var t=e,{label:a,classname:n}=t,l=o(t,["label","classname"]);return u.createElement("button",i({className:"px-3 py-1 h-8 leading-4 border border-outline rounded focus:outline-none "+n},l),a)},_e=({label:e,value:t,setValue:a,classname:n,inputProps:l})=>u.createElement("label",{className:"flex flex-col "+n},e,u.createElement("input",c(i({},l),{className:"px-2 py-1 bg-teritary border border-solid border-outline rounded",value:t,onChange:e=>a(e.target.value)}))),Ce=({times:e,setTimes:t})=>u.createElement("label",{className:"flex flex-col"},"時間指定",u.createElement("div",{className:"flex items-center"},u.createElement("input",{className:"px-2 py-1 w-40 bg-teritary border-2 border-outline rounded",value:e[0],onChange:a=>t([a.target.value,e[1]]),placeholder:"2020/01/01 11:11:11"}),u.createElement("div",{className:"text-xl"},"~"),u.createElement("input",{className:"px-2 py-1 w-40 bg-teritary border-2 border-outline rounded",value:e[1],onChange:a=>t([e[0],a.target.value]),placeholder:"2020/01/01 11:11:11"}))),De=({query:e,times:t,search:a})=>{const[n,l]=m.exports.useState(e),[s,r]=m.exports.useState(t);return u.createElement("div",{className:"flex flex-wrap gap-6 items-center"},u.createElement(_e,{label:"検索条件",value:n,setValue:l,classname:"flex-1"}),u.createElement(Ce,{times:s,setTimes:r}),u.createElement(Ne,{label:"検索",onClick:()=>a({times:s,query:n})}))},Se=e=>{const t=e.split(",");for(const a of t)if(!["critical","warning","info"].includes(a))return alert("検索条件には critical,warning,info のいずれか一つ以上をカンマ区切りで入力してください"),!1;return!0},je=e=>{const t=new Date(e);return!isNaN(t.getTime())&&t},ke=({isu:e})=>{const t=m.exports.useCallback((t=>Z.getIsuConditions(e.jia_isu_uuid,t)),[e]),{conditions:a,query:n,times:l,search:s,next:r,prev:i,page:c}=(e=>{const[t,a]=m.exports.useState([]);m.exports.useEffect((()=>{(async()=>{a(await e({end_time:A(),condition_level:"critical,warning,info"}))})()}),[e,a]);const[n,l]=m.exports.useState("critical,warning,info"),[s,r]=m.exports.useState(["",""]),[i,c]=m.exports.useState([[]]),[o,u]=m.exports.useState(1),d=()=>{const e=s[0]?new Date(s[0]+"Z"):new Date(0);return{end_time:s[1]?new Date(s[1]+"Z"):t[19].date,start_time:e,condition_level:n}};return{conditions:t,query:n,times:s,search:async t=>{if(t.query&&!Se(t.query))return;let n,s;if(t.times[0]){const e=je(t.times[0]+"Z");if(!e)return void alert("時間指定の下限が不正です");n=e}else n=new Date(0);if(t.times[1]){const e=je(t.times[1]+"Z");if(!e)return void alert("時間指定の上限が不正です");s=e}else s=A();l(t.query),r(t.times);const i={start_time:n,end_time:s,condition_level:t.query};a(await e(i)),u(1),c([[]])},page:o,next:async()=>{i[o]||(i[o]=t,c(i));const n=d();n&&(a(await e(n)),u(o+1))},prev:async()=>{a(i[o-1]),u(o-1)}}})(t);return u.createElement("div",{className:"flex flex-col gap-2"},u.createElement(De,{query:n,times:l,search:s}),u.createElement("div",{className:"flex flex-col gap-4 items-center"},u.createElement(we,{conditions:a}),u.createElement(ve,{conditions:a,page:c,next:r,prev:i})))},$e=({isu:e})=>e?u.createElement("div",{className:"flex flex-col gap-10 items-center"},u.createElement(Y,null,u.createElement(ke,{isu:e}))):u.createElement(ne,null),Ie=({isu:e,reloadKey:t})=>u.createElement("img",{src:`/api/isu/${e.jia_isu_uuid}/icon`,className:"w-48 h-48 object-contain",key:t}),Oe=({isu:e})=>u.createElement("div",{className:"flex flex-wrap gap-16 justify-center"},u.createElement(Ie,{isu:e}),u.createElement("div",{className:"flex flex-col min-h-full"},u.createElement("div",{className:"text-xl font-bold"},e.name),u.createElement("div",{className:"flex flex-1 mt-4 pl-8"},u.createElement("div",{className:"mr-4"},"せいかく"),u.createElement("div",{className:"flex-1"},e.character)))),Te=({isu:e})=>e?u.createElement("div",{className:"flex flex-col gap-10 items-center"},u.createElement(Y,null,u.createElement(Oe,{isu:e}))):u.createElement(ne,null),Pe=({transitionData:e,timeCategories:t,tooltipData:a})=>{const n={chart:{toolbar:{show:!1}},grid:{yaxis:{lines:{show:!1}}},colors:["#008FFB"],series:[{type:"line",data:e}],xaxis:{categories:t},yaxis:{min:0},tooltip:{custom:({dataPointIndex:e})=>qe(a[e])}};return u.createElement("div",null,u.createElement(D,{type:"line",options:n,series:n.series}))},qe=e=>`\n  <div class="flex flex-col px-3 py-1 text-primary">\n    <div class="flex flex-row">\n      <div class="w-25">score</div>\n      <div>${e.score}</div>\n    </div>\n    <div class="flex flex-row">\n      <div class="w-25">is_dirty</div>\n      <div>${e.is_dirty}</div>\n    </div>\n    <div class="flex flex-row">\n      <div class="w-25">is_overweight</div>\n      <div>${e.is_overweight}</div>\n    </div>\n    <div class="flex flex-row">\n      <div class="w-25">is_broken</div>\n      <div>${e.is_broken}</div>\n    </div>\n  </div>`,ze=({sittingData:e,timeCategories:t})=>{const a={chart:{toolbar:{show:!1}},colors:["#ff6433"],series:[{type:"heatmap",data:e,name:""}],xaxis:{categories:t,labels:{show:!1}},plotOptions:{heatmap:{colorScale:{ranges:[{from:0,to:20,color:"#d1d1d1"}]}}}};return u.createElement("div",{style:{transform:"translateX(13px) translateY(-32px) scaleX(0.993)"}},u.createElement(D,{type:"heatmap",options:a,series:a.series}))},Ue=e=>{const[t,a]=m.exports.useState({graphs:[],transitionData:[],sittingData:[],timeCategories:[],day:"",tooltipData:[]}),[n,l]=m.exports.useState((()=>{const e=A();return e.setHours(0,0,0,0),e})());m.exports.useEffect((()=>{(async()=>{const t=await e({date:n}),l=Ge(t);a((e=>c(i({},e),{graphs:t,transitionData:l.transitionData,sittingData:l.sittingData,timeCategories:l.timeCategories,day:n.toLocaleDateString(),tooltipData:l.tooltipData})))})()}),[e,a,n]);const s=async()=>{const t=await e({date:n}),l=Ge(t);a((e=>c(i({},e),{loading:!1,graphs:t,transitionData:l.transitionData,sittingData:l.sittingData,timeCategories:l.timeCategories,day:n.toLocaleTimeString(),tooltipData:l.tooltipData})))};return c(i({},t),{fetchGraphs:async e=>{const t=new Date(e.day);isNaN(t.getTime())?alert("日時の指定が不正です"):(l(t),s())},prev:async()=>{l((e=>new Date(e.setDate(e.getDate()-1)))(n)),s()},next:async()=>{l((e=>new Date(e.setDate(e.getDate()+1)))(n)),s()}})},Ge=e=>{const t=[],a=[],n=[],l=[];return e.forEach((e=>{e.data?(t.push(e.data.score),a.push(e.data.percentage.sitting),l.push({score:e.data.score.toString(),is_dirty:`${e.data.percentage.is_dirty}%`,is_overweight:`${e.data.percentage.is_overweight}%`,is_broken:`${e.data.percentage.is_broken}%`})):(t.push(0),a.push(0),l.push({score:"-",is_dirty:"-",is_overweight:"-",is_broken:"-"})),n.push(e.start_at.toLocaleTimeString("ja-JP",{hour:"2-digit",minute:"2-digit"}))})),{transitionData:t,sittingData:a,timeCategories:n,tooltipData:l}},Le=({day:e,fetchGraphs:t})=>{const[a,n]=m.exports.useState(e);return m.exports.useEffect((()=>{n(e)}),[e,n]),u.createElement("div",{className:"flex gap-8 w-full"},u.createElement(S,{value:a,onChange:e=>n(e.target.value),onKeyPress:e=>{"Enter"===e.key&&(e.preventDefault(),t({day:a}))}}))},Me=({next:e,prev:t,day:a,fetchGraphs:n})=>u.createElement("div",{className:"flex gap-8"},u.createElement(xe,{onClick:t},u.createElement(b,{size:24})),u.createElement(Le,{day:a,fetchGraphs:n}),u.createElement(xe,{onClick:e},u.createElement(w,{size:24}))),Ae=({isu:e})=>{const t=m.exports.useCallback((t=>Z.getIsuGraphs(e.jia_isu_uuid,t)),[e.jia_isu_uuid]),{graphs:a,transitionData:n,sittingData:l,timeCategories:s,day:r,tooltipData:i,fetchGraphs:c,prev:o,next:d}=Ue(t);return 0===a.length?u.createElement(ne,null):u.createElement("div",{className:"flex flex-col gap-12"},u.createElement("div",{className:"flex justify-center w-full"},u.createElement(Me,{prev:o,next:d,day:r,fetchGraphs:c})),u.createElement("div",{className:"relative flex flex-col gap-8"},u.createElement("div",{className:"z-10"},u.createElement(Pe,{transitionData:n,timeCategories:s,tooltipData:i})),u.createElement("div",{className:"absolute top-0 w-full"},u.createElement(ze,{sittingData:l,timeCategories:s}))))},Fe=({isu:e})=>e?u.createElement("div",{className:"flex flex-col gap-10 items-center"},u.createElement(Y,null,u.createElement(Ae,{isu:e}))):u.createElement(ne,null),Ve=()=>{const[e,t]=m.exports.useState(null),{id:a}=j(),[n,l]=m.exports.useState(!1);return m.exports.useEffect((()=>{(async()=>{try{t(await Z.getIsu(a))}catch(e){404===e.response.status&&l(!0)}})()}),[a,l]),e?u.createElement("div",null,u.createElement(fe,{isu:e}),u.createElement("div",{className:"p-10"},u.createElement(Be,{isu:e}))):n?u.createElement(h,{to:"/"}):u.createElement(ne,null)},Be=({isu:e})=>u.createElement(k,null,u.createElement(ge,{path:"/isu/:id",exact:!0},u.createElement(Te,{isu:e})),u.createElement(ge,{path:"/isu/:id/condition",exact:!0},u.createElement($e,{isu:e})),u.createElement(ge,{path:"/isu/:id/graph",exact:!0},u.createElement(Fe,{isu:e}))),Re=({putIsuIcon:e})=>{const{startSelect:t,destroy:a}=(e=>{const t=document.createElement("input");t.type="file",t.accept="image/jpeg";const a=()=>{t.files&&t.files[0]&&e(t.files[0])};return t.addEventListener("change",a),{startSelect:()=>{t.click()},destroy:()=>{t.removeEventListener("change",a)}}})(e);return m.exports.useEffect((()=>a)),u.createElement("button",{className:"px-3 py-1 border border-outline rounded",onClick:t},"画像をアップロード")},Ze=()=>{const[e,t]=m.exports.useState(""),[a,n]=m.exports.useState(""),[l,s]=m.exports.useState(null),r=$();return u.createElement("div",{className:"flex justify-center p-10"},u.createElement("div",{className:"flex justify-center w-full max-w-2xl"},u.createElement(Y,null,u.createElement("div",{className:"w-full"},u.createElement("h2",{className:"mb-8 text-xl font-bold"},"ISUを登録"),u.createElement("div",{className:"flex flex-col gap-4"},u.createElement(_e,{label:"JIAのIsuID",value:e,setValue:t}),u.createElement(_e,{label:"ISUの名前",value:a,setValue:n}),u.createElement("div",{className:"flex flex-col gap-8 items-center mt-6"},u.createElement(Re,{putIsuIcon:s}),u.createElement("button",{onClick:async()=>{try{const t={jia_isu_uuid:e,isu_name:a};l&&(t.image=l),await Z.postIsu(t),r.push(`/isu/${e}`)}catch(t){409===t.response.status?r.push(`/isu/${e}`):alert(t.response.data)}},className:"px-3 py-1 w-20 h-8 text-white-primary font-bold bg-button rounded-2xl focus:outline-none"},"登録")))))))},Je=()=>u.createElement("div",{className:"flex flex-col min-h-full text-primary"},u.createElement(I,null,u.createElement(X,null),u.createElement("div",{className:"flex-1 bg-primary"},u.createElement(k,null,u.createElement(ge,{path:"/",exact:!0},u.createElement(me,null)),u.createElement(ge,{path:"/isu/:id"},u.createElement(Ve,null)),u.createElement(ge,{path:"/register",exact:!0},u.createElement(Ze,null)),u.createElement(y,null,u.createElement(h,{to:"/"}))))));O.render(u.createElement(m.exports.StrictMode,null,u.createElement(U,null,u.createElement(Je,null))),document.getElementById("root")),p.setAppElement("#root");
diff --git a/webapp/public/assets/vendor.e20fa6ba.js b/webapp/public/assets/vendor.519e803f.js
similarity index 100%
rename from webapp/public/assets/vendor.e20fa6ba.js
rename to webapp/public/assets/vendor.519e803f.js
diff --git a/webapp/public/index.html b/webapp/public/index.html
index 3192d0272..72cfd469f 100644
--- a/webapp/public/index.html
+++ b/webapp/public/index.html
@@ -5,9 +5,9 @@
     <link rel="icon" type="image/svg+xml" href="/assets/favicon.d0f5f504.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>ISUCONDITION</title>
-  <script type="module" crossorigin src="/assets/index.f7314141.js"></script>
-  <link rel="modulepreload" href="/assets/vendor.e20fa6ba.js">
-  <link rel="stylesheet" href="/assets/index.4689f22a.css">
+  <script type="module" crossorigin src="/assets/index.b1cd63b1.js"></script>
+  <link rel="modulepreload" href="/assets/vendor.519e803f.js">
+  <link rel="stylesheet" href="/assets/index.ee7b4942.css">
 </head>
   <body>
     <div id="root"></div>


Test patch: