({
session,
onTerminate,
isTerminating,
}: SessionCardProps)
| 15 | } |
| 16 | |
| 17 | export const SessionCard = ({ |
| 18 | session, |
| 19 | onTerminate, |
| 20 | isTerminating, |
| 21 | }: SessionCardProps) => { |
| 22 | const { isDemoMode } = useAppMode(); |
| 23 | const t = useTranslations(); |
| 24 | return ( |
| 25 | <div |
| 26 | className={`jotty-session-card flex items-start justify-between p-4 rounded-jotty border ${session.isCurrent |
| 27 | ? "bg-primary/5 border-primary/20" |
| 28 | : "bg-background border-border" |
| 29 | }`} |
| 30 | > |
| 31 | <div className="flex items-start gap-4 flex-1 min-w-0"> |
| 32 | <div className="p-2 bg-muted rounded-jotty flex-shrink-0"> |
| 33 | <Tv02Icon className="h-5 w-5" /> |
| 34 | </div> |
| 35 | <div className="space-y-1 min-w-0 flex-1"> |
| 36 | <div className="flex items-center gap-2"> |
| 37 | <span className="font-medium"> |
| 38 | {getDeviceInfo(session.userAgent)} |
| 39 | </span> |
| 40 | {session.isCurrent && ( |
| 41 | <span className="px-2 py-1 text-sm lg:text-xs bg-primary/10 text-primary rounded-full"> |
| 42 | Current |
| 43 | </span> |
| 44 | )} |
| 45 | </div> |
| 46 | <div className="flex flex-wrap items-center gap-4 text-md lg:text-sm text-muted-foreground"> |
| 47 | <div className="flex items-center gap-1"> |
| 48 | <Location05Icon className="h-3 w-3" /> |
| 49 | <span>{isDemoMode ? "Hidden in demo" : session.ipAddress}</span> |
| 50 | </div> |
| 51 | <div className="flex items-center gap-1"> |
| 52 | <Clock01Icon className="h-3 w-3" /> |
| 53 | {formatTimeAgo(session.lastActivity, t)} |
| 54 | </div> |
| 55 | </div> |
| 56 | <p className="text-md lg:text-xs text-muted-foreground truncate"> |
| 57 | {isDemoMode |
| 58 | ? "Browser info hidden in demo mode" |
| 59 | : session.userAgent} |
| 60 | </p> |
| 61 | {isDemoMode && ( |
| 62 | <p className="text-md lg:text-xs text-amber-600 mt-1"> |
| 63 | Sensitive information is hidden in demo mode |
| 64 | </p> |
| 65 | )} |
| 66 | </div> |
| 67 | </div> |
| 68 | {!session.isCurrent && !isDemoMode && ( |
| 69 | <Button |
| 70 | variant="destructive" |
| 71 | size="sm" |
| 72 | onClick={() => onTerminate(session.id)} |
| 73 | className="flex-shrink-0 ml-2" |
| 74 | disabled={isTerminating} |
nothing calls this directly
no test coverage detected