-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
176 additions
and
78 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
.instanceTable { | ||
display: flex; | ||
width: 100%; | ||
height: 100%; | ||
overflow: auto; | ||
scroll-snap-type: both mandatory; | ||
|
||
table { | ||
width: 2850px !important; | ||
scroll-snap-type: both mandatory !important; | ||
font-size: 16px; | ||
} | ||
|
||
> div, > div > div { | ||
height: 100%; | ||
} | ||
|
||
> div > div > div > div:nth-child(2) { | ||
position: absolute !important; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { useState, type FC } from 'react' | ||
import CreateModal from '../CreateModal' | ||
import { useSearchParams } from 'react-router-dom' | ||
import style from './style.module.scss' | ||
import { Button } from '@cloudscape-design/components' | ||
|
||
interface Props { | ||
max: number | ||
prices?: number | ||
} | ||
|
||
const TableControl: FC<Props> = ({ max, prices }) => { | ||
const [searchParams, setSearchParams] = useSearchParams() | ||
const [createModalStatus, setCreateModal] = useState(false) | ||
|
||
const page = parseInt(searchParams.get('page') ?? '0') | ||
const isPrevDisabled = page < 1 | ||
const isNextDisabled = page + 1 >= max | ||
|
||
const onPrevClick = (): void => { | ||
if (isPrevDisabled) | ||
return | ||
|
||
searchParams.set('page', (page - 1).toString()) | ||
setSearchParams(searchParams) | ||
} | ||
|
||
const onNextClick = (): void => { | ||
if (isNextDisabled) | ||
return | ||
|
||
searchParams.set('page', (page + 1).toString()) | ||
setSearchParams(searchParams) | ||
} | ||
|
||
return ( | ||
<> | ||
<div className={style.tableControl}> | ||
<h1>인스턴스</h1> | ||
<p>총 <b>{prices ?? '...'}</b>$/월</p> | ||
|
||
<div className={style.pageBtns}> | ||
<button className={style.pageBtn} disabled={isPrevDisabled} onClick={onPrevClick}> | ||
<img src="/assets/icon/prev.svg" alt="Previous" /> | ||
</button> | ||
|
||
<b>{page + 1}</b> | ||
|
||
<button className={style.pageBtn} disabled={isNextDisabled} onClick={onNextClick}> | ||
<img src="/assets/icon/next.svg" alt="Next" /> | ||
</button> | ||
</div> | ||
|
||
<Button | ||
className="orangeButton" | ||
variant="primary" | ||
onClick={() => { setCreateModal(true) }}> | ||
인스턴스 생성 | ||
</Button> | ||
</div> | ||
|
||
<CreateModal display={createModalStatus} action={setCreateModal} /> | ||
</> | ||
) | ||
} | ||
|
||
export default TableControl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
.tableControl { | ||
display: flex; | ||
align-items: center; | ||
font-size: 20px; | ||
height: 25px; | ||
gap: 20px; | ||
|
||
h1 { | ||
flex-grow: 1; | ||
} | ||
|
||
.pageBtns { | ||
display: flex; | ||
justify-content: center; | ||
|
||
.pageBtn { | ||
background-color: transparent; | ||
border: none; | ||
height: 25px; | ||
width: 25px; | ||
cursor: pointer; | ||
|
||
&:disabled { | ||
opacity: 0.5; | ||
cursor: default; | ||
} | ||
|
||
> img { | ||
width: 100%; | ||
height: 100%; | ||
object-position: center; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,59 @@ | ||
import axios from 'axios' | ||
import { type FC, useEffect, useState } from 'react' | ||
import { styled } from 'styled-components' | ||
import CreateModal from '../components/CreateModal' | ||
import Button from '../components/Button' | ||
import { Body, Title, Top } from '../styles/globals' | ||
import { Link, useLocation } from 'react-router-dom' | ||
import { useLocation } from 'react-router-dom' | ||
import Container from '../components/Container' | ||
import InstanceTable from '../components/InstanceTable' | ||
import { useRefreshNotifier } from '../components/RefreshNotifier' | ||
import TableControl from '../components/TableControl' | ||
|
||
const Instances: FC = () => { | ||
const location = useLocation() | ||
const serachParams = new URLSearchParams(location.search) | ||
const page = serachParams.get('page') ?? '0' | ||
const [prices, setPrices] = useState(0) | ||
const [prices, setPrices] = useState<number | undefined>(undefined) | ||
const [max, setMax] = useState(0) | ||
const [instances, setInstances] = useState([]) | ||
const [createModalStatus, setCreateModal] = useState(false) | ||
const [isLoading, setIsLoading] = useState(true) | ||
const { refreshToken } = useRefreshNotifier() | ||
|
||
useEffect(() => { | ||
const fetchData = async (): Promise<void> => { | ||
setIsLoading(true) | ||
axios(`/api/instances?take=10&skip=${parseInt(String(serachParams.get('page') !== null ? page : '0')) * 10}`, { | ||
|
||
const res = await axios(`/api/instances?take=10&skip=${parseInt(page) * 10}`, { | ||
method: 'GET', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
} | ||
}).then((res) => { | ||
setInstances(res.data.body.instances) | ||
setMax(res.data.body.pageCount) | ||
setIsLoading(false) | ||
}).catch((err) => { console.error(err) }) | ||
}, [serachParams.get('page')]) | ||
}) | ||
|
||
useEffect(() => { | ||
void axios('/api/prices', { | ||
const res2 = await axios('/api/prices', { | ||
method: 'GET', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
} | ||
}).then((res) => { | ||
setPrices(Math.floor((res.data.body.pricePerHour * 24) * 30) + ((Number.isNaN(res.data.body.storageSize) ? 0 : res.data.body.storageSize) * 0.1)) | ||
}) | ||
}, []) | ||
|
||
return ( | ||
<Container> | ||
<Body> | ||
<Top> | ||
<Title>인스턴스</Title> | ||
<div> | ||
<p> | ||
총 {prices}$/월 | ||
</p> | ||
<Link to={parseInt(page) + 1 > 1 ? `/instances?page=${parseInt(page) - 1}` : window.location.href}> | ||
<svg className={parseInt(page) + 1 > 1 ? 'enabled' : 'disabled'} width="28" height="28" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false" preserveAspectRatio="xMidYMid meet"><path fillRule="evenodd" clipRule="evenodd" d="M16.2071 19.7071C16.5976 19.3166 16.5976 18.6834 16.2071 18.2929L9.91421 12L16.2071 5.70711C16.5976 5.31658 16.5976 4.68342 16.2071 4.29289C15.8166 3.90237 15.1834 3.90237 14.7929 4.29289L7.79289 11.2929C7.40237 11.6834 7.40237 12.3166 7.79289 12.7071L14.7929 19.7071C15.1834 20.0976 15.8166 20.0976 16.2071 19.7071Z"></path></svg> | ||
</Link> | ||
<a>{serachParams.get('page') !== null ? parseInt(page) + 1 : 1}</a> | ||
<Link to={parseInt(page) + 1 >= max ? window.location.href : `/instances?page=${parseInt(page) + 1}`}> | ||
<svg className={parseInt(page) + 1 >= max ? 'disabled' : 'enabled'} width="28" height="28" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false" preserveAspectRatio="xMidYMid meet"><path fillRule="evenodd" clipRule="evenodd" d="M7.79289 19.7071C7.40237 19.3166 7.40237 18.6834 7.79289 18.2929L14.0858 12L7.79289 5.70711C7.40237 5.31658 7.40237 4.68342 7.79289 4.29289C8.18342 3.90237 8.81658 3.90237 9.20711 4.29289L16.2071 11.2929C16.5976 11.6834 16.5976 12.3166 16.2071 12.7071L9.20711 19.7071C8.81658 20.0976 8.18342 20.0976 7.79289 19.7071Z"></path></svg> | ||
</Link> | ||
<Button style={{ backgroundColor: '#ff9900' }} onClick={() => { setCreateModal(true) }}>인스턴스 생성</Button> | ||
</div> | ||
setInstances(res.data.body.instances) | ||
setMax(res.data.body.pageCount) | ||
setPrices(res2.data.body.pricePerHour * 24 * 30 + res2.data.body.storageSize * 0.1) | ||
|
||
</Top> | ||
setIsLoading(false) | ||
} | ||
|
||
<TableMain> | ||
<InstanceTable instances={instances} isLoading={isLoading} /> | ||
</TableMain> | ||
useEffect(() => { | ||
void fetchData() | ||
}, [serachParams.get('page')]) | ||
|
||
useEffect(() => { | ||
void fetchData() | ||
}, [refreshToken]) | ||
|
||
<CreateModal display={createModalStatus} action={setCreateModal}></CreateModal> | ||
</Body > | ||
return ( | ||
<Container> | ||
<TableControl prices={prices} max={max} /> | ||
<InstanceTable instances={instances} isLoading={isLoading} /> | ||
</Container> | ||
) | ||
} | ||
|
||
const TableMain = styled.div` | ||
display: flex; | ||
width: 100%; | ||
height: calc(100% - 45px); | ||
overflow: auto; /* Apply overflow to enable scrolling */ | ||
scroll-snap-type: both mandatory; | ||
table { | ||
width: 2850px !important; | ||
scroll-snap-type: both mandatory !important; | ||
font-size: 16px; | ||
} | ||
` | ||
|
||
export default Instances |