|
1 | 1 | import clsx from 'clsx' |
2 | | -import {convertUser} from 'common/supabase/users' |
3 | | -import {Row as rowfor, run} from 'common/supabase/utils' |
4 | | -import {User} from 'common/user' |
5 | | -import {HOUR_MS} from 'common/util/time' |
| 2 | +import {IS_LOCAL} from 'common/hosting/constants' |
| 3 | +import {Row as rowfor} from 'common/supabase/utils' |
6 | 4 | import {groupBy, orderBy} from 'lodash' |
7 | | -import {useEffect, useState} from 'react' |
| 5 | +import Router from 'next/router' |
| 6 | +import {useEffect} from 'react' |
8 | 7 | import {Button} from 'web/components/buttons/button' |
9 | 8 | import {Col} from 'web/components/layout/col' |
10 | 9 | import {Row} from 'web/components/layout/row' |
11 | 10 | import {NoSEO} from 'web/components/NoSEO' |
12 | 11 | import {UserAvatarAndBadge} from 'web/components/widgets/user-link' |
13 | 12 | import {useAdmin} from 'web/hooks/use-admin' |
| 13 | +import {useAPIGetter} from 'web/hooks/use-api-getter' |
14 | 14 | import {usePersistentQueryState} from 'web/hooks/use-persistent-query-state' |
15 | | -import {useIsAuthorized} from 'web/hooks/use-user' |
16 | | -import {db} from 'web/lib/supabase/db' |
17 | 15 |
|
18 | 16 | export default function Journeys() { |
19 | | - const [eventsByUser, setEventsByUser] = useState<Record<string, rowfor<'user_events'>[]>>({}) |
20 | 17 | const [hoursFromNowQ, setHoursFromNowQ] = usePersistentQueryState('h', '5') |
21 | | - const hoursFromNow = parseInt(hoursFromNowQ ?? '5') |
22 | | - const [unBannedUsers, setUnBannedUsers] = useState<User[]>([]) |
23 | | - const [bannedUsers, setBannedUsers] = useState<User[]>([]) |
24 | | - const isAuthed = useIsAuthorized() |
| 18 | + const hoursFromNow = hoursFromNowQ ?? '5' |
25 | 19 |
|
26 | | - const getEvents = async () => { |
27 | | - const start = Date.now() - hoursFromNow * HOUR_MS |
28 | | - const users = await run(db.from('users').select('id').gt('data->createdTime', start)) |
29 | | - const events = await run( |
30 | | - db |
31 | | - .from('user_events') |
32 | | - .select('*') |
33 | | - .in( |
34 | | - 'user_id', |
35 | | - users.data.map((u) => u.id), |
36 | | - ), |
37 | | - ) |
38 | | - const eventsByUser = groupBy( |
39 | | - orderBy(events.data as rowfor<'user_events'>[], 'ts', 'asc'), |
40 | | - 'user_id', |
41 | | - ) |
| 20 | + const {data} = useAPIGetter('get-user-journeys', {hoursFromNow}) |
42 | 21 |
|
43 | | - setEventsByUser(eventsByUser) |
44 | | - } |
| 22 | + const users = data?.users ?? [] |
| 23 | + const events = data?.events ?? [] |
45 | 24 |
|
46 | | - const getUsers = async () => { |
47 | | - const userData = await run(db.from('users').select().in('id', Object.keys(eventsByUser))) |
48 | | - const users = userData.data.map(convertUser) |
49 | | - setBannedUsers(users.filter((u) => u.isBannedFromPosting)) |
50 | | - setUnBannedUsers(users.filter((u) => !u.isBannedFromPosting)) |
51 | | - } |
| 25 | + const bannedUsers = users.filter((u) => u.isBannedFromPosting) |
| 26 | + const unBannedUsers = users.filter((u) => !u.isBannedFromPosting) |
52 | 27 |
|
53 | | - useEffect(() => { |
54 | | - getUsers() |
55 | | - }, [JSON.stringify(Object.keys(eventsByUser))]) |
| 28 | + const eventsByUser = groupBy(orderBy(events as rowfor<'user_events'>[], 'ts', 'asc'), 'user_id') |
| 29 | + |
| 30 | + const isAdmin = useAdmin() |
| 31 | + |
| 32 | + const authorized = isAdmin || IS_LOCAL |
56 | 33 |
|
57 | 34 | useEffect(() => { |
58 | | - if (!isAuthed) return |
59 | | - getEvents() |
60 | | - }, [hoursFromNow, isAuthed]) |
| 35 | + if (!authorized) Router.push('/') |
| 36 | + }, []) |
61 | 37 |
|
62 | | - const isAdmin = useAdmin() |
63 | | - if (!isAdmin) return <></> |
| 38 | + if (!authorized) return <></> |
64 | 39 |
|
65 | 40 | return ( |
66 | 41 | <Row> |
67 | 42 | <NoSEO /> |
68 | 43 | <div className="text-ink-900 mx-8"> |
69 | 44 | <div className={'text-primary-700 my-1 text-2xl'}>User Journeys</div> |
70 | 45 | <Row className={'items-center gap-2'}> |
71 | | - Viewing journeys from {unBannedUsers.length} unbanned users ({bannedUsers.length} banned). |
72 | | - Showing users created: {hoursFromNow}h ago. |
| 46 | + Viewing journeys from {unBannedUsers.length} users. Showing users created: {hoursFromNow}h |
| 47 | + ago. |
73 | 48 | <Button |
74 | 49 | color={'indigo-outline'} |
75 | 50 | size={'xs'} |
@@ -115,11 +90,21 @@ export default function Journeys() { |
115 | 90 | const timePeriod = |
116 | 91 | new Date(group[times - 1].ts!).valueOf() - new Date(group[0].ts!).valueOf() |
117 | 92 | const duration = Math.round(timePeriod / 1000) |
| 93 | + const data = group |
| 94 | + .map((g) => { |
| 95 | + if (!Object.keys(g.data).length) return |
| 96 | + return Object.entries(g.data) |
| 97 | + .map(([_k, v]) => `${v}`) |
| 98 | + .join(' ') |
| 99 | + }) |
| 100 | + .filter(Boolean) |
| 101 | + .join('. ') |
118 | 102 |
|
119 | 103 | return ( |
120 | 104 | <li key={index}> |
121 | 105 | {name} {times > 1 ? `${times}x` : ' '} |
122 | 106 | {duration > 1 ? ` (${duration}s)` : ' '} |
| 107 | + {data && <ul>{data}</ul>} |
123 | 108 | </li> |
124 | 109 | ) |
125 | 110 | })} |
|
0 commit comments