mirror of
https://github.com/hubHarmony/servii-frontend.git
synced 2024-11-18 05:40:31 +00:00
commit
0a0b5a7020
210
src/pages/PlayersStatus/PlayersStatus.jsx
Normal file
210
src/pages/PlayersStatus/PlayersStatus.jsx
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import styles from './PlayersStatus.module.scss';
|
||||||
|
import serviiApi from "../../service/api.tsx";
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
const getPlayerhead = async (username) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://mc-heads.net/avatar/${username}/64.png`);
|
||||||
|
return response.url;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching player head:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlayerStatus = () => {
|
||||||
|
const { serverName } = useParams();
|
||||||
|
|
||||||
|
const [whiteListUsername, setWhiteListUsername] = useState('');
|
||||||
|
const [adminUsername, setAdminUsername] = useState('');
|
||||||
|
const [banUsername, setBanUsername] = useState('');
|
||||||
|
|
||||||
|
const [whitelist, setWhitelist] = useState([]);
|
||||||
|
const [operators, setOperators] = useState([]);
|
||||||
|
const [bannedPlayers, setBannedPlayers] = useState([]);
|
||||||
|
|
||||||
|
const [avatars, setAvatars] = useState({});
|
||||||
|
|
||||||
|
const loadPlayersStatus = async () => {
|
||||||
|
try {
|
||||||
|
const ApiResponse = await serviiApi.fetchPlayersStatus(serverName);
|
||||||
|
|
||||||
|
const cleanAndParse = (data) => {
|
||||||
|
if (data) {
|
||||||
|
const cleanedData = data.replace(/\\n/g, '').replace(/\\r/g, '');
|
||||||
|
return tryParseJSON(cleanedData);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedWhitelist = cleanAndParse(ApiResponse.message.Whitelist);
|
||||||
|
const parsedOperators = cleanAndParse(ApiResponse.message.Operators);
|
||||||
|
const parsedBannedPlayers = cleanAndParse(ApiResponse.message.BannedPlayers);
|
||||||
|
|
||||||
|
setWhitelist(parsedWhitelist || []);
|
||||||
|
setOperators(parsedOperators || []);
|
||||||
|
setBannedPlayers(parsedBannedPlayers || []);
|
||||||
|
|
||||||
|
const allPlayers = [...parsedWhitelist, ...parsedOperators, ...parsedBannedPlayers];
|
||||||
|
const avatarsPromises = allPlayers.map(player =>
|
||||||
|
getPlayerhead(player.name).then(url => ({ [player.uuid]: url }))
|
||||||
|
);
|
||||||
|
|
||||||
|
const avatarsArray = await Promise.all(avatarsPromises);
|
||||||
|
const avatarsMap = Object.assign({}, ...avatarsArray);
|
||||||
|
|
||||||
|
setAvatars(avatarsMap);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching data:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tryParseJSON = (jsonString) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(jsonString);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Could not parse JSON: ", jsonString);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePlayerStatus = async (username, commandType, clearInput) => {
|
||||||
|
try {
|
||||||
|
await AddPlayerStatus(username, commandType);
|
||||||
|
clearInput();
|
||||||
|
loadPlayersStatus();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding a player status:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddPlayerStatus = async (username, commandType) => {
|
||||||
|
let serverCommande = '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (commandType) {
|
||||||
|
case 'whiteList':
|
||||||
|
serverCommande = `whitelist add ${username}`;
|
||||||
|
break;
|
||||||
|
case 'admin':
|
||||||
|
serverCommande = `op ${username}`;
|
||||||
|
break;
|
||||||
|
case 'ban':
|
||||||
|
serverCommande = `ban ${username}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await serviiApi.command(serverCommande, serverName);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding a player status:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadPlayersStatus();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderPlayerItem = (player) => {
|
||||||
|
const avatarUrl = avatars[player.uuid];
|
||||||
|
|
||||||
|
const isIp = /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/.test(player.name);
|
||||||
|
|
||||||
|
if (isIp) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={player.uuid} className={styles.playerItem}>
|
||||||
|
{avatarUrl ? (
|
||||||
|
<img src={avatarUrl} alt="head" className={styles.playerAvatar} />
|
||||||
|
) : (
|
||||||
|
<div className={styles.missingAvatar}>Avatar non disponible</div>
|
||||||
|
)}
|
||||||
|
<span className={styles.playerName}>{player.name}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div>
|
||||||
|
<div className={styles.titlesection}>Liste Blanche</div>
|
||||||
|
<div className={styles.inputSection}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Nom d'utilisateur"
|
||||||
|
value={whiteListUsername}
|
||||||
|
onChange={(e) => setWhiteListUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={styles.sendButton}
|
||||||
|
onClick={() => handlePlayerStatus(whiteListUsername, 'whiteList', () => setWhiteListUsername(''))}
|
||||||
|
>
|
||||||
|
Envoyer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={styles.playerList}>
|
||||||
|
{whitelist.length > 0 ? (
|
||||||
|
whitelist.map(renderPlayerItem)
|
||||||
|
) : (
|
||||||
|
<div>Aucun joueur dans la liste blanche</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className={styles.titlesection}>Admins</div>
|
||||||
|
<div className={styles.inputSection}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Nom d'utilisateur"
|
||||||
|
value={adminUsername}
|
||||||
|
onChange={(e) => setAdminUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={styles.sendButton}
|
||||||
|
onClick={() => handlePlayerStatus(adminUsername, 'admin', () => setAdminUsername(''))}
|
||||||
|
>
|
||||||
|
Envoyer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={styles.playerList}>
|
||||||
|
{operators.length > 0 ? (
|
||||||
|
operators.map(renderPlayerItem)
|
||||||
|
) : (
|
||||||
|
<div>Aucun administrateur</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className={styles.titlesection}>Joueurs bannis</div>
|
||||||
|
<div className={styles.inputSection}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Nom d'utilisateur"
|
||||||
|
value={banUsername}
|
||||||
|
onChange={(e) => setBanUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={styles.sendButton}
|
||||||
|
onClick={() => handlePlayerStatus(banUsername, 'ban', () => setBanUsername(''))}
|
||||||
|
>
|
||||||
|
Envoyer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={styles.playerList}>
|
||||||
|
{bannedPlayers.length > 0 ? (
|
||||||
|
bannedPlayers.map(renderPlayerItem)
|
||||||
|
) : (
|
||||||
|
<div>Aucun joueur banni</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlayerStatus;
|
55
src/pages/PlayersStatus/PlayersStatus.module.scss
Normal file
55
src/pages/PlayersStatus/PlayersStatus.module.scss
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
.container {
|
||||||
|
margin-top: calc(var(--navbar-height) - .5rem);
|
||||||
|
width: 45rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputSection {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: .5rem;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sendButton {
|
||||||
|
margin-left: .5rem;
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
background-color: blue;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.titlesection {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerItem {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerAvatar {
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerName {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
10
src/pages/ServerDetails/FeatureSoon.jsx
Normal file
10
src/pages/ServerDetails/FeatureSoon.jsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
const FeatureSoon = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Cette fonctionnalitée sera prochainement disponible</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FeatureSoon;
|
@ -4,30 +4,33 @@ import Navbar from '../../components/navbar/Navbar';
|
|||||||
import ServerProperties from '../ServerProperties/ServerProperties';
|
import ServerProperties from '../ServerProperties/ServerProperties';
|
||||||
import ServerConsole from '../ServerConsole/ServerConsole';
|
import ServerConsole from '../ServerConsole/ServerConsole';
|
||||||
import ServerHistory from '../ServerHistory/ServerHistory';
|
import ServerHistory from '../ServerHistory/ServerHistory';
|
||||||
// import ServerPlayers from '../ServerPlayers/ServerPlayers';
|
import PlayerStatus from '../PlayersStatus/PlayersStatus';
|
||||||
// import ServerWorlds from '../ServerWorlds/ServerWorlds';
|
import FeatureSoon from './FeatureSoon';
|
||||||
// import ServerBackups from '../ServerBackups/ServerBackups';
|
|
||||||
// import ServerAccess from '../ServerAccess/ServerAccess';
|
|
||||||
import styles from './ServerDetails.module.scss';
|
import styles from './ServerDetails.module.scss';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { FaCogs, FaUserFriends, FaGlobe, FaHistory, FaClipboardList, FaSave, FaLock } from 'react-icons/fa';
|
import { FaCogs, FaUserFriends, FaGlobe, FaHistory, FaClipboardList, FaSave, FaLock } from 'react-icons/fa';
|
||||||
import { TbArrowBackUp } from 'react-icons/tb';
|
import { TbArrowBackUp } from 'react-icons/tb';
|
||||||
import { FiChevronsLeft,FiChevronsRight } from "react-icons/fi";
|
import { FiChevronsLeft, FiChevronsRight } from "react-icons/fi";
|
||||||
import NotFoundPage from '../NotFoundPage/NotFoundPage';
|
import NotFoundPage from '../NotFoundPage/NotFoundPage';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
const ServerDetails = ({ user }) => {
|
const ServerDetails = ({ user }) => {
|
||||||
const { serverName } = useParams();
|
const { serverName } = useParams();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const initialStatus = location.state?.status || false;
|
|
||||||
|
const initialStatus = location.state?.status ?? JSON.parse(localStorage.getItem('status')) ?? false;
|
||||||
const [status, setStatus] = useState(initialStatus);
|
const [status, setStatus] = useState(initialStatus);
|
||||||
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
|
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (location.state?.status !== undefined) {
|
if (location.pathname.includes('/console')) {
|
||||||
setStatus(location.state.status);
|
setStatus(initialStatus);
|
||||||
}
|
}
|
||||||
}, [location]);
|
}, [location.pathname, initialStatus]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem('status', JSON.stringify(status));
|
||||||
|
}, [status]);
|
||||||
|
|
||||||
const handleSidebarToggle = () => {
|
const handleSidebarToggle = () => {
|
||||||
setIsSidebarCollapsed(!isSidebarCollapsed);
|
setIsSidebarCollapsed(!isSidebarCollapsed);
|
||||||
@ -39,9 +42,9 @@ const ServerDetails = ({ user }) => {
|
|||||||
<div className={`${styles.container} ${isSidebarCollapsed ? styles.collapsed : ''}`}>
|
<div className={`${styles.container} ${isSidebarCollapsed ? styles.collapsed : ''}`}>
|
||||||
<div className={`${styles.sidebar} ${isSidebarCollapsed ? styles.collapsed : ''}`}>
|
<div className={`${styles.sidebar} ${isSidebarCollapsed ? styles.collapsed : ''}`}>
|
||||||
<div className={styles.menu}>
|
<div className={styles.menu}>
|
||||||
<div className={styles.toggleButton} onClick={handleSidebarToggle}>
|
<div className={styles.toggleButton} onClick={handleSidebarToggle}>
|
||||||
{isSidebarCollapsed ? <FiChevronsRight /> : <FiChevronsLeft />}
|
{isSidebarCollapsed ? <FiChevronsRight /> : <FiChevronsLeft />}
|
||||||
</div>
|
</div>
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`/server/${serverName}/options`}
|
to={`/server/${serverName}/options`}
|
||||||
className={({ isActive }) => isActive ? `${styles.menuItem} ${styles.active}` : styles.menuItem}>
|
className={({ isActive }) => isActive ? `${styles.menuItem} ${styles.active}` : styles.menuItem}>
|
||||||
@ -97,10 +100,10 @@ const ServerDetails = ({ user }) => {
|
|||||||
<Route path="options" element={<ServerProperties user={user} status={status} />} />
|
<Route path="options" element={<ServerProperties user={user} status={status} />} />
|
||||||
<Route path="console" element={<ServerConsole user={user} status={status} />} />
|
<Route path="console" element={<ServerConsole user={user} status={status} />} />
|
||||||
<Route path="history" element={<ServerHistory user={user} status={status} />} />
|
<Route path="history" element={<ServerHistory user={user} status={status} />} />
|
||||||
<Route path="players" element={<h2 >Cette fonctionnalité sera prochainement disponible.</h2>} />
|
<Route path="players" element={<PlayerStatus user={user} status={status} />} />
|
||||||
<Route path="worlds" element={<h2>Cette fonctionnalité sera prochainement disponible.</h2>} />
|
<Route path="worlds" element={<FeatureSoon user={user} status={status}/>} />
|
||||||
<Route path="backups" element={<h2>Cette fonctionnalité sera prochainement disponible.</h2>} />
|
<Route path="backups" element={<FeatureSoon user={user} status={status}/>} />
|
||||||
<Route path="access" element={<h2>Cette fonctionnalité sera prochainement disponible.</h2>} />
|
<Route path="access" element={<FeatureSoon user={user} status={status}/>}/>
|
||||||
<Route path="*" element={<NotFoundPage />} />
|
<Route path="*" element={<NotFoundPage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,12 +49,14 @@ enum serviiRequest {
|
|||||||
command = 'Command',
|
command = 'Command',
|
||||||
fetchLogs = 'FetchLogs',
|
fetchLogs = 'FetchLogs',
|
||||||
fetchHistory = 'FetchHistory',
|
fetchHistory = 'FetchHistory',
|
||||||
|
fetchPlayersStatus = 'FetchPlayersStatus',
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonToastableCalls: string[] = [
|
const nonToastableCalls: string[] = [
|
||||||
serviiRequest.fetchServers,
|
serviiRequest.fetchServers,
|
||||||
serviiRequest.fetchLogs,
|
serviiRequest.fetchLogs,
|
||||||
serviiRequest.fetchHistory,
|
serviiRequest.fetchHistory,
|
||||||
|
serviiRequest.fetchPlayersStatus,
|
||||||
];
|
];
|
||||||
|
|
||||||
class serviiApi {
|
class serviiApi {
|
||||||
@ -134,6 +136,11 @@ class serviiApi {
|
|||||||
return this.call(serviiRequest.fetchHistory, payload);
|
return this.call(serviiRequest.fetchHistory, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async fetchPlayersStatus(name: string): Promise<ApiResponse> {
|
||||||
|
const payload: ServerRequest = { token: this.token(), name: name,};
|
||||||
|
return this.call(serviiRequest.fetchPlayersStatus, payload);
|
||||||
|
}
|
||||||
|
|
||||||
public static async accountCreate(): Promise<ApiResponse> {
|
public static async accountCreate(): Promise<ApiResponse> {
|
||||||
const payload: BaseRequest = { token: this.token() };
|
const payload: BaseRequest = { token: this.token() };
|
||||||
return this.call(serviiRequest.accountCreate, payload);
|
return this.call(serviiRequest.accountCreate, payload);
|
||||||
|
Loading…
Reference in New Issue
Block a user