chore: use monorepo organization

This commit is contained in:
Rafael Taranto 2025-05-12 10:52:54 +01:00
parent deaf7d6ecc
commit a687827f7e
1099 changed files with 8184 additions and 11535 deletions

View file

@ -0,0 +1,74 @@
.titleWrapper {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
}
.wrapper {
flex: 1;
display: flex;
flex-direction: row;
height: 100%;
}
.tableWrapper {
flex: 1;
margin-left: 40px;
display: block;
overflow-x: auto;
width: 100%;
max-width: 78%;
max-height: 70vh;
}
.table {
white-space: nowrap;
display: block;
}
.table th {
position: sticky;
top: 0;
}
.dateColumn {
min-width: 160px;
}
.levelColumn {
min-width: 100px;
}
.fillColumn {
width: 100%;
}
.shareButton {
margin: 8px;
display: flex;
align-items: center;
font-size: 13px;
padding: 0 12px;
}
.shareIcon {
margin-right: 6px;
}
.button {
margin: 8px;
}
.titleAndButtonsContainer {
display: flex;
}
.buttonsWrapper {
display: flex;
margin-left: 16px;
}
.buttonsWrapper > * {
margin: auto 6px;
}

View file

@ -0,0 +1,168 @@
import { useQuery, gql } from '@apollo/client'
import * as R from 'ramda'
import React, { useState } from 'react'
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper.jsx'
import Title from 'src/components/Title.jsx'
import Sidebar from 'src/components/layout/Sidebar.jsx'
import { Info3, H4 } from 'src/components/typography/index.jsx'
import {
Table,
TableHead,
TableRow,
TableHeader,
TableBody,
TableCell
} from 'src/components/table/index.js'
import { formatDate } from 'src/utils/timezones.js'
import classes from './Logs.module.css'
const GET_MACHINES = gql`
{
machines {
name
deviceId
}
}
`
const NUM_LOG_RESULTS = 500
const GET_MACHINE_LOGS_CSV = gql`
query MachineLogs(
$deviceId: ID!
$limit: Int
$from: DateTimeISO
$until: DateTimeISO
$timezone: String
) {
machineLogsCsv(
deviceId: $deviceId
limit: $limit
from: $from
until: $until
timezone: $timezone
)
}
`
const GET_MACHINE_LOGS = gql`
query MachineLogs(
$deviceId: ID!
$limit: Int
$from: DateTimeISO
$until: DateTimeISO
) {
machineLogs(
deviceId: $deviceId
limit: $limit
from: $from
until: $until
) {
logLevel
id
timestamp
message
}
}
`
const GET_DATA = gql`
query getData {
config
}
`
const Logs = () => {
const [selected, setSelected] = useState(null)
const [saveMessage, setSaveMessage] = useState(null)
const deviceId = selected?.deviceId
const { data: machineResponse, loading: machinesLoading } =
useQuery(GET_MACHINES)
const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const { data: logsResponse, loading: logsLoading } = useQuery(
GET_MACHINE_LOGS,
{
variables: { deviceId, limit: NUM_LOG_RESULTS },
skip: !selected,
onCompleted: () => setSaveMessage('')
}
)
if (machineResponse?.machines?.length && !selected) {
setSelected(machineResponse?.machines[0])
}
const isSelected = it => {
return R.path(['deviceId'])(selected) === it.deviceId
}
const loading = machinesLoading || configLoading || logsLoading
return (
<>
<div className={classes.titleWrapper}>
<div className={classes.titleAndButtonsContainer}>
<Title>Machine logs</Title>
{logsResponse && (
<div className={classes.buttonsWrapper}>
<LogsDowloaderPopover
title="Download logs"
name={selected.name}
query={GET_MACHINE_LOGS_CSV}
args={{ deviceId, timezone }}
getLogs={logs => R.path(['machineLogsCsv'])(logs)}
timezone={timezone}
/>
<Info3>{saveMessage}</Info3>
</div>
)}
</div>
</div>
<div className={classes.wrapper}>
<Sidebar
displayName={it => it.name}
data={machineResponse?.machines || []}
isSelected={isSelected}
onClick={setSelected}
/>
<div className={classes.tableWrapper}>
<Table className={classes.table}>
<TableHead>
<TableRow header>
<TableHeader className={classes.dateColumn}>Date</TableHeader>
<TableHeader className={classes.levelColumn}>Level</TableHeader>
<TableHeader className={classes.fillColumn} />
</TableRow>
</TableHead>
<TableBody>
{logsResponse &&
logsResponse.machineLogs.map((log, idx) => (
<TableRow key={idx} size="sm">
<TableCell>
{timezone &&
formatDate(log.timestamp, timezone, 'yyyy-MM-dd HH:mm')}
</TableCell>
<TableCell>{log.logLevel}</TableCell>
<TableCell>{log.message}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{loading && <H4>{'Loading...'}</H4>}
{!loading && !logsResponse?.machineLogs?.length && (
<H4>{'No activity so far'}</H4>
)}
</div>
</div>
</>
)
}
export default Logs

View file

@ -0,0 +1,197 @@
import { useQuery, gql } from '@apollo/client'
import * as R from 'ramda'
import React, { useState, useRef } from 'react'
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper.jsx'
import Title from 'src/components/Title.jsx'
import Uptime from 'src/pages/Logs/Uptime.jsx'
import { Info3, H4 } from 'src/components/typography/index.jsx'
import { Select } from 'src/components/inputs/index.js'
import {
Table,
TableHead,
TableRow,
TableHeader,
TableBody,
TableCell
} from 'src/components/table/index.js'
import { startCase } from 'src/utils/string.js'
import { formatDate } from 'src/utils/timezones.js'
import logsClasses from './Logs.module.css'
import classes from './ServerLogs.module.css'
const SHOW_ALL = { code: 'SHOW_ALL', display: 'Show all' }
const NUM_LOG_RESULTS = 500
const GET_CSV = gql`
query ServerData(
$limit: Int
$from: DateTimeISO
$until: DateTimeISO
$timezone: String
) {
serverLogsCsv(
limit: $limit
from: $from
until: $until
timezone: $timezone
)
}
`
const GET_SERVER_DATA = gql`
query ServerData($limit: Int, $from: DateTimeISO, $until: DateTimeISO) {
serverVersion
uptime {
name
state
uptime
}
serverLogs(limit: $limit, from: $from, until: $until) {
logLevel
id
timestamp
message
}
}
`
const GET_DATA = gql`
query getData {
config
}
`
const Logs = () => {
const tableEl = useRef()
const [saveMessage, setSaveMessage] = useState(null)
const [logLevel, setLogLevel] = useState(SHOW_ALL)
const { data, loading: dataLoading } = useQuery(GET_SERVER_DATA, {
onCompleted: () => setSaveMessage(''),
variables: {
limit: NUM_LOG_RESULTS
}
})
const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const defaultLogLevels = [
{ code: 'error', display: 'Error' },
{ code: 'info', display: 'Info' },
{ code: 'debug', display: 'Debug' }
]
const serverVersion = data?.serverVersion
const processStates = data?.uptime ?? []
const getLogLevels = R.compose(
R.prepend(SHOW_ALL),
R.uniq,
R.concat(defaultLogLevels),
R.map(it => ({
code: R.path(['logLevel'])(it),
display: startCase(R.path(['logLevel'])(it))
})),
R.path(['serverLogs'])
)
const handleLogLevelChange = logLevel => {
if (tableEl.current) tableEl.current.scrollTo(0, 0)
setLogLevel(logLevel)
}
const loading = dataLoading || configLoading
return (
<>
<div className={logsClasses.titleWrapper}>
<div className={logsClasses.titleAndButtonsContainer}>
<Title>Server</Title>
{data && (
<div className={logsClasses.buttonsWrapper}>
<LogsDowloaderPopover
title="Download logs"
name="server-logs"
query={GET_CSV}
args={{ timezone }}
logs={data.serverLogs}
getLogs={logs => R.path(['serverLogsCsv'])(logs)}
timezone={timezone}
/>
<Info3>{saveMessage}</Info3>
</div>
)}
</div>
<div className={classes.serverVersion}>
{serverVersion && <span>Server version: v{serverVersion}</span>}
</div>
</div>
<div className={classes.headerLine2}>
{data && (
<Select
onSelectedItemChange={handleLogLevelChange}
label="Level"
items={getLogLevels(data)}
default={SHOW_ALL}
selectedItem={logLevel}
/>
)}
<div className={classes.uptimeContainer}>
{processStates &&
processStates.map((process, idx) => (
<Uptime key={idx} process={process} />
))}
</div>
</div>
<div className={logsClasses.wrapper}>
<div ref={tableEl} className={classes.serverTableWrapper}>
<Table className={logsClasses.table}>
<TableHead>
<TableRow header>
<TableHeader className={logsClasses.dateColumn}>
Date
</TableHeader>
<TableHeader className={logsClasses.levelColumn}>
Level
</TableHeader>
<TableHeader className={logsClasses.fillColumn} />
</TableRow>
</TableHead>
<TableBody>
{data &&
data.serverLogs
.filter(
log =>
logLevel === SHOW_ALL || log.logLevel === logLevel.code
)
.map((log, idx) => (
<TableRow key={idx} size="sm">
<TableCell>
{timezone &&
formatDate(
log.timestamp,
timezone,
'yyyy-MM-dd HH:mm'
)}
</TableCell>
<TableCell>{log.logLevel}</TableCell>
<TableCell>{log.message}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{loading && <H4>{'Loading...'}</H4>}
{!loading && !data?.serverLogs?.length && (
<H4>{'No activity so far'}</H4>
)}
</div>
</div>
</>
)
}
export default Logs

View file

@ -0,0 +1,20 @@
.serverTableWrapper {
composes: tableWrapper from 'Logs.module.css';
max-width: 100%;
margin-left: 0;
}
.serverVersion {
color: var(--comet);
margin: auto 0 auto 0;
}
.headerLine2 {
display: flex;
justify-content: space-between;
margin-bottom: 24px;
}
.uptimeContainer {
margin: auto 0 auto 0;
}

View file

@ -0,0 +1,33 @@
import Chip from '@mui/material/Chip'
import * as R from 'ramda'
import React from 'react'
import { onlyFirstToUpper } from 'src/utils/string.js'
import { Label1 } from 'src/components/typography/index.jsx'
const Uptime = ({ process }) => {
const uptime = time => {
if (time < 60) return `${time}s`
if (time < 3600) return `${Math.floor(time / 60)}m`
if (time < 86400) return `${Math.floor(time / 60 / 60)}h`
return `${Math.floor(time / 60 / 60 / 24)}d`
}
return (
<div className="inline-block min-w-26 my-0 mx-5">
<Label1 noMargin className="pl-1 color-comet">
{R.toLower(process.name)}
</Label1>
<Chip
color={process.state === 'RUNNING' ? 'success' : 'error'}
label={
process.state === 'RUNNING'
? `Running for ${uptime(process.uptime)}`
: onlyFirstToUpper(process.state)
}
/>
</div>
)
}
export default Uptime