Навалил запросы в api, поиск, подразбил на компоненты

This commit is contained in:
Dhaverd 2026-05-08 00:58:27 +08:00
parent c288fb92fa
commit 6cda405c0b
7 changed files with 198 additions and 58 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.DS_Store
.idea
/node_modules/
*.tsbuildinfo
# React Router
/.react-router/
/build/
package-lock.json

44
app/api.ts Normal file
View File

@ -0,0 +1,44 @@
import axios, {type AxiosResponse} from "axios";
import {ResponseError} from "~/classes/responseError";
import {BlacklistItem} from "~/classes/blacklistItem";
const apiUrl = import.meta.env.VITE_API_URL;
const instance = axios.create({
baseURL: apiUrl
});
const getReportData = (data: AxiosResponse<any, any, {}>): BlacklistItem[] => {
let result = data.data;
let blacklistItems: BlacklistItem[] = [];
for (const [key, value] of Object.entries(result)) {
let blacklistItem: BlacklistItem = new BlacklistItem(
value.link,
value.nicknames,
value.comments,
value.afk,
value.cheater,
value.griefer,
value.toxic,
value.useless,
);
blacklistItems.push(blacklistItem);
}
return blacklistItems;
}
export async function getAll(searchText?: string): Promise<BlacklistItem[] | ResponseError | undefined> {
let result: BlacklistItem[] | ResponseError | undefined;
let route: string;
if (searchText !== ''){
route = '/blacklist/search?text=' + searchText;
} else {
route = '/blacklist/all';
}
result = await instance.get(route).then((data: AxiosResponse<any, any, {}>) => {
return getReportData(data);
}).catch(err => {
console.log(err);
return new ResponseError(err.code, err.message, err.status);
});
return result;
}

View File

@ -0,0 +1,74 @@
import type {Theme} from "@mui/material";
import {createTheme} from "@mui/material";
import {outlinedInputClasses} from "@mui/material";
const customTheme = (outerTheme: Theme) =>
createTheme({
palette: {
mode: outerTheme.palette.mode,
},
components: {
MuiTextField: {
styleOverrides: {
root: {
'--TextField-brandBorderColor': '#E0E3E7',
'--TextField-brandBorderHoverColor': '#B2BAC2',
'--TextField-brandBorderFocusedColor': '#6F7E8C',
'& label': {
color: 'var(--TextField-brandBorderColor)',
},
'& label.Mui-focused': {
color: 'var(--TextField-brandBorderColor)',
},
'& input': {
color: 'var(--TextField-brandBorderColor)',
borderColor: 'var(--TextField-brandBorderColor)',
},
},
},
},
MuiOutlinedInput: {
styleOverrides: {
notchedOutline: {
borderColor: 'var(--TextField-brandBorderColor)',
},
root: {
[`&:hover .${outlinedInputClasses.notchedOutline}`]: {
borderColor: 'var(--TextField-brandBorderHoverColor)',
},
[`&.Mui-focused .${outlinedInputClasses.notchedOutline}`]: {
borderColor: 'var(--TextField-brandBorderFocusedColor)',
},
},
},
},
MuiDivider: {
styleOverrides: {
root: {
'--TextField-brandBorderColor': '#E0E3E7',
borderColor: 'var(--TextField-brandBorderColor)',
borderWidth: 1,
width: '95%',
}
}
},
MuiButton: {
styleOverrides: {
root: {
'--TextField-brandBorderColor': '#E0E3E7',
'--variant-outlinedBorder': '#b5b5b5',
'--variant-containedBg': '#b5b5b5',
'--variant-custom-textBg': 'rgba(246,246,248,0.06)',
color: 'var(--TextField-brandBorderColor)',
'&:hover': {
backgroundColor: 'var(--variant-custom-textBg)',
borderColor: 'var(--variant-custom-textBg)',
boxShadow: 'none',
},
}
}
}
}
});
export default customTheme;

View File

@ -18,4 +18,18 @@
.bg-dark {
background-color: #282c34;
height: 100%;
}
.list-container {
justify-items: center;
}
.item-list {
display: flex;
flex-direction: column;
width: 100%;
}
.loader {
text-align: center;
}

View File

@ -0,0 +1,22 @@
import {BlacklistItem} from "~/classes/blacklistItem";
import {Button, CircularProgress} from "@mui/material";
export default function ItemList({ isFetching, blacklistItems }: { isFetching: boolean, blacklistItems: BlacklistItem[] }) {
if (isFetching){
return(
<div className="loader">
<CircularProgress aria-label="Loading…" />
</div>
)
} else {
return (
<div className="list-container">
<div className="item-list">
{blacklistItems.map((item) => {
return <Button variant="text">{item.link}</Button>
})}
</div>
</div>
)
}
}

View File

@ -1,71 +1,42 @@
import {useState} from "react";
import {useEffect, useState} from "react";
import '../assets/styles/home.css';
import {
createTheme,
Divider,
outlinedInputClasses,
TextField,
type Theme,
ThemeProvider,
useTheme
} from "@mui/material";
import {getAll} from '~/api';
import customTheme from "~/assets/mui/themes/home";
import {BlacklistItem} from "~/classes/blacklistItem";
import {ResponseError} from "~/classes/responseError";
import ItemList from "~/components/itemList";
export default function Home() {
const [searchText, setSearchText] = useState('');
const outerTheme = useTheme();
const customTheme = (outerTheme: Theme) =>
createTheme({
palette: {
mode: outerTheme.palette.mode,
},
components: {
MuiTextField: {
styleOverrides: {
root: {
'--TextField-brandBorderColor': '#E0E3E7',
'--TextField-brandBorderHoverColor': '#B2BAC2',
'--TextField-brandBorderFocusedColor': '#6F7E8C',
'& label': {
color: 'var(--TextField-brandBorderColor)',
},
'& label.Mui-focused': {
color: 'var(--TextField-brandBorderColor)',
},
'& input': {
color: 'var(--TextField-brandBorderColor)',
borderColor: 'var(--TextField-brandBorderColor)',
},
},
},
},
MuiOutlinedInput: {
styleOverrides: {
notchedOutline: {
borderColor: 'var(--TextField-brandBorderColor)',
},
root: {
[`&:hover .${outlinedInputClasses.notchedOutline}`]: {
borderColor: 'var(--TextField-brandBorderHoverColor)',
},
[`&.Mui-focused .${outlinedInputClasses.notchedOutline}`]: {
borderColor: 'var(--TextField-brandBorderFocusedColor)',
},
},
},
},
MuiDivider: {
styleOverrides: {
root: {
'--TextField-brandBorderColor': '#E0E3E7',
borderColor: 'var(--TextField-brandBorderColor)',
borderWidth: 1,
width: '95%',
}
}
const [searchText, setSearchText] = useState('');
const [isFetchingList, setIsFetchingList] = useState(false);
const [blacklistItems, setBlacklistItems] = useState<BlacklistItem[]>([]);
useEffect(() => {
let isIgnore = false;
setIsFetchingList(true);
getAll(searchText).then((res: BlacklistItem[] | ResponseError | undefined)=> {
if (!isIgnore) {
if (res instanceof Array){
setBlacklistItems(res);
} else if (res instanceof ResponseError) {
// TODO error handler
console.log(res);
}
setIsFetchingList(false);
}
});
const outerTheme = useTheme();
return () => {
isIgnore = true; // При следующем изменении someValue старый запрос будет проигнорирован
};
}, [searchText]);
function handleSearch(text: string) {
setSearchText(text);
@ -87,9 +58,14 @@ export default function Home() {
<div style={{justifyItems: 'center'}}>
<Divider sx={{marginTop: '10px', marginBottom: '10px'}}/>
</div>
<div className="list-container">
<p>{searchText} //</p>
</div>
<ItemList isFetching={isFetchingList} blacklistItems={blacklistItems}></ItemList>
{/*<div className="list-container">*/}
{/* <div className="item-list">*/}
{/* {blacklistItems.map((item, i) => {*/}
{/* return <Button variant="text">{item.link}</Button>*/}
{/* })}*/}
{/* </div>*/}
{/*</div>*/}
</div>
</ThemeProvider>
);

View File

@ -14,6 +14,7 @@
"@mui/material": "^7.3.7",
"@react-router/express": "7.10.1",
"@react-router/node": "7.10.1",
"axios": "^1.13.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"compression": "^1.8.1",