Improved sensors list

This commit is contained in:
Jan Zípek 2022-08-28 21:22:33 +02:00
parent 4b0220027e
commit b68efea626
Signed by: kamen
GPG Key ID: A17882625B33AC31
7 changed files with 152 additions and 71 deletions

View File

@ -1,33 +1,41 @@
.sensors-page {
.sensors-head {
display: flex;
align-items: center;
flex: 1;
.sensors-head {
display: flex;
align-items: center;
flex: 1;
> button {
margin-left: auto;
}
}
> button {
margin-left: auto;
}
}
section.content {
padding: 1rem;
> .box {
max-width: 50rem;
padding: 1rem;
overflow: auto;
}
.sensors-list {
.sensor-item {
display: flex;
align-items: center;
margin: 0.25rem 0;
display: grid;
grid-template-columns: repeat(6, 1fr);
overflow: auto;
padding: 1rem;
&.head {
opacity: 0.75;
font-size: 85%;
}
@media screen and (max-width: 1500px) {
grid-template-columns: repeat(4, 1fr);
}
@media screen and (max-width: 1200px) {
grid-template-columns: repeat(3, 1fr);
}
@media screen and (max-width: 900px) {
grid-template-columns: repeat(2, 1fr);
}
@media screen and (max-width: 768px) {
grid-template-columns: repeat(1, 1fr);
}
.sensor-item {
margin: 0.25rem;
overflow: auto;
padding: 0.5rem;
> div {
flex-grow: 0;
@ -36,30 +44,46 @@
text-overflow: ellipsis;
}
> .id {
flex: 0.3;
margin-right: 0.5rem;
}
> .auth {
.auth-value {
display: flex;
align-items: center;
font-size: 85%;
margin: 0.25rem 0;
> .name {
margin-right: 0.5rem;
flex: 1;
}
.label {
width: 2rem;
background-color: #666;
padding: 0.2rem;
border-radius: 0.25rem;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
> .key {
display: flex;
flex: 1.5;
.value {
flex: 1;
display: flex;
button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
padding: 0.25rem 0.5rem;
input {
flex: 1;
border-radius: 0;
}
button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
padding: 0rem 0.5rem;
flex-grow: 0;
flex-shrink: 0;
font-size: 120%;
}
}
}
}
> .actions {
flex: 2;
text-align: right;
margin-top: 0.5rem;
button {
margin-left: 0.25rem;
@ -69,3 +93,29 @@
}
}
}
/*
@media screen and (max-width: 768px) {
.sensors-page .sensors-list {
grid-template-columns: repeat(1, 1fr);
}
}
@media screen and (max-width: 900px) {
.sensors-page .sensors-list {
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (max-width: 1200px) {
.sensors-page .sensors-list {
grid-template-columns: repeat(3, 1fr);
}
}
@media screen and (max-width: 1980px) {
.sensors-page .sensors-list {
grid-template-columns: repeat(4, 1fr);
}
}
*/

View File

@ -0,0 +1 @@
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1 @@
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"></path></svg>

After

Width:  |  Height:  |  Size: 354 B

View File

@ -0,0 +1,36 @@
import { ClipboardCheckIcon, ClipboardCopyIcon } from '@/icons'
import { useRef, useState } from 'preact/hooks'
type Props = {
value: string
}
export const InputWithCopy = ({ value }: Props) => {
const inputRef = useRef<HTMLInputElement>(null)
const [hasCopied, setHasCopied] = useState(false)
const handleCopy = () => {
const target = inputRef.current
if (target) {
target.select()
target.setSelectionRange(0, 99999)
navigator.clipboard.writeText(target.value)
setHasCopied(true)
setTimeout(() => setHasCopied(false), 2000)
}
}
return (
<>
<input type="text" value={value} ref={inputRef} readOnly disabled />
<button className="icon" onClick={handleCopy}>
{hasCopied ? <ClipboardCheckIcon /> : <ClipboardCopyIcon />}
</button>
</>
)
}

View File

@ -7,3 +7,5 @@ export { ReactComponent as PlusIcon } from '@/assets/icons/plus.svg'
export { ReactComponent as EyeIcon } from '@/assets/icons/eye.svg'
export { ReactComponent as EyeOffIcon } from '@/assets/icons/eye-off.svg'
export { ReactComponent as EditIcon } from '@/assets/icons/edit.svg'
export { ReactComponent as ClipboardCopyIcon } from '@/assets/icons/clipboard-copy.svg'
export { ReactComponent as ClipboardCheckIcon } from '@/assets/icons/clipboard-check.svg'

View File

@ -24,18 +24,10 @@ export const SensorsPage = () => {
}
className="sensors-page"
>
<div className="box">
<div className="sensors-list">
<div className="sensor-item head">
<div className="id">ID</div>
<div className="name">Name</div>
<div className="key">Key</div>
<div className="actions"></div>
</div>
{sensors.data?.map((i) => (
<SensorItem key={i.id} sensor={i} onEdit={setEdited} />
))}
</div>
<div className="sensors-list">
{sensors.data?.map((i) => (
<SensorItem key={i.id} sensor={i} onEdit={setEdited} />
))}
</div>
{(showNew || edited) && (
<SensorFormModal

View File

@ -1,6 +1,6 @@
import { SensorInfo } from '@/api/sensors'
import { CancelIcon, EditIcon, EyeIcon, RefreshIcon } from '@/icons'
import { useState } from 'preact/hooks'
import { InputWithCopy } from '@/components/InputWithCopy'
import { CancelIcon, EditIcon } from '@/icons'
type Props = {
sensor: SensorInfo
@ -8,30 +8,29 @@ type Props = {
}
export const SensorItem = ({ sensor, onEdit }: Props) => {
const [showPassword, setShowPassword] = useState(false)
return (
<div className="sensor-item">
<div className="id">{sensor.id}</div>
<div className="box sensor-item">
<div className="name">{sensor.name}</div>
<div className="key">
<input
type={showPassword ? 'text' : 'password'}
value={sensor.authKey}
disabled
readOnly
/>
<button className="icon" onClick={() => setShowPassword((v) => !v)}>
<EyeIcon />
</button>
<div className="auth">
<div className="auth-value">
<div className="label">ID</div>
<div className="value">
<InputWithCopy value={sensor.id.toString()} />
</div>
</div>
<div className="auth-value">
<div className="label">KEY</div>
<div className="value">
<InputWithCopy value={sensor.authKey} />
</div>
</div>
</div>
<div className="actions">
<button onClick={() => onEdit(sensor)}>
<EditIcon /> Edit
</button>
<button>
<RefreshIcon /> Refresh key
</button>
<button>
<CancelIcon /> Delete
</button>