经理相关
This commit is contained in:
parent
ec252cec2c
commit
28051f2acd
@ -21,6 +21,7 @@ import {
|
||||
setCoinSellerStatus,
|
||||
updateBDLeaderPositionAlias,
|
||||
updateCountry,
|
||||
updateManager,
|
||||
} from "./api";
|
||||
|
||||
afterEach(() => {
|
||||
@ -60,7 +61,8 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
||||
region_id: 7,
|
||||
status: "active",
|
||||
});
|
||||
await createManager({ contact: "+63", targetUserId: "1003" });
|
||||
await createManager({ canBlockUser: false, contact: "+63", targetUserId: "1003" });
|
||||
await updateManager("1003", { canBlockUser: false, canGrantVehicle: true, contact: "+63" });
|
||||
await listHosts({ agency_id: 41, page: 1, page_size: 10, sort_by: "diamond", sort_direction: "desc" });
|
||||
await listCoinSellers({
|
||||
page: 1,
|
||||
@ -98,15 +100,16 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
||||
const [agencyUrl] = vi.mocked(fetch).mock.calls[1];
|
||||
const [managerUrl, managerInit] = vi.mocked(fetch).mock.calls[2];
|
||||
const [managerCreateUrl, managerCreateInit] = vi.mocked(fetch).mock.calls[3];
|
||||
const [hostUrl] = vi.mocked(fetch).mock.calls[4];
|
||||
const [coinSellerUrl, coinSellerInit] = vi.mocked(fetch).mock.calls[5];
|
||||
const [bdLeaderCreateUrl, bdLeaderCreateInit] = vi.mocked(fetch).mock.calls[6];
|
||||
const [bdLeaderAliasUrl, bdLeaderAliasInit] = vi.mocked(fetch).mock.calls[7];
|
||||
const [coinSellerCreateUrl, coinSellerCreateInit] = vi.mocked(fetch).mock.calls[8];
|
||||
const [coinSellerStatusUrl, coinSellerStatusInit] = vi.mocked(fetch).mock.calls[9];
|
||||
const [coinSellerStockUrl, coinSellerStockInit] = vi.mocked(fetch).mock.calls[10];
|
||||
const [coinSellerStockDebitUrl, coinSellerStockDebitInit] = vi.mocked(fetch).mock.calls[11];
|
||||
const [agencyDeleteUrl, agencyDeleteInit] = vi.mocked(fetch).mock.calls[12];
|
||||
const [managerUpdateUrl, managerUpdateInit] = vi.mocked(fetch).mock.calls[4];
|
||||
const [hostUrl] = vi.mocked(fetch).mock.calls[5];
|
||||
const [coinSellerUrl, coinSellerInit] = vi.mocked(fetch).mock.calls[6];
|
||||
const [bdLeaderCreateUrl, bdLeaderCreateInit] = vi.mocked(fetch).mock.calls[7];
|
||||
const [bdLeaderAliasUrl, bdLeaderAliasInit] = vi.mocked(fetch).mock.calls[8];
|
||||
const [coinSellerCreateUrl, coinSellerCreateInit] = vi.mocked(fetch).mock.calls[9];
|
||||
const [coinSellerStatusUrl, coinSellerStatusInit] = vi.mocked(fetch).mock.calls[10];
|
||||
const [coinSellerStockUrl, coinSellerStockInit] = vi.mocked(fetch).mock.calls[11];
|
||||
const [coinSellerStockDebitUrl, coinSellerStockDebitInit] = vi.mocked(fetch).mock.calls[12];
|
||||
const [agencyDeleteUrl, agencyDeleteInit] = vi.mocked(fetch).mock.calls[13];
|
||||
|
||||
expect(String(bdUrl)).toContain("/api/v1/admin/bds?");
|
||||
expect(String(bdUrl)).toContain("parent_leader_user_id=21");
|
||||
@ -127,7 +130,18 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
||||
expect(managerInit?.method).toBe("GET");
|
||||
expect(String(managerCreateUrl)).toContain("/api/v1/admin/managers");
|
||||
expect(managerCreateInit?.method).toBe("POST");
|
||||
expect(JSON.parse(String(managerCreateInit?.body))).toEqual({ contact: "+63", targetUserId: "1003" });
|
||||
expect(JSON.parse(String(managerCreateInit?.body))).toEqual({
|
||||
canBlockUser: false,
|
||||
contact: "+63",
|
||||
targetUserId: "1003",
|
||||
});
|
||||
expect(String(managerUpdateUrl)).toContain("/api/v1/admin/managers/1003");
|
||||
expect(managerUpdateInit?.method).toBe("PUT");
|
||||
expect(JSON.parse(String(managerUpdateInit?.body))).toEqual({
|
||||
canBlockUser: false,
|
||||
canGrantVehicle: true,
|
||||
contact: "+63",
|
||||
});
|
||||
expect(String(hostUrl)).toContain("/api/v1/admin/hosts?");
|
||||
expect(String(hostUrl)).toContain("agency_id=41");
|
||||
expect(String(hostUrl)).toContain("sort_by=diamond");
|
||||
|
||||
@ -38,6 +38,7 @@ import type {
|
||||
SalaryWalletAdjustPayload,
|
||||
SalaryWalletAdjustResultDto,
|
||||
SalaryWalletHistoryDto,
|
||||
UpdateManagerPayload,
|
||||
} from "@/shared/api/types";
|
||||
|
||||
export function listCountries(query: PageQuery = {}): Promise<ApiList<CountryDto>> {
|
||||
@ -186,6 +187,13 @@ export function createManager(payload: CreateManagerPayload): Promise<ManagerDto
|
||||
});
|
||||
}
|
||||
|
||||
export function updateManager(userId: EntityId, payload: UpdateManagerPayload): Promise<ManagerDto> {
|
||||
return apiRequest<ManagerDto, UpdateManagerPayload>(`/v1/admin/managers/${encodeURIComponent(String(userId))}`, {
|
||||
body: payload,
|
||||
method: "PUT",
|
||||
});
|
||||
}
|
||||
|
||||
export function listHosts(query: PageQuery = {}): Promise<ApiPage<HostProfileDto>> {
|
||||
const endpoint = API_ENDPOINTS.listHosts;
|
||||
return apiRequest<ApiPage<HostProfileDto>>(apiEndpointPath(API_OPERATIONS.listHosts), {
|
||||
@ -210,10 +218,13 @@ export function listSalaryWalletHistory(query: PageQuery = {}): Promise<ApiPage<
|
||||
}
|
||||
|
||||
export function adjustSalaryWallet(payload: SalaryWalletAdjustPayload): Promise<SalaryWalletAdjustResultDto> {
|
||||
return apiRequest<SalaryWalletAdjustResultDto, SalaryWalletAdjustPayload>("/v1/admin/host-org/salary-wallets/adjust", {
|
||||
body: payload,
|
||||
method: "POST",
|
||||
});
|
||||
return apiRequest<SalaryWalletAdjustResultDto, SalaryWalletAdjustPayload>(
|
||||
"/v1/admin/host-org/salary-wallets/adjust",
|
||||
{
|
||||
body: payload,
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function createBDLeader(payload: CreateBDLeaderPayload): Promise<BDProfileDto> {
|
||||
|
||||
@ -2,20 +2,42 @@ import { useMemo, useState } from "react";
|
||||
import { parseForm } from "@/shared/forms/validation";
|
||||
import { usePaginatedQuery } from "@/shared/hooks/usePaginatedQuery.js";
|
||||
import { useRegionOptions } from "@/shared/hooks/useRegionOptions.js";
|
||||
import { createManager, listManagers } from "@/features/host-org/api";
|
||||
import { createManager, listManagers, updateManager } from "@/features/host-org/api";
|
||||
import { useBDAbilities } from "@/features/host-org/permissions.js";
|
||||
import { createManagerSchema } from "@/features/host-org/schema";
|
||||
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
||||
|
||||
const pageSize = 50;
|
||||
const emptyData = { items: [], page: 1, pageSize, total: 0 };
|
||||
const emptyManagerForm = () => ({ contact: "", targetUserId: "" });
|
||||
export const managerPermissionFields = [
|
||||
"canGrantAvatarFrame",
|
||||
"canGrantVehicle",
|
||||
"canUpdateUserLevel",
|
||||
"canAddBdLeader",
|
||||
"canAddAdmin",
|
||||
"canAddSuperadmin",
|
||||
"canBlockUser",
|
||||
];
|
||||
|
||||
const emptyPermissions = () => Object.fromEntries(managerPermissionFields.map((field) => [field, true]));
|
||||
|
||||
const emptyManagerForm = () => ({ contact: "", targetUserId: "", ...emptyPermissions() });
|
||||
|
||||
const managerPermissionPayload = (form) =>
|
||||
Object.fromEntries(managerPermissionFields.map((field) => [field, form[field] !== false]));
|
||||
|
||||
const managerFormFromItem = (item) => ({
|
||||
contact: item?.contact || "",
|
||||
targetUserId: String(item?.userId || ""),
|
||||
...Object.fromEntries(managerPermissionFields.map((field) => [field, item?.[field] !== false])),
|
||||
});
|
||||
|
||||
export function useHostManagersPage() {
|
||||
const abilities = useBDAbilities();
|
||||
const { showToast } = useToast();
|
||||
const { loadingRegions, regionOptions } = useRegionOptions();
|
||||
const [activeAction, setActiveAction] = useState("");
|
||||
const [editingManager, setEditingManager] = useState(null);
|
||||
const [loadingAction, setLoadingAction] = useState("");
|
||||
const [managerForm, setManagerForm] = useState(emptyManagerForm);
|
||||
const [query, setQuery] = useState("");
|
||||
@ -68,16 +90,30 @@ export function useHostManagersPage() {
|
||||
|
||||
const submitManager = async (event) => {
|
||||
event.preventDefault();
|
||||
setLoadingAction("manager-create");
|
||||
const isEdit = activeAction === "manager-edit" && editingManager?.userId;
|
||||
setLoadingAction(isEdit ? "manager-update" : "manager-create");
|
||||
try {
|
||||
const payload = parseForm(createManagerSchema, managerForm);
|
||||
await createManager(payload);
|
||||
if (isEdit) {
|
||||
await updateManager(editingManager.userId, {
|
||||
contact: payload.contact,
|
||||
...managerPermissionPayload(payload),
|
||||
});
|
||||
showToast("经理权限已更新", "success");
|
||||
} else {
|
||||
await createManager({
|
||||
contact: payload.contact,
|
||||
targetUserId: payload.targetUserId,
|
||||
...managerPermissionPayload(payload),
|
||||
});
|
||||
showToast("经理已添加", "success");
|
||||
}
|
||||
setManagerForm(emptyManagerForm());
|
||||
setActiveAction("");
|
||||
showToast("经理已添加", "success");
|
||||
setEditingManager(null);
|
||||
await reload();
|
||||
} catch (err) {
|
||||
showToast(err.message || "添加经理失败", "error");
|
||||
showToast(err.message || (isEdit ? "更新经理权限失败" : "添加经理失败"), "error");
|
||||
} finally {
|
||||
setLoadingAction("");
|
||||
}
|
||||
@ -85,9 +121,22 @@ export function useHostManagersPage() {
|
||||
|
||||
const closeAction = () => {
|
||||
setActiveAction("");
|
||||
setEditingManager(null);
|
||||
setManagerForm(emptyManagerForm());
|
||||
};
|
||||
|
||||
const openManagerForm = () => {
|
||||
setEditingManager(null);
|
||||
setManagerForm(emptyManagerForm());
|
||||
setActiveAction("manager");
|
||||
};
|
||||
|
||||
const openManagerEdit = (item) => {
|
||||
setEditingManager(item);
|
||||
setManagerForm(managerFormFromItem(item));
|
||||
setActiveAction("manager-edit");
|
||||
};
|
||||
|
||||
return {
|
||||
abilities,
|
||||
activeAction,
|
||||
@ -96,12 +145,14 @@ export function useHostManagersPage() {
|
||||
changeStatus,
|
||||
closeAction,
|
||||
data,
|
||||
editingManager,
|
||||
error,
|
||||
loadingAction,
|
||||
loading,
|
||||
loadingRegions,
|
||||
managerForm,
|
||||
openManagerForm: () => setActiveAction("manager"),
|
||||
openManagerEdit,
|
||||
openManagerForm,
|
||||
page,
|
||||
query,
|
||||
regionId,
|
||||
|
||||
@ -104,6 +104,69 @@
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.permissionGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.permissionSwitch {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
min-height: 40px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
padding: 0 var(--space-3);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-control);
|
||||
background: var(--bg-input);
|
||||
color: var(--text-primary);
|
||||
font-size: var(--admin-font-size);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.permissionSwitch span {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.permissionList {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.permissionPill {
|
||||
display: inline-flex;
|
||||
min-height: 24px;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid var(--success-border);
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--success-surface);
|
||||
color: var(--success);
|
||||
font-size: 12px;
|
||||
font-weight: 720;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.permissionPill span {
|
||||
color: inherit;
|
||||
font-weight: 780;
|
||||
}
|
||||
|
||||
.permissionPillOff {
|
||||
border-color: var(--border);
|
||||
background: var(--bg-input);
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.rateDrawerForm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import Add from "@mui/icons-material/Add";
|
||||
import EditOutlined from "@mui/icons-material/EditOutlined";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||
import { IconButton } from "@/shared/ui/IconButton.jsx";
|
||||
import { managerStatusFilters, statusLabels } from "@/features/host-org/constants.js";
|
||||
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
|
||||
import { HostOrgPerson, hostOrgRegionName } from "@/features/host-org/components/HostOrgIdentity.jsx";
|
||||
@ -11,6 +15,16 @@ import { useHostManagersPage } from "@/features/host-org/hooks/useHostManagersPa
|
||||
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
|
||||
import styles from "@/features/host-org/host-org.module.css";
|
||||
|
||||
const managerPermissionOptions = [
|
||||
{ key: "canGrantAvatarFrame", label: "头像框" },
|
||||
{ key: "canGrantVehicle", label: "Car" },
|
||||
{ key: "canUpdateUserLevel", label: "升级等级" },
|
||||
{ key: "canAddBdLeader", label: "BD Leader" },
|
||||
{ key: "canAddAdmin", label: "Admin" },
|
||||
{ key: "canAddSuperadmin", label: "Superadmin" },
|
||||
{ key: "canBlockUser", label: "Block User" },
|
||||
];
|
||||
|
||||
const managerColumns = [
|
||||
{
|
||||
key: "userId",
|
||||
@ -30,6 +44,12 @@ const managerColumns = [
|
||||
render: (item) => item.contact || "-",
|
||||
width: "minmax(150px, 0.85fr)",
|
||||
},
|
||||
{
|
||||
key: "permissions",
|
||||
label: "权限详情",
|
||||
render: (item) => <ManagerPermissions item={item} />,
|
||||
width: "minmax(270px, 1.35fr)",
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
label: "状态",
|
||||
@ -54,11 +74,19 @@ const managerColumns = [
|
||||
render: (item) => formatMillis(item.updatedAtMs),
|
||||
width: "minmax(170px, 1fr)",
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
label: "操作",
|
||||
render: (item, _index, context) => <ManagerActions item={item} page={context?.page} />,
|
||||
resizable: false,
|
||||
width: "88px",
|
||||
},
|
||||
];
|
||||
|
||||
export function HostManagersPage() {
|
||||
const page = useHostManagersPage();
|
||||
const createDisabled = !page.abilities.canCreate;
|
||||
const isEditing = page.activeAction === "manager-edit";
|
||||
const formDisabled = isEditing ? !page.abilities.canUpdate : !page.abilities.canCreate;
|
||||
const items = page.data.items || [];
|
||||
const total = page.data.total || 0;
|
||||
const toolbarActions = [
|
||||
@ -101,7 +129,7 @@ export function HostManagersPage() {
|
||||
columns={columns}
|
||||
context={{ page, regionOptions: page.regionOptions }}
|
||||
items={items}
|
||||
minWidth="1240px"
|
||||
minWidth="1640px"
|
||||
pagination={
|
||||
total > 0
|
||||
? {
|
||||
@ -118,16 +146,16 @@ export function HostManagersPage() {
|
||||
</DataState>
|
||||
</div>
|
||||
<HostOrgActionModal
|
||||
disabled={createDisabled}
|
||||
loading={page.loadingAction === "manager-create"}
|
||||
open={page.activeAction === "manager"}
|
||||
disabled={formDisabled}
|
||||
loading={["manager-create", "manager-update"].includes(page.loadingAction)}
|
||||
open={page.activeAction === "manager" || page.activeAction === "manager-edit"}
|
||||
sectionTitle="经理信息"
|
||||
onClose={page.closeAction}
|
||||
onSubmit={page.submitManager}
|
||||
title="添加经理"
|
||||
title={isEditing ? "编辑经理权限" : "添加经理"}
|
||||
>
|
||||
<TextField
|
||||
disabled={createDisabled}
|
||||
disabled={formDisabled || isEditing}
|
||||
label="用户短 ID"
|
||||
required
|
||||
type="number"
|
||||
@ -135,11 +163,12 @@ export function HostManagersPage() {
|
||||
onChange={(event) => page.setManagerForm({ ...page.managerForm, targetUserId: event.target.value })}
|
||||
/>
|
||||
<TextField
|
||||
disabled={createDisabled}
|
||||
disabled={formDisabled}
|
||||
label="联系方式"
|
||||
value={page.managerForm.contact}
|
||||
onChange={(event) => page.setManagerForm({ ...page.managerForm, contact: event.target.value })}
|
||||
/>
|
||||
<PermissionSwitchGrid disabled={formDisabled} form={page.managerForm} onChange={page.setManagerForm} />
|
||||
</HostOrgActionModal>
|
||||
</section>
|
||||
);
|
||||
@ -164,3 +193,59 @@ function ManagerStatus({ item }) {
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function ManagerPermissions({ item }) {
|
||||
return (
|
||||
<div className={styles.permissionList}>
|
||||
{managerPermissionOptions.map((permission) => {
|
||||
const enabled = item?.[permission.key] !== false;
|
||||
return (
|
||||
<span
|
||||
className={
|
||||
enabled ? styles.permissionPill : `${styles.permissionPill} ${styles.permissionPillOff}`
|
||||
}
|
||||
key={permission.key}
|
||||
>
|
||||
{permission.label}
|
||||
<span>{enabled ? "开" : "关"}</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ManagerActions({ item, page }) {
|
||||
const disabled = !page?.abilities?.canUpdate;
|
||||
return (
|
||||
<div className={styles.rowActions}>
|
||||
<Tooltip title={disabled ? "无编辑权限" : "编辑权限"}>
|
||||
<span>
|
||||
<IconButton disabled={disabled} label="编辑权限" onClick={() => page.openManagerEdit(item)}>
|
||||
<EditOutlined fontSize="small" />
|
||||
</IconButton>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PermissionSwitchGrid({ disabled, form, onChange }) {
|
||||
return (
|
||||
<div className={styles.permissionGrid}>
|
||||
{managerPermissionOptions.map((permission) => (
|
||||
<label className={styles.permissionSwitch} key={permission.key}>
|
||||
<span>{permission.label}</span>
|
||||
<AdminSwitch
|
||||
checked={form?.[permission.key] !== false}
|
||||
checkedLabel="开"
|
||||
disabled={disabled}
|
||||
label={permission.label}
|
||||
uncheckedLabel="关"
|
||||
onChange={(event) => onChange({ ...form, [permission.key]: event.target.checked })}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -18,7 +18,10 @@ const userIdSchema = z.preprocess(
|
||||
}
|
||||
return String(value).trim();
|
||||
},
|
||||
z.string().min(1, "请输入有效用户短 ID").regex(/^[1-9]\d*$/, "请输入有效用户短 ID"),
|
||||
z
|
||||
.string()
|
||||
.min(1, "请输入有效用户短 ID")
|
||||
.regex(/^[1-9]\d*$/, "请输入有效用户短 ID"),
|
||||
);
|
||||
const entityIdSchema = z.preprocess(
|
||||
(value) => {
|
||||
@ -106,6 +109,13 @@ export const createCoinSellerSchema = commandContactSchema.extend({
|
||||
});
|
||||
|
||||
export const createManagerSchema = z.object({
|
||||
canAddAdmin: z.boolean().optional().default(true),
|
||||
canAddBdLeader: z.boolean().optional().default(true),
|
||||
canAddSuperadmin: z.boolean().optional().default(true),
|
||||
canBlockUser: z.boolean().optional().default(true),
|
||||
canGrantAvatarFrame: z.boolean().optional().default(true),
|
||||
canGrantVehicle: z.boolean().optional().default(true),
|
||||
canUpdateUserLevel: z.boolean().optional().default(true),
|
||||
contact: z.string().trim().max(128, "联系方式不能超过 128 个字符").optional().default(""),
|
||||
targetUserId: userIdSchema,
|
||||
});
|
||||
|
||||
@ -561,6 +561,13 @@ export interface AgencyDto {
|
||||
export interface ManagerDto {
|
||||
avatar?: string;
|
||||
bdLeaderCount?: number;
|
||||
canAddAdmin?: boolean;
|
||||
canAddBdLeader?: boolean;
|
||||
canAddSuperadmin?: boolean;
|
||||
canBlockUser?: boolean;
|
||||
canGrantAvatarFrame?: boolean;
|
||||
canGrantVehicle?: boolean;
|
||||
canUpdateUserLevel?: boolean;
|
||||
contact?: string;
|
||||
createdAtMs?: number;
|
||||
displayUserId?: string;
|
||||
@ -574,10 +581,28 @@ export interface ManagerDto {
|
||||
}
|
||||
|
||||
export interface CreateManagerPayload {
|
||||
canAddAdmin?: boolean;
|
||||
canAddBdLeader?: boolean;
|
||||
canAddSuperadmin?: boolean;
|
||||
canBlockUser?: boolean;
|
||||
canGrantAvatarFrame?: boolean;
|
||||
canGrantVehicle?: boolean;
|
||||
canUpdateUserLevel?: boolean;
|
||||
contact?: string;
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
export interface UpdateManagerPayload {
|
||||
canAddAdmin?: boolean;
|
||||
canAddBdLeader?: boolean;
|
||||
canAddSuperadmin?: boolean;
|
||||
canBlockUser?: boolean;
|
||||
canGrantAvatarFrame?: boolean;
|
||||
canGrantVehicle?: boolean;
|
||||
canUpdateUserLevel?: boolean;
|
||||
contact?: string;
|
||||
}
|
||||
|
||||
export interface HostProfileDto {
|
||||
avatar?: string;
|
||||
createdAtMs?: number;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user