Skip to content

Commit 815ef68

Browse files
committed
fix: renterd contract set change alert
1 parent 90280b0 commit 815ef68

File tree

1 file changed

+115
-37
lines changed

1 file changed

+115
-37
lines changed

apps/renterd/contexts/alerts/SetChange.tsx

Lines changed: 115 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Text, Tooltip, ValueCopyable } from '@siafoundation/design-system'
22
import { HostContextMenuFromKey } from '../../components/Hosts/HostContextMenuFromKey'
33
import { ContractContextMenuFromId } from '../../components/Contracts/ContractContextMenuFromId'
44
import { humanBytes } from '@siafoundation/units'
5-
import { format } from 'date-fns'
5+
import { formatRelative } from 'date-fns'
66
import { useMemo } from 'react'
77
import { Add16, Subtract16 } from '@siafoundation/react-icons'
88
import { cx } from 'class-variance-authority'
@@ -50,36 +50,103 @@ export function SetChangesField({
5050
...Object.keys(setAdditions),
5151
...Object.keys(setRemovals),
5252
])
53-
return contractIds.map((contractId) => {
54-
const additions = setAdditions[contractId]?.additions || []
55-
const removals = setRemovals[contractId]?.removals || []
56-
return {
57-
contractId,
58-
hostKey:
59-
setAdditions[contractId]?.hostKey || setRemovals[contractId]?.hostKey,
60-
events: [
61-
...additions.map((a) => ({
62-
type: 'addition',
63-
size: a.size,
64-
time: a.time,
65-
})),
66-
...removals.map((r) => ({
67-
type: 'removal',
68-
size: r.size,
69-
time: r.time,
70-
reasons: r.reasons,
71-
})),
72-
].sort((a, b) =>
73-
new Date(a.time).getTime() > new Date(b.time).getTime() ? 1 : -1
74-
) as ChangeEvent[],
75-
}
76-
})
53+
return contractIds
54+
.map((contractId) => {
55+
const additions = setAdditions[contractId]?.additions || []
56+
const removals = setRemovals[contractId]?.removals || []
57+
return {
58+
contractId,
59+
hostKey:
60+
setAdditions[contractId]?.hostKey ||
61+
setRemovals[contractId]?.hostKey,
62+
events: [
63+
...additions.map((a) => ({
64+
type: 'addition',
65+
size: a.size,
66+
time: a.time,
67+
})),
68+
...removals.map((r) => ({
69+
type: 'removal',
70+
size: r.size,
71+
time: r.time,
72+
reasons: r.reasons,
73+
})),
74+
].sort((a, b) =>
75+
new Date(a.time).getTime() < new Date(b.time).getTime() ? 1 : -1
76+
) as ChangeEvent[],
77+
}
78+
})
79+
.sort((a, b) => {
80+
// size in latest event
81+
const aSize = a.events[0].size
82+
const bSize = b.events[0].size
83+
return aSize < bSize ? 1 : -1
84+
})
7785
}, [setAdditions, setRemovals])
86+
87+
// calculate churn %: contracts removed size / total size
88+
const totalSize = useMemo(
89+
() => changes.reduce((acc, { events }) => acc + events[0].size, 0),
90+
[changes]
91+
)
92+
const removals = useMemo(
93+
() => changes.filter(({ events }) => events[0].type === 'removal'),
94+
[changes]
95+
)
96+
const additions = useMemo(
97+
() => changes.filter(({ events }) => events[0].type === 'addition'),
98+
[changes]
99+
)
100+
const removedSize = useMemo(
101+
() => removals.reduce((acc, { events }) => acc + events[0].size, 0),
102+
[removals]
103+
)
104+
const churn = useMemo(
105+
() => (removedSize / totalSize) * 100,
106+
[removedSize, totalSize]
107+
)
108+
78109
return (
79110
<div className="flex flex-col gap-2">
80-
<Text size="12" color="subtle" ellipsis>
81-
contract set changes
82-
</Text>
111+
<div className="flex gap-2 items-center pr-1">
112+
<Text size="12" color="subtle" ellipsis>
113+
contract set changes
114+
</Text>
115+
<div className="flex-1" />
116+
<Tooltip
117+
content={`${humanBytes(removedSize)} of ${humanBytes(
118+
totalSize
119+
)} contract size removed`}
120+
>
121+
<div className="flex gap-1 items-center">
122+
<Text size="12" color="contrast" ellipsis>
123+
churn: {churn.toFixed(2)}%
124+
</Text>
125+
<Text size="12" color="subtle" ellipsis>
126+
({humanBytes(removedSize)} / {humanBytes(totalSize)})
127+
</Text>
128+
</div>
129+
</Tooltip>
130+
<div className="flex gap-1 items-center">
131+
<Tooltip content={`${additions.length} contracts added`}>
132+
<Text
133+
size="12"
134+
color="green"
135+
ellipsis
136+
className="flex items-center"
137+
>
138+
<Add16 />
139+
{additions.length}
140+
</Text>
141+
</Tooltip>
142+
<Tooltip content={`${removals.length} contracts removed`}>
143+
<Text size="12" color="red" ellipsis className="flex items-center">
144+
<Subtract16 />
145+
{removals.length}
146+
</Text>
147+
</Tooltip>
148+
</div>
149+
</div>
83150
<div className="flex flex-col gap-3 mb-2">
84151
{changes.map(({ contractId, hostKey, events }, i) => (
85152
<ContractSetChange
@@ -104,11 +171,12 @@ function ContractSetChange({
104171
i: number
105172
}) {
106173
return (
107-
<div className="flex flex-col gap-2">
108-
<div className="flex gap-2 justify-between items-center">
109-
<Text size="12" ellipsis>
174+
<div className="flex flex-col gap-[3px]">
175+
<div className="flex gap-2 items-center px-[3px]">
176+
<Text size="12" weight="medium" ellipsis>
110177
{i + 1}.
111178
</Text>
179+
<div className="flex-1" />
112180
<div className="flex gap-2 items-center">
113181
<Text size="12" color="subtle" ellipsis>
114182
contract
@@ -119,6 +187,9 @@ function ContractSetChange({
119187
contextMenu={
120188
<ContractContextMenuFromId
121189
id={contractId}
190+
buttonProps={{
191+
size: 'none',
192+
}}
122193
contentProps={{
123194
align: 'end',
124195
}}
@@ -137,6 +208,9 @@ function ContractSetChange({
137208
contextMenu={
138209
<HostContextMenuFromKey
139210
hostKey={hostKey}
211+
buttonProps={{
212+
size: 'none',
213+
}}
140214
contentProps={{
141215
align: 'end',
142216
}}
@@ -145,7 +219,7 @@ function ContractSetChange({
145219
/>
146220
</div>
147221
</div>
148-
{events.map(({ type, reasons, size, time }) => (
222+
{events.map(({ type, reasons, size, time }, i) => (
149223
<Tooltip
150224
key={type + reasons + time}
151225
content={type === 'addition' ? 'added' : `removed: ${reasons}`}
@@ -154,8 +228,12 @@ function ContractSetChange({
154228
>
155229
<div
156230
className={cx(
157-
'flex gap-2 justify-between',
158-
type === 'addition' ? 'bg-green-400/20' : 'bg-red-400/20'
231+
'flex gap-2 justify-between mr-2 pr-1',
232+
i === 0
233+
? type === 'addition'
234+
? 'bg-green-400/20'
235+
: 'bg-red-400/20'
236+
: 'opacity-50'
159237
)}
160238
>
161239
<div className="flex gap-1 items-center overflow-hidden">
@@ -168,15 +246,15 @@ function ContractSetChange({
168246
</div>
169247
<div className="flex-1" />
170248
<div className="flex gap-2">
171-
<Text size="12" color="subtle" ellipsis>
249+
<Text color="subtle" size="12" ellipsis>
172250
time
173251
</Text>
174252
<Text size="12" ellipsis>
175-
{format(new Date(time), 'yyyy-MM-dd HH:mm a')}
253+
{formatRelative(new Date(time), new Date())}
176254
</Text>
177255
</div>
178256
<div className="flex gap-2">
179-
<Text size="12" color="subtle" ellipsis>
257+
<Text color="subtle" size="12" ellipsis>
180258
size
181259
</Text>
182260
<Text size="12" ellipsis>

0 commit comments

Comments
 (0)