Skip to content

Commit

Permalink
wip: player modal data
Browse files Browse the repository at this point in the history
  • Loading branch information
tabarra committed Dec 27, 2023
1 parent 4109888 commit 80bd16e
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 109 deletions.
7 changes: 7 additions & 0 deletions core/components/FxRunner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@ export default class FXRunner {
//Reseting monitor stats
globals.healthMonitor.resetMonitorStats();

//Resetting frontend playerlist
globals.webServer.webSocket.buffer('playerlist', {
mutex: this.currentMutex,
type: 'fullPlayerlist',
playerlist: [],
});

//Announcing
if (announce === 'true' || announce === true) {
globals.discordBot.sendAnnouncement({
Expand Down
2 changes: 1 addition & 1 deletion panel/src/components/AccountDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { memo, useEffect, useState } from "react";
import { TabsTrigger, TabsList, TabsContent, Tabs } from "@/components/ui/tabs";
import { ApiChangeIdentifiersReq, ApiChangePasswordReq } from "@shared/authApiTypes";
import { useAccountModal, useCloseAccountModal } from "@/hooks/dialogs";
import { ApiAuthErrorResp, GenericApiOkResp, GenericApiResp } from "@shared/genericApiTypes";
import { GenericApiOkResp } from "@shared/genericApiTypes";
import { fetchWithTimeout, useAuthedFetcher, useBackendApi } from "@/hooks/fetch";
import consts from "@shared/consts";
import { txToast } from "./TxToaster";
Expand Down
2 changes: 1 addition & 1 deletion panel/src/hooks/playerModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { atomWithReset } from 'jotai/utils';
/**
* Prompt Dialog Stuff
*/
type PlayerModalRefType = {
export type PlayerModalRefType = {
mutex: string;
netid: number;
} | {
Expand Down
4 changes: 3 additions & 1 deletion panel/src/layout/playerModal/BanTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { PlayerModalRefType } from "@/hooks/playerModal";
import { Loader2Icon } from "lucide-react";
import { useRef, useState } from "react";


export default function BanTab() {
export default function BanTab({ playerRef }: { playerRef: Exclude<PlayerModalRefType, undefined> }) {
const reasonRef = useRef<HTMLInputElement>(null);
const [currentDuration, setCurrentDuration] = useState('2 days');
const [customMultiplier, setCustomMultipler] = useState('2');
Expand All @@ -21,6 +22,7 @@ export default function BanTab() {
console.log('Duration:', currentDuration);
console.log('Custom Multiplier:', customMultiplier);
console.log('Custom Units:', customUnits);
console.log('Player:', playerRef);
console.groupEnd();
setTimeout(() => {
setIsSaving(false);
Expand Down
22 changes: 14 additions & 8 deletions panel/src/layout/playerModal/HistoryTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { cn } from "@/lib/utils";
import { PlayerHistoryItem, PlayerModalSuccess } from "@shared/playerApiTypes";



type HistoryItemProps = {
action: PlayerHistoryItem,
permsDisableWarn: boolean,
permsDisableBan: boolean,
serverTime: number,
}

function HistoryItem({ action, permsDisableWarn, permsDisableBan, serverTime }: HistoryItemProps) {
const isRevokeDisabled = (
!!action.revokedBy ||
Expand All @@ -36,7 +36,7 @@ function HistoryItem({ action, permsDisableWarn, permsDisableBan, serverTime }:
}

return (
<div className={cn('pl-2 border-l-4 hover:bg-muted', borderColorClass)}>
<div className={cn('pl-2 border-l-4 hover:bg-muted rounded-r-sm', borderColorClass)}>
<div className="flex w-full justify-between">
<strong className="text-sm text-muted-foreground">{actionMessage}</strong>
<small className="text-right text-xxs space-x-1">
Expand All @@ -57,10 +57,16 @@ function HistoryItem({ action, permsDisableWarn, permsDisableBan, serverTime }:
}


export default function HistoryTab() {
return <div className="flex flex-col gap-1">
{exampleData.player.actionHistory.map((action) => (
<HistoryItem action={action} permsDisableWarn={false} permsDisableBan={false} serverTime={0} />
))}
</div>;
export default function HistoryTab({ actionHistory }: { actionHistory: PlayerHistoryItem[] }) {
if (!actionHistory.length) {
return <div className="flex items-center justify-center min-h-[16.5rem] p-1">
<span className="text-xl text-muted-foreground">No bans/warns found.</span>
</div>;
} else {
return <div className="flex flex-col gap-1 p-1">
{actionHistory.map((action) => (
<HistoryItem key={action.id} action={action} permsDisableWarn={false} permsDisableBan={false} serverTime={0} />
))}
</div>;
}
}
28 changes: 11 additions & 17 deletions panel/src/layout/playerModal/IdsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { txToast } from "@/components/TxToaster";
import { cn } from "@/lib/utils";
import { PlayerModalPlayerData } from "@shared/playerApiTypes";
import { CopyIcon } from "lucide-react";
import { useState } from "react";

//DEBUG
const exampleData = {
ids: [],
hwids: [],
oldIds: [],
oldHwids: [],
}

type IdsBlockProps = {
title: string,
emptyMessage: string,
allIds: string[],
currIds: string[],
allIds: string[],
isSmaller?: boolean,
}
function IdsBlock({ title, emptyMessage, allIds, currIds, isSmaller }: IdsBlockProps) {
function IdsBlock({ title, emptyMessage, currIds, allIds, isSmaller }: IdsBlockProps) {
const [hasCopiedIds, setHasCopiedIds] = useState(false);
const displayCurrIds = currIds.sort((a, b) => a.localeCompare(b));
const displayOldIds = allIds.filter((id) => !currIds.includes(id)).sort((a, b) => a.localeCompare(b));
Expand Down Expand Up @@ -51,29 +45,29 @@ function IdsBlock({ title, emptyMessage, allIds, currIds, isSmaller }: IdsBlockP
)}>
{displayCurrIds.length === 0 && <span className="block px-1 opacity-50 italic">{emptyMessage}</span>}
{displayCurrIds.map((id) => (
<span className="block px-1 font-semibold">{id}</span>
<span key={id} className="block px-1 font-semibold">{id}</span>
))}
{displayOldIds.map((id) => (
<span className="block px-1 opacity-50">{id}</span>
<span key={id} className="block px-1 opacity-50">{id}</span>
))}
</p>
</div>
}


export default function IdsTab() {
return <div className="flex flex-col gap-4">
export default function IdsTab({ player }: { player: PlayerModalPlayerData }) {
return <div className="flex flex-col gap-4 p-1">
<IdsBlock
title="Player Identifiers"
emptyMessage="This player has no identifiers."
allIds={exampleData.oldIds}
currIds={exampleData.ids}
currIds={player.ids}
allIds={player?.oldIds ?? []}
/>
<IdsBlock
title="Player Hardware IDs"
emptyMessage="This player has no hardware IDs."
allIds={exampleData.oldHwids}
currIds={exampleData.hwids} isSmaller
currIds={player.hwids} isSmaller
allIds={player?.oldHwids ?? []}
/>
</div>;
}
100 changes: 75 additions & 25 deletions panel/src/layout/playerModal/InfoTab.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import InlineCode from "@/components/InlineCode";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import { cn, msToDuration, tsToLocaleDate } from "@/lib/utils";
import { PlayerModalPlayerData } from "@shared/playerApiTypes";
import { useRef, useState } from "react";


function LogActionCounter({ type, count }: { type: 'Ban' | 'Warn', count: number }) {
Expand All @@ -24,66 +25,115 @@ function LogActionCounter({ type, count }: { type: 'Ban' | 'Warn', count: number
}
}

function PlayerNotesBox() {

function PlayerNotesBox({ player }: { player: PlayerModalPlayerData }) {
const textAreaRef = useRef<HTMLTextAreaElement>(null);
const [notesLogText, setNotesLogText] = useState(player.notesLog ?? '');

const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
setNotesLogText('Saving...');
// FIXME: send request to save notes
setTimeout(() => {
setNotesLogText('Saved!');
}, 2000);
console.log(textAreaRef.current?.value)
}
}

return <>
<Label htmlFor="playerNotes">
Notes:&nbsp;
<span className="text-muted-foreground">Last modified by <InlineCode>tabarra</InlineCode> in October 18, 2003.</span>
Notes: <span className="text-muted-foreground">{notesLogText}</span>
</Label>
<Textarea
ref={textAreaRef}
id="playerNotes"
placeholder="Type your notes about the player."
className="w-full mt-1"
disabled={!player.isRegistered}
defaultValue={player.notes}
onChange={() => setNotesLogText('Press enter to save.')}
onKeyDown={handleKeyDown}
placeholder={player.isRegistered
? 'Type your notes about the player.'
: 'Cannot set notes for players that are not registered.'}
/>
</>
}

export default function InfoTab() {
return <div>
type InfoTabProps = {
player: PlayerModalPlayerData,
setSelectedTab: (t: string) => void,
}

export default function InfoTab({ player, setSelectedTab }: InfoTabProps) {
const sessionTimeText = player.sessionTime ? msToDuration(
player.sessionTime * 60_000,
{ units: ['h', 'm'] }
) : '--';
const lastConnectionText = player.tsLastConnection
? tsToLocaleDate(player.tsLastConnection)
: '--';
const playTimeText = player.playTime ? msToDuration(
player.playTime * 60_000,
{ units: ['d', 'h', 'm'] }
) : '--';
const joinDateText = player.tsJoined ? tsToLocaleDate(player.tsJoined) : '--';
const whitelistedText = player.tsWhitelisted ? tsToLocaleDate(player.tsWhitelisted) : 'not yet';
const banCount = player.actionHistory.filter((a) => a.type === 'ban').length;
const warnCount = player.actionHistory.filter((a) => a.type === 'warn').length;

return <div className="p-1">
<dl className="pb-2">
<div className="py-0.5 grid grid-cols-3 gap-4 px-0">
{player.isConnected && <div className="py-0.5 grid grid-cols-3 gap-4 px-0">
<dt className="text-sm font-medium leading-6 text-muted-foreground">Session Time</dt>
<dd className="text-sm leading-6 col-span-2 mt-0">5 hours, 43 minutes</dd>
</div>
<dd className="text-sm leading-6 col-span-2 mt-0">{sessionTimeText}</dd>
</div>}
<div className="py-0.5 grid grid-cols-3 gap-4 px-0">
<dt className="text-sm font-medium leading-6 text-muted-foreground">Play Time</dt>
<dd className="text-sm leading-6 col-span-2 mt-0">18 days, 21 hours</dd>
<dd className="text-sm leading-6 col-span-2 mt-0">{playTimeText}</dd>
</div>
<div className="py-0.5 grid grid-cols-3 gap-4 px-0">
<dt className="text-sm font-medium leading-6 text-muted-foreground">Joined</dt>
<dd className="text-sm leading-6 col-span-2 mt-0">20 December 2022</dd>
<dt className="text-sm font-medium leading-6 text-muted-foreground">Join Date</dt>
<dd className="text-sm leading-6 col-span-2 mt-0">{joinDateText}</dd>
</div>
{!player.isConnected && <div className="py-0.5 grid grid-cols-3 gap-4 px-0">
<dt className="text-sm font-medium leading-6 text-muted-foreground">Last Connection</dt>
<dd className="text-sm leading-6 col-span-2 mt-0">{lastConnectionText}</dd>
</div>}

<div className="py-0.5 grid grid-cols-3 gap-4 px-0">
<dt className="text-sm font-medium leading-6 text-muted-foreground">Whitelisted</dt>
<dd className="text-sm leading-6 mt-0">
not yet
</dd>
<dd className="text-sm leading-6 mt-0">{whitelistedText}</dd>
<dd className="text-right">
<Button
variant="outline"
size='inline'
style={{ minWidth: '8ch' }}
onClick={() => { }}
>Add WL</Button>
style={{ minWidth: '8.25ch' }}
onClick={() => { }} //FIXME:
disabled={false} //FIXME:
>
{player.tsWhitelisted ? 'Remove' : 'Add WL'}
</Button>
</dd>
</div>
<div className="py-0.5 grid grid-cols-3 gap-4 px-0">
<dt className="text-sm font-medium leading-6 text-muted-foreground">Log</dt>
<dd className="text-sm leading-6 mt-0 space-x-2">
<LogActionCounter type="Ban" count={12} />
<LogActionCounter type="Warn" count={2} />
<LogActionCounter type="Ban" count={banCount} />
<LogActionCounter type="Warn" count={warnCount} />
</dd>
<dd className="text-right">
<Button
variant="outline"
size='inline'
style={{ minWidth: '8ch' }}
onClick={() => { }}
style={{ minWidth: '8.25ch' }}
onClick={() => {setSelectedTab('History')}}
>View</Button>
</dd>
</div>
</dl>

<PlayerNotesBox />
<PlayerNotesBox player={player} />
</div>;
}

0 comments on commit 80bd16e

Please sign in to comment.