chore: update theme berry
This commit is contained in:
parent
1d7470d6ad
commit
704ec1a827
Binary file not shown.
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 4.2 KiB |
@ -3,186 +3,186 @@ export const CHANNEL_OPTIONS = {
|
|||||||
key: 1,
|
key: 1,
|
||||||
text: 'OpenAI',
|
text: 'OpenAI',
|
||||||
value: 1,
|
value: 1,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
14: {
|
14: {
|
||||||
key: 14,
|
key: 14,
|
||||||
text: 'Anthropic Claude',
|
text: 'Anthropic Claude',
|
||||||
value: 14,
|
value: 14,
|
||||||
color: 'info'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
key: 3,
|
key: 3,
|
||||||
text: 'Azure OpenAI',
|
text: 'Azure OpenAI',
|
||||||
value: 3,
|
value: 3,
|
||||||
color: 'secondary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
11: {
|
11: {
|
||||||
key: 11,
|
key: 11,
|
||||||
text: 'Google PaLM2',
|
text: 'Google PaLM2',
|
||||||
value: 11,
|
value: 11,
|
||||||
color: 'orange'
|
color: 'warning'
|
||||||
},
|
},
|
||||||
24: {
|
24: {
|
||||||
key: 24,
|
key: 24,
|
||||||
text: 'Google Gemini',
|
text: 'Google Gemini',
|
||||||
value: 24,
|
value: 24,
|
||||||
color: 'orange'
|
color: 'warning'
|
||||||
},
|
},
|
||||||
28: {
|
28: {
|
||||||
key: 28,
|
key: 28,
|
||||||
text: 'Mistral AI',
|
text: 'Mistral AI',
|
||||||
value: 28,
|
value: 28,
|
||||||
color: 'orange'
|
color: 'warning'
|
||||||
},
|
},
|
||||||
15: {
|
15: {
|
||||||
key: 15,
|
key: 15,
|
||||||
text: '百度文心千帆',
|
text: '百度文心千帆',
|
||||||
value: 15,
|
value: 15,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
17: {
|
17: {
|
||||||
key: 17,
|
key: 17,
|
||||||
text: '阿里通义千问',
|
text: '阿里通义千问',
|
||||||
value: 17,
|
value: 17,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
18: {
|
18: {
|
||||||
key: 18,
|
key: 18,
|
||||||
text: '讯飞星火认知',
|
text: '讯飞星火认知',
|
||||||
value: 18,
|
value: 18,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
16: {
|
16: {
|
||||||
key: 16,
|
key: 16,
|
||||||
text: '智谱 ChatGLM',
|
text: '智谱 ChatGLM',
|
||||||
value: 16,
|
value: 16,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
19: {
|
19: {
|
||||||
key: 19,
|
key: 19,
|
||||||
text: '360 智脑',
|
text: '360 智脑',
|
||||||
value: 19,
|
value: 19,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
25: {
|
25: {
|
||||||
key: 25,
|
key: 25,
|
||||||
text: 'Moonshot AI',
|
text: 'Moonshot AI',
|
||||||
value: 25,
|
value: 25,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
23: {
|
23: {
|
||||||
key: 23,
|
key: 23,
|
||||||
text: '腾讯混元',
|
text: '腾讯混元',
|
||||||
value: 23,
|
value: 23,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
26: {
|
26: {
|
||||||
key: 26,
|
key: 26,
|
||||||
text: '百川大模型',
|
text: '百川大模型',
|
||||||
value: 26,
|
value: 26,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
27: {
|
27: {
|
||||||
key: 27,
|
key: 27,
|
||||||
text: 'MiniMax',
|
text: 'MiniMax',
|
||||||
value: 27,
|
value: 27,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
29: {
|
29: {
|
||||||
key: 29,
|
key: 29,
|
||||||
text: 'Groq',
|
text: 'Groq',
|
||||||
value: 29,
|
value: 29,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
30: {
|
30: {
|
||||||
key: 30,
|
key: 30,
|
||||||
text: 'Ollama',
|
text: 'Ollama',
|
||||||
value: 30,
|
value: 30,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
31: {
|
31: {
|
||||||
key: 31,
|
key: 31,
|
||||||
text: '零一万物',
|
text: '零一万物',
|
||||||
value: 31,
|
value: 31,
|
||||||
color: 'default'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
8: {
|
8: {
|
||||||
key: 8,
|
key: 8,
|
||||||
text: '自定义渠道',
|
text: '自定义渠道',
|
||||||
value: 8,
|
value: 8,
|
||||||
color: 'primary'
|
color: 'error'
|
||||||
},
|
},
|
||||||
22: {
|
22: {
|
||||||
key: 22,
|
key: 22,
|
||||||
text: '知识库:FastGPT',
|
text: '知识库:FastGPT',
|
||||||
value: 22,
|
value: 22,
|
||||||
color: 'default'
|
color: 'success'
|
||||||
},
|
},
|
||||||
21: {
|
21: {
|
||||||
key: 21,
|
key: 21,
|
||||||
text: '知识库:AI Proxy',
|
text: '知识库:AI Proxy',
|
||||||
value: 21,
|
value: 21,
|
||||||
color: 'purple'
|
color: 'success'
|
||||||
},
|
},
|
||||||
20: {
|
20: {
|
||||||
key: 20,
|
key: 20,
|
||||||
text: '代理:OpenRouter',
|
text: '代理:OpenRouter',
|
||||||
value: 20,
|
value: 20,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
key: 2,
|
key: 2,
|
||||||
text: '代理:API2D',
|
text: '代理:API2D',
|
||||||
value: 2,
|
value: 2,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
5: {
|
5: {
|
||||||
key: 5,
|
key: 5,
|
||||||
text: '代理:OpenAI-SB',
|
text: '代理:OpenAI-SB',
|
||||||
value: 5,
|
value: 5,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
7: {
|
7: {
|
||||||
key: 7,
|
key: 7,
|
||||||
text: '代理:OhMyGPT',
|
text: '代理:OhMyGPT',
|
||||||
value: 7,
|
value: 7,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
10: {
|
10: {
|
||||||
key: 10,
|
key: 10,
|
||||||
text: '代理:AI Proxy',
|
text: '代理:AI Proxy',
|
||||||
value: 10,
|
value: 10,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
key: 4,
|
key: 4,
|
||||||
text: '代理:CloseAI',
|
text: '代理:CloseAI',
|
||||||
value: 4,
|
value: 4,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
6: {
|
6: {
|
||||||
key: 6,
|
key: 6,
|
||||||
text: '代理:OpenAI Max',
|
text: '代理:OpenAI Max',
|
||||||
value: 6,
|
value: 6,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
9: {
|
9: {
|
||||||
key: 9,
|
key: 9,
|
||||||
text: '代理:AI.LS',
|
text: '代理:AI.LS',
|
||||||
value: 9,
|
value: 9,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
12: {
|
12: {
|
||||||
key: 12,
|
key: 12,
|
||||||
text: '代理:API2GPT',
|
text: '代理:API2GPT',
|
||||||
value: 12,
|
value: 12,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
},
|
},
|
||||||
13: {
|
13: {
|
||||||
key: 13,
|
key: 13,
|
||||||
text: '代理:AIGC2D',
|
text: '代理:AIGC2D',
|
||||||
value: 13,
|
value: 13,
|
||||||
color: 'primary'
|
color: 'success'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -180,7 +180,7 @@ const LoginForm = ({ ...others }) => {
|
|||||||
{({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => (
|
{({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values }) => (
|
||||||
<form noValidate onSubmit={handleSubmit} {...others}>
|
<form noValidate onSubmit={handleSubmit} {...others}>
|
||||||
<FormControl fullWidth error={Boolean(touched.username && errors.username)} sx={{ ...theme.typography.customInput }}>
|
<FormControl fullWidth error={Boolean(touched.username && errors.username)} sx={{ ...theme.typography.customInput }}>
|
||||||
<InputLabel htmlFor="outlined-adornment-username-login">用户名</InputLabel>
|
<InputLabel htmlFor="outlined-adornment-username-login">用户名 / 邮箱</InputLabel>
|
||||||
<OutlinedInput
|
<OutlinedInput
|
||||||
id="outlined-adornment-username-login"
|
id="outlined-adornment-username-login"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -3,6 +3,19 @@ import Label from "ui-component/Label";
|
|||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from "@mui/material/Divider";
|
||||||
|
|
||||||
|
function name2color(name) {
|
||||||
|
switch (name) {
|
||||||
|
case "default":
|
||||||
|
return "info";
|
||||||
|
case "vip":
|
||||||
|
return "warning"
|
||||||
|
case "svip":
|
||||||
|
return "error"
|
||||||
|
default:
|
||||||
|
return "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const GroupLabel = ({ group }) => {
|
const GroupLabel = ({ group }) => {
|
||||||
let groups = [];
|
let groups = [];
|
||||||
if (group === "") {
|
if (group === "") {
|
||||||
@ -14,7 +27,7 @@ const GroupLabel = ({ group }) => {
|
|||||||
return (
|
return (
|
||||||
<Stack divider={<Divider orientation="vertical" flexItem />} spacing={0.5}>
|
<Stack divider={<Divider orientation="vertical" flexItem />} spacing={0.5}>
|
||||||
{groups.map((group, index) => {
|
{groups.map((group, index) => {
|
||||||
return <Label key={index}>{group}</Label>;
|
return <Label key={index} color={name2color(group)}>{group}</Label>;
|
||||||
})}
|
})}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
@ -10,6 +10,7 @@ const ChannelTableHead = () => {
|
|||||||
<TableCell>类型</TableCell>
|
<TableCell>类型</TableCell>
|
||||||
<TableCell>状态</TableCell>
|
<TableCell>状态</TableCell>
|
||||||
<TableCell>响应时间</TableCell>
|
<TableCell>响应时间</TableCell>
|
||||||
|
<TableCell>已消耗</TableCell>
|
||||||
<TableCell>余额</TableCell>
|
<TableCell>余额</TableCell>
|
||||||
<TableCell>优先级</TableCell>
|
<TableCell>优先级</TableCell>
|
||||||
<TableCell>操作</TableCell>
|
<TableCell>操作</TableCell>
|
||||||
|
@ -170,6 +170,9 @@ export default function ChannelTableRow({
|
|||||||
handle_action={handleResponseTime}
|
handle_action={handleResponseTime}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{renderNumber(item.used_quota)}
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={"点击更新余额"}
|
title={"点击更新余额"}
|
||||||
|
@ -193,20 +193,14 @@ export default function ChannelPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={5}>
|
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={2.5}>
|
||||||
<Typography variant="h4">渠道</Typography>
|
<Typography variant="h4">渠道</Typography>
|
||||||
|
|
||||||
<Button variant="contained" color="primary" startIcon={<IconPlus />} onClick={() => handleOpenModal(0)}>
|
<Button variant="contained" color="primary" startIcon={<IconPlus />} onClick={() => handleOpenModal(0)}>
|
||||||
新建渠道
|
新建渠道
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack mb={5}>
|
|
||||||
<Alert severity="info">
|
|
||||||
OpenAI 渠道已经不再支持通过 key 获取余额,因此余额显示为 0。对于支持的渠道类型,请点击余额进行刷新。
|
|
||||||
</Alert>
|
|
||||||
</Stack>
|
|
||||||
<Card>
|
<Card>
|
||||||
<Box component="form" onSubmit={searchChannels} noValidate>
|
<Box component="form" onSubmit={searchChannels} noValidate sx={{marginTop: 2}}>
|
||||||
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} placeholder={'搜索渠道的 ID,名称和密钥 ...'} />
|
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} placeholder={'搜索渠道的 ID,名称和密钥 ...'} />
|
||||||
</Box>
|
</Box>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
@ -220,7 +214,7 @@ export default function ChannelPage() {
|
|||||||
>
|
>
|
||||||
<Container>
|
<Container>
|
||||||
{matchUpMd ? (
|
{matchUpMd ? (
|
||||||
<ButtonGroup variant="outlined" aria-label="outlined small primary button group">
|
<ButtonGroup variant="outlined" aria-label="outlined small primary button group" sx={{marginBottom: 2}}>
|
||||||
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -102,11 +102,11 @@ export default function Log() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={5}>
|
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={2.5}>
|
||||||
<Typography variant="h4">日志</Typography>
|
<Typography variant="h4">日志</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Card>
|
<Card>
|
||||||
<Box component="form" onSubmit={searchLogs} noValidate>
|
<Box component="form" onSubmit={searchLogs} noValidate sx={{marginTop: 2}}>
|
||||||
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} userIsAdmin={userIsAdmin} />
|
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} userIsAdmin={userIsAdmin} />
|
||||||
</Box>
|
</Box>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
@ -119,7 +119,7 @@ export default function Log() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container>
|
<Container>
|
||||||
<ButtonGroup variant="outlined" aria-label="outlined small primary button group">
|
<ButtonGroup variant="outlined" aria-label="outlined small primary button group" sx={{marginBottom: 2}}>
|
||||||
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
||||||
刷新/清除搜索条件
|
刷新/清除搜索条件
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -141,7 +141,7 @@ export default function Redemption() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={5}>
|
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={2.5}>
|
||||||
<Typography variant="h4">兑换</Typography>
|
<Typography variant="h4">兑换</Typography>
|
||||||
|
|
||||||
<Button variant="contained" color="primary" startIcon={<IconPlus />} onClick={() => handleOpenModal(0)}>
|
<Button variant="contained" color="primary" startIcon={<IconPlus />} onClick={() => handleOpenModal(0)}>
|
||||||
@ -149,7 +149,7 @@ export default function Redemption() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Card>
|
<Card>
|
||||||
<Box component="form" onSubmit={searchRedemptions} noValidate>
|
<Box component="form" onSubmit={searchRedemptions} noValidate sx={{marginTop: 2}}>
|
||||||
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} placeholder={'搜索兑换码的ID和名称...'} />
|
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} placeholder={'搜索兑换码的ID和名称...'} />
|
||||||
</Box>
|
</Box>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
@ -162,7 +162,7 @@ export default function Redemption() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container>
|
<Container>
|
||||||
<ButtonGroup variant="outlined" aria-label="outlined small primary button group">
|
<ButtonGroup variant="outlined" aria-label="outlined small primary button group" sx={{marginBottom: 2}}>
|
||||||
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -141,9 +141,8 @@ export default function Token() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={5}>
|
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={2.5}>
|
||||||
<Typography variant="h4">令牌</Typography>
|
<Typography variant="h4">令牌</Typography>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
@ -155,13 +154,13 @@ export default function Token() {
|
|||||||
新建令牌
|
新建令牌
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack mb={5}>
|
<Stack mb={2}>
|
||||||
<Alert severity="info">
|
<Alert severity="info">
|
||||||
将 OpenAI API 基础地址 https://api.openai.com 替换为 <b>{siteInfo.server_address}</b>,复制下面的密钥即可使用
|
将 OpenAI API 基础地址 https://api.openai.com 替换为 <b>{siteInfo.server_address}</b>,复制下面的密钥即可使用
|
||||||
</Alert>
|
</Alert>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Card>
|
<Card>
|
||||||
<Box component="form" onSubmit={searchTokens} noValidate>
|
<Box component="form" onSubmit={searchTokens} noValidate sx={{marginTop: 2}}>
|
||||||
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} placeholder={'搜索令牌的名称...'} />
|
<TableToolBar filterName={searchKeyword} handleFilterName={handleSearchKeyword} placeholder={'搜索令牌的名称...'} />
|
||||||
</Box>
|
</Box>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
@ -174,7 +173,7 @@ export default function Token() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container>
|
<Container>
|
||||||
<ButtonGroup variant="outlined" aria-label="outlined small primary button group">
|
<ButtonGroup variant="outlined" aria-label="outlined small primary button group" sx={{marginBottom: 2}}>
|
||||||
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -139,7 +139,7 @@ export default function Users() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={5}>
|
<Stack direction="row" alignItems="center" justifyContent="space-between" mb={2.5}>
|
||||||
<Typography variant="h4">用户</Typography>
|
<Typography variant="h4">用户</Typography>
|
||||||
|
|
||||||
<Button variant="contained" color="primary" startIcon={<IconPlus />} onClick={() => handleOpenModal(0)}>
|
<Button variant="contained" color="primary" startIcon={<IconPlus />} onClick={() => handleOpenModal(0)}>
|
||||||
@ -147,7 +147,7 @@ export default function Users() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Card>
|
<Card>
|
||||||
<Box component="form" onSubmit={searchUsers} noValidate>
|
<Box component="form" onSubmit={searchUsers} noValidate sx={{marginTop: 2}}>
|
||||||
<TableToolBar
|
<TableToolBar
|
||||||
filterName={searchKeyword}
|
filterName={searchKeyword}
|
||||||
handleFilterName={handleSearchKeyword}
|
handleFilterName={handleSearchKeyword}
|
||||||
@ -164,7 +164,7 @@ export default function Users() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container>
|
<Container>
|
||||||
<ButtonGroup variant="outlined" aria-label="outlined small primary button group">
|
<ButtonGroup variant="outlined" aria-label="outlined small primary button group" sx={{marginBottom: 2}}>
|
||||||
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
<Button onClick={handleRefresh} startIcon={<IconRefresh width={'18px'} />}>
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
|
Loading…
Reference in New Issue
Block a user