11"use client"
22import { useQuery , useQueryClient } from "@tanstack/react-query"
3- import { ArrowLeft , ExternalLinkIcon , Search , X } from "lucide-react"
3+ import { ArrowLeft , ExternalLinkIcon , Search , Star , X } from "lucide-react"
44import Link from "next/link"
55import { useState } from "react"
66import { toast } from "sonner"
@@ -9,9 +9,10 @@ import { Button } from "@/components/ui/button"
99import { Card , CardContent , CardFooter , CardHeader , CardTitle } from "@/components/ui/card"
1010import { Input } from "@/components/ui/input"
1111import { Label } from "@/components/ui/label"
12+ import { useSession } from "@/lib/auth"
1213import { useTRPC } from "@/lib/trpc/client"
1314import type { ApiOutput } from "@/lib/trpc/types"
14- import { stripChatId } from "@/lib/utils/telegram"
15+ import { fmtUser , stripChatId } from "@/lib/utils/telegram"
1516import { AddRole } from "./add-role"
1617import { DeleteGroupAdmin } from "./delete-group-admin"
1718import { NewGroupAdmin } from "./new-group-admin"
@@ -29,6 +30,7 @@ export default function TgUsers() {
2930 const searchQuery = trpc . tg . users . getByUsername . queryOptions ( { username : query } )
3031 const { data : userData } = useQuery ( trpc . tg . permissions . getRoles . queryOptions ( { userId : user ?. id ?? 0 } ) )
3132 const { data : messages } = useQuery ( trpc . tg . messages . getLastByUser . queryOptions ( { userId : user ?. id ?? 0 } ) )
33+ const { data : auditLog } = useQuery ( trpc . tg . auditLog . getById . queryOptions ( { targetId : user ?. id ?? 0 } ) )
3234
3335 async function search ( e : React . FormEvent < HTMLFormElement > ) {
3436 e . preventDefault ( )
@@ -97,13 +99,37 @@ export default function TgUsers() {
9799 . map ( ( m ) => (
98100 < GroupAdminCard groupAdminInfo = { m } user = { user } key = { m . group . id } />
99101 ) ) }
102+
103+ { userData ?. groupAdmin . length === 0 && (
104+ < p className = "bg-card rounded-lg p-3 italic text-muted-foreground text-sm" >
105+ This user is not group admin in any group.
106+ </ p >
107+ ) }
100108 </ div >
101109
102110 < p className = "pt-6" > Last messages:</ p >
103111 < div className = "grid grid-cols-3 py-2 gap-4" >
104112 { messages ?. messages ?. map ( ( m ) => (
105113 < MessageCard message = { m } key = { `${ m . chatId } -${ m . messageId } ` } />
106114 ) ) }
115+
116+ { messages ?. messages ?. length === 0 && (
117+ < p className = "bg-card rounded-lg p-3 italic text-muted-foreground text-sm" >
118+ No recent messages sent by this user
119+ </ p >
120+ ) }
121+ </ div >
122+
123+ < p className = "pt-6" > Audit log:</ p >
124+ < div className = "grid grid-cols-3 py-2 gap-4" >
125+ { auditLog ?. map ( ( m ) => (
126+ < AuditLogCard log = { m } key = { `${ m . id } -${ m . type } ` } />
127+ ) ) }
128+ { auditLog ?. length === 0 && (
129+ < p className = "bg-card rounded-lg p-3 italic text-muted-foreground text-sm" >
130+ No audit log found for this user
131+ </ p >
132+ ) }
107133 </ div >
108134 </ >
109135 ) }
@@ -113,15 +139,26 @@ export default function TgUsers() {
113139
114140type UserRoles = ApiOutput [ "tg" ] [ "permissions" ] [ "getRoles" ] [ "roles" ]
115141function UserInfoCard ( { user, roles } : { user : NonNullable < User > ; roles : UserRoles } ) {
142+ const sesh = useSession ( )
143+ const seshUserId = sesh . data ?. user . telegramId
144+ const isSelf = seshUserId && seshUserId === user . id
145+
116146 return (
117147 < Card className = "w-fit min-w-120" >
118148 < CardHeader >
119- < CardTitle > User ID: { user . id } </ CardTitle >
149+ < CardTitle >
150+ User ID: { user . id } { " " }
151+ { isSelf && (
152+ < Badge >
153+ < Star /> You
154+ </ Badge >
155+ ) }
156+ </ CardTitle >
120157 </ CardHeader >
121158 < CardContent className = "space-y-2" >
122159 < p >
123160 < span className = "text-muted-foreground" > Name: </ span >
124- { user . firstName } { user . lastName }
161+ { user . firstName } { user . lastName ?? "" }
125162 </ p >
126163 < p >
127164 < span className = "text-muted-foreground" > Username: </ span >
@@ -143,8 +180,8 @@ function UserInfoCard({ user, roles }: { user: NonNullable<User>; roles: UserRol
143180type GroupAdminSingle = NonNullable < ApiOutput [ "tg" ] [ "permissions" ] [ "getRoles" ] [ "groupAdmin" ] [ number ] >
144181function GroupAdminCard ( { user, groupAdminInfo : m } : { user : NonNullable < User > ; groupAdminInfo : GroupAdminSingle } ) {
145182 return (
146- < Card key = { m . group . id } >
147- < CardContent >
183+ < Card >
184+ < CardContent className = "space-y-2" >
148185 < p >
149186 { " " }
150187 < span className = "text-muted-foreground" > Chat: </ span >
@@ -165,20 +202,17 @@ function GroupAdminCard({ user, groupAdminInfo: m }: { user: NonNullable<User>;
165202type Message = NonNullable < ApiOutput [ "tg" ] [ "messages" ] [ "getLastByUser" ] [ "messages" ] > [ number ]
166203function MessageCard ( { message : m } : { message : Message } ) {
167204 return (
168- < Card key = { ` ${ m . messageId } - ${ m . chatId } ` } >
169- < CardContent >
205+ < Card >
206+ < CardContent className = "space-y-1" >
170207 < p >
171- { " " }
172208 < span className = "text-muted-foreground" > Chat: </ span >
173209 { m . group && < span > { m . group . title } </ span > } [{ m . chatId } ]
174210 </ p >
175211 < p >
176- { " " }
177212 < span className = "text-muted-foreground" > Message ID: </ span >
178213 { m . messageId }
179214 </ p >
180215 < p >
181- { " " }
182216 < span className = "text-muted-foreground" > Timestamp: </ span >
183217 { m . timestamp . toLocaleString ( ) }
184218 </ p >
@@ -207,3 +241,46 @@ function MessageCard({ message: m }: { message: Message }) {
207241 </ Card >
208242 )
209243}
244+
245+ type Log = NonNullable < ApiOutput [ "tg" ] [ "auditLog" ] [ "getById" ] > [ number ]
246+ function AuditLogCard ( { log : m } : { log : Log } ) {
247+ const trpc = useTRPC ( )
248+
249+ const { data : admin } = useQuery ( trpc . tg . users . get . queryOptions ( { userId : m . adminId } ) )
250+
251+ return (
252+ < Card >
253+ < CardHeader >
254+ < CardTitle > { m . type } </ CardTitle >
255+ </ CardHeader >
256+ < CardContent >
257+ < p >
258+ { " " }
259+ < span className = "text-muted-foreground" > Chat: </ span >
260+ { m . groupTitle && < span > { m . groupTitle } </ span > } [{ m . groupId } ]
261+ </ p >
262+ < p >
263+ { " " }
264+ < span className = "text-muted-foreground" > Admin ID: </ span >
265+ { admin && admin . user && fmtUser ( admin . user ) }
266+ </ p >
267+ { m . createdAt && (
268+ < p >
269+ < span className = "text-muted-foreground" > Created: </ span >
270+ { m . createdAt . toLocaleString ( ) }
271+ </ p >
272+ ) }
273+ { m . until && (
274+ < p >
275+ < span className = "text-muted-foreground" > Until: </ span >
276+ { m . until . toLocaleString ( ) }
277+ </ p >
278+ ) }
279+ < p >
280+ < span className = "text-muted-foreground" > Reason:</ span >
281+ { m . reason }
282+ </ p >
283+ </ CardContent >
284+ </ Card >
285+ )
286+ }
0 commit comments