Skip to content

Commit

Permalink
style: change instace style
Browse files Browse the repository at this point in the history
  • Loading branch information
pmh-only committed Aug 20, 2023
1 parent 7f72024 commit 6f6341c
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 78 deletions.
1 change: 1 addition & 0 deletions public/assets/icon/next.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/assets/icon/prev.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion src/components/Container/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@
top: 0;
z-index: 0;

padding-top: 40px;
padding: 20px;
padding-top: 60px;

display: flex;
flex-direction: column;
gap: 20px;
}
38 changes: 19 additions & 19 deletions src/components/InstanceTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import updateModalReducer from '../../modules/ModalReducer'
import axios from 'axios'
import UpdateModal from '../UpdateModal'
import showStatus from '../../utils/showStatus'
import { useRefreshNotifier } from '../RefreshNotifier'

import style from './style.module.scss'

interface Props {
instances: InstancesType[]
isLoading: boolean
}

const InstanceTable: FC<Props> = ({ instances, isLoading }) => {
const { refresh } = useRefreshNotifier()
const [uuid, setUuid] = useState('')
const [updateModalStatus, setUpdateModal] = useState(false)
const [event, dispatch] = useReducer(updateModalReducer, {
Expand All @@ -33,7 +37,8 @@ const InstanceTable: FC<Props> = ({ instances, isLoading }) => {
method: 'DELETE'
}).then(() => { alert('인스턴스가 삭제되었습니다.') })
.catch(() => { alert('인스턴스를 삭제하는 도중 에러가 발생했습니다.') })
window.location.reload()

refresh()
}
}

Expand All @@ -43,7 +48,8 @@ const InstanceTable: FC<Props> = ({ instances, isLoading }) => {
method: 'POST'
}).then(() => { alert('인스턴스가 재시작 되었습니다.') })
.catch(() => { alert('인스턴스를 재시작하는 도중 에러가 발생했습니다.') })
window.location.reload()

refresh()
}
}

Expand All @@ -53,7 +59,8 @@ const InstanceTable: FC<Props> = ({ instances, isLoading }) => {
method: 'POST'
}).then(() => { alert('인스턴스가 초기화 되었습니다.') })
.catch(() => { alert('인스턴스를 초기화하는 도중 에러가 발생했습니다.') })
window.location.reload()

refresh()
}
}

Expand Down Expand Up @@ -98,24 +105,13 @@ const InstanceTable: FC<Props> = ({ instances, isLoading }) => {
instanceID: uuid
}
}).then((res) => {
const $textarea = document.createElement('textarea')
// body 요소에 존재해야 복사가 진행됨
document.body.appendChild($textarea)

// 복사할 특정 텍스트를 임시의 textarea에 넣어주고 모두 셀렉션 상태
$textarea.value = `${window.location.origin}/invites/${res.data.body.id as string}`
$textarea.select()

// 복사 후 textarea 지우기
document.execCommand('copy')
document.body.removeChild($textarea)

void navigator.clipboard.writeText(`${window.location.origin}/invites/${res.data.body.id as string}`)
alert('초대링크를 복사했습니다.')
})
}

return (
<>
<section className={style.instanceTable}>
<Table
variant="full-page"
columnDisplay={[
Expand Down Expand Up @@ -254,12 +250,16 @@ const InstanceTable: FC<Props> = ({ instances, isLoading }) => {

items={instances}
loading={isLoading}

loadingText="인스턴스를 불러오는 중..."
/>

<UpdateModal display={updateModalStatus} booleanAction={setUpdateModal} instance={event} instanceAction={dispatch} uuid={uuid}></UpdateModal>
</>
<UpdateModal
display={updateModalStatus}
booleanAction={setUpdateModal}
instance={event}
instanceAction={dispatch}
uuid={uuid} />
</section>
)
}

Expand Down
21 changes: 21 additions & 0 deletions src/components/InstanceTable/style.module.scss
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;
}
}
67 changes: 67 additions & 0 deletions src/components/TableControl/index.tsx
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
35 changes: 35 additions & 0 deletions src/components/TableControl/style.module.scss
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;
}
}
}
}
84 changes: 26 additions & 58 deletions src/pages/Instances.tsx
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

0 comments on commit 6f6341c

Please sign in to comment.