mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-03-25 18:13:48 -04:00
* Test * Add pretty formatting * Fix Tests * Fix Tests * Fix Tests * Fix * Add pretty formatting fix * Fix * Test * Fix tests * Clean typeckech * Add prettier check * Fix api tsconfig * Fix api tsconfig * Fix tsconfig * Fix * Fix * Prettier
138 lines
4.9 KiB
TypeScript
138 lines
4.9 KiB
TypeScript
import clsx from 'clsx'
|
|
import {convertUser} from 'common/supabase/users'
|
|
import {Row as rowfor, run} from 'common/supabase/utils'
|
|
import {User} from 'common/user'
|
|
import {HOUR_MS} from 'common/util/time'
|
|
import {groupBy, orderBy} from 'lodash'
|
|
import {useEffect, useState} from 'react'
|
|
import {Button} from 'web/components/buttons/button'
|
|
import {Col} from 'web/components/layout/col'
|
|
import {Row} from 'web/components/layout/row'
|
|
import {NoSEO} from 'web/components/NoSEO'
|
|
import {UserAvatarAndBadge} from 'web/components/widgets/user-link'
|
|
import {useAdmin} from 'web/hooks/use-admin'
|
|
import {usePersistentQueryState} from 'web/hooks/use-persistent-query-state'
|
|
import {useIsAuthorized} from 'web/hooks/use-user'
|
|
import {db} from 'web/lib/supabase/db'
|
|
|
|
export default function Journeys() {
|
|
const [eventsByUser, setEventsByUser] = useState<Record<string, rowfor<'user_events'>[]>>({})
|
|
const [hoursFromNowQ, setHoursFromNowQ] = usePersistentQueryState('h', '5')
|
|
const hoursFromNow = parseInt(hoursFromNowQ ?? '5')
|
|
const [unBannedUsers, setUnBannedUsers] = useState<User[]>([])
|
|
const [bannedUsers, setBannedUsers] = useState<User[]>([])
|
|
const isAuthed = useIsAuthorized()
|
|
|
|
const getEvents = async () => {
|
|
const start = Date.now() - hoursFromNow * HOUR_MS
|
|
const users = await run(db.from('users').select('id').gt('data->createdTime', start))
|
|
const events = await run(
|
|
db
|
|
.from('user_events')
|
|
.select('*')
|
|
.in(
|
|
'user_id',
|
|
users.data.map((u) => u.id),
|
|
),
|
|
)
|
|
const eventsByUser = groupBy(
|
|
orderBy(events.data as rowfor<'user_events'>[], 'ts', 'asc'),
|
|
'user_id',
|
|
)
|
|
|
|
setEventsByUser(eventsByUser)
|
|
}
|
|
|
|
const getUsers = async () => {
|
|
const userData = await run(db.from('users').select().in('id', Object.keys(eventsByUser)))
|
|
const users = userData.data.map(convertUser)
|
|
setBannedUsers(users.filter((u) => u.isBannedFromPosting))
|
|
setUnBannedUsers(users.filter((u) => !u.isBannedFromPosting))
|
|
}
|
|
|
|
useEffect(() => {
|
|
getUsers()
|
|
}, [JSON.stringify(Object.keys(eventsByUser))])
|
|
|
|
useEffect(() => {
|
|
if (!isAuthed) return
|
|
getEvents()
|
|
}, [hoursFromNow, isAuthed])
|
|
|
|
const isAdmin = useAdmin()
|
|
if (!isAdmin) return <></>
|
|
|
|
return (
|
|
<Row>
|
|
<NoSEO />
|
|
<div className="text-ink-900 mx-8">
|
|
<div className={'text-primary-700 my-1 text-2xl'}>User Journeys</div>
|
|
<Row className={'items-center gap-2'}>
|
|
Viewing journeys from {unBannedUsers.length} unbanned users ({bannedUsers.length} banned).
|
|
Showing users created: {hoursFromNow}h ago.
|
|
<Button
|
|
color={'indigo-outline'}
|
|
size={'xs'}
|
|
onClick={() => {
|
|
setHoursFromNowQ((hoursFromNow + 1).toString())
|
|
}}
|
|
>
|
|
+1h
|
|
</Button>
|
|
</Row>
|
|
<Row className={'flex-wrap gap-2 scroll-auto'}>
|
|
{Object.keys(eventsByUser).map((userId) => {
|
|
if (bannedUsers.find((u) => u.id === userId)) return null
|
|
const events = eventsByUser[userId]
|
|
const eventGroups: {[key: string]: any[]} = {}
|
|
let eventName = ''
|
|
let groupKey = ''
|
|
events.forEach((event, index) => {
|
|
if (event.name !== eventName) groupKey = `${event.name}_${index}`
|
|
if (!eventGroups[groupKey]) eventGroups[groupKey] = []
|
|
eventGroups[groupKey].push(event)
|
|
eventName = event.name
|
|
})
|
|
const user = unBannedUsers.find((u) => u.id === userId)
|
|
|
|
return (
|
|
<Col className={'mt-4 min-w-[15rem]'} key={userId}>
|
|
<Row
|
|
className={clsx(
|
|
'rounded-md p-1',
|
|
// user && isUserLikelySpammer(user) ? 'bg-amber-100' : ''
|
|
)}
|
|
>
|
|
{user ? <UserAvatarAndBadge user={user} /> : userId}
|
|
</Row>
|
|
<ul>
|
|
<li>{new Date(events[0].ts!).toLocaleString()}</li>
|
|
</ul>
|
|
<Col>
|
|
{Object.values(eventGroups).map((group, index) => {
|
|
const name = group[0].name
|
|
const times = group.length
|
|
const timePeriod =
|
|
new Date(group[times - 1].ts!).valueOf() - new Date(group[0].ts!).valueOf()
|
|
const duration = Math.round(timePeriod / 1000)
|
|
|
|
return (
|
|
<li key={index}>
|
|
{name} {times > 1 ? `${times}x` : ' '}
|
|
{duration > 1 ? ` (${duration}s)` : ' '}
|
|
</li>
|
|
)
|
|
})}
|
|
</Col>
|
|
<ul>
|
|
<li>{new Date(events[events.length - 1].ts!).toLocaleString()}</li>
|
|
</ul>
|
|
</Col>
|
|
)
|
|
})}
|
|
</Row>
|
|
</div>
|
|
</Row>
|
|
)
|
|
}
|