556 lines
19 KiB
Svelte
556 lines
19 KiB
Svelte
<script>
|
||
import { browser } from "$app/environment";
|
||
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
|
||
import { makePost } from "$lib/tools/requests/requests.js";
|
||
import { toValidNumberFormat } from "$lib/tools/strings/Strings.js";
|
||
import { sayError } from "$lib/tools/toaster/Toaster.js";
|
||
import { redirect } from "$lib/tools/url/URLTools.js";
|
||
import CopyButton from "$lib/ui-components/CopyButton.svelte";
|
||
import Pagination from "$lib/ui-components/pagination.svelte";
|
||
|
||
export let data;
|
||
let { userData } = data;
|
||
let { token } = data;
|
||
|
||
let userInfoReloading = false;
|
||
|
||
const orderStatusMap = {
|
||
"1": "Ожидает исполнения",
|
||
"2": "Время заявки истекло",
|
||
"3": "Исполнена",
|
||
};
|
||
|
||
async function deleteUser() {
|
||
const result = await makePost(
|
||
"deleteUser",
|
||
{
|
||
token: token,
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
if (result.status === 401) {
|
||
sayError("Данные авторизации устарели");
|
||
// window.location.href = "/";
|
||
redirect("/admin/");
|
||
return;
|
||
}
|
||
if (result.error) {
|
||
sayError("Не удалось удалить пользователя");
|
||
return;
|
||
}
|
||
redirect("/admin/userslist");
|
||
}
|
||
|
||
async function reloadUserInfo() {
|
||
userInfoReloading = true;
|
||
const userResult = await makePost(
|
||
"getUser",
|
||
{
|
||
token: token,
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
if (userResult.status === 401) {
|
||
userInfoReloading = false;
|
||
sayError("Данные авторизации устарели");
|
||
redirect("/admin/");
|
||
return;
|
||
}
|
||
if (userResult.error) {
|
||
userInfoReloading = false;
|
||
sayError("Не удалось настроить пользователя");
|
||
return;
|
||
}
|
||
userData = userResult.data;
|
||
userInfoReloading = false;
|
||
}
|
||
|
||
let userOrders = [];
|
||
let numPagesUserOrders = 1;
|
||
let currentPageUserOrders = 1;
|
||
let disablePagesUserOrders = false;
|
||
let ordersFilter = "1";
|
||
async function getUserOrders() {
|
||
disablePagesUserOrders = true;
|
||
const result = await makePost(
|
||
"admin/getUserOrders",
|
||
{
|
||
token: token,
|
||
page: currentPageUserOrders,
|
||
// orderBy: "uuid",
|
||
statuses_to_send: Number(ordersFilter),
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
if (result.status === 401) {
|
||
sayError("Данные авторизации устарели");
|
||
// window.location.href = "/";
|
||
redirect("/admin/");
|
||
disablePagesUserOrders = false;
|
||
return;
|
||
}
|
||
if (result.error) {
|
||
sayError("Не удалось получить ордеры пользователя");
|
||
disablePagesUserOrders = false;
|
||
return;
|
||
}
|
||
userOrders = result.data.data;
|
||
if (!Array.isArray(userOrders)) userOrders = [];
|
||
numPagesUserOrders = Number(result.data?.pages);
|
||
if (numPagesUserOrders === 0) numPagesUserOrders = 1;
|
||
console.log(result.data);
|
||
disablePagesUserOrders = false;
|
||
}
|
||
|
||
let reqsCurrentPage = 1;
|
||
let reqsMaxPage = 1;
|
||
let userRequisites = [];
|
||
async function getUserRequisites() {
|
||
const result = await makePost(
|
||
"admin/getRequisite",
|
||
{
|
||
page: Number(reqsCurrentPage),
|
||
token: token,
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
if (result.status === 401) {
|
||
sayError("Данные авторизации устарели");
|
||
redirect("/admin/");
|
||
return;
|
||
}
|
||
if (result.error) {
|
||
sayError("Не удалось получить реквизиты пользователя");
|
||
return;
|
||
}
|
||
userRequisites = result.data.data;
|
||
if (!Array.isArray(userDeposits)) userRequisites = [];
|
||
reqsMaxPage = Number(result.data.pages);
|
||
console.log(result.data);
|
||
}
|
||
|
||
let userDeposits = [];
|
||
let numOfPagesUserDeposits = 1;
|
||
let currentPageUserDeposits = 1;
|
||
let disablePagesUserDeposits = false;
|
||
const depoStatusMap = {
|
||
"0": "Активна",
|
||
"1": "Завершена",
|
||
"2": "Отклонена",
|
||
};
|
||
///api/v1/admin/getUserDeposits
|
||
async function getUserDeposits() {
|
||
disablePagesUserDeposits = true;
|
||
const result = await makePost(
|
||
"admin/getUserDeposits",
|
||
{
|
||
token: token,
|
||
page: 1,
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
if (result.status === 401) {
|
||
sayError("Данные авторизации устарели");
|
||
redirect("/admin/");
|
||
disablePagesUserDeposits = false;
|
||
return;
|
||
}
|
||
if (result.error) {
|
||
sayError("Не удалось получить депозиты пользователя");
|
||
disablePagesUserDeposits = false;
|
||
return;
|
||
}
|
||
userDeposits = result.data.deposits;
|
||
if (!Array.isArray(userDeposits)) userDeposits = [];
|
||
numPagesUserOrders = result.data.pages;
|
||
disablePagesUserDeposits = false;
|
||
// console.log(result.data, 'depos');
|
||
}
|
||
|
||
$: if (browser || Number(ordersFilter) > -999) getUserOrders();
|
||
|
||
if (browser) {
|
||
getUserOrders();
|
||
getUserRequisites();
|
||
getUserDeposits();
|
||
}
|
||
|
||
let findFieldValue = "";
|
||
let showLoadingFind = false;
|
||
let showFoundedOrderWindow = false;
|
||
async function findOrder() {
|
||
showLoadingFind = true;
|
||
const res = await makePost(
|
||
"admin/find",
|
||
{},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
}
|
||
showLoadingFind = false;
|
||
const orderTypesMap = {
|
||
card: "Карта",
|
||
sbp: "СБП",
|
||
clearing_account: "По номеру счёта",
|
||
};
|
||
|
||
let showAddRef = false;
|
||
</script>
|
||
|
||
<div class="w-full flex flex-col gap-8">
|
||
<div class="flex gap-4">
|
||
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
|
||
<h1 class="text-2xl font-semibold">Информация о пользователе</h1>
|
||
</div>
|
||
|
||
<div class="flex flex-col w-full gap-2">
|
||
<div class="w-full grid grid-cols-1 lg:grid-cols-2 gap-2">
|
||
<div class="w-full flex flex-col">
|
||
<div
|
||
class="bg-base-300 rounded-t-box p-4 pb-2 border-2 border-b-0 border-base-300"
|
||
>
|
||
<p class="text-lg font-semibold leading-none">Основная информация</p>
|
||
</div>
|
||
<div
|
||
class="flex flex-col p-4 gap-2 border-2 border-t-0 rounded-b-box border-base-300"
|
||
>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Токен</p>
|
||
<div class="flex gap-2">
|
||
<p class="text-accent font-semibold">{token}</p>
|
||
<CopyButton data={token} />
|
||
</div>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Имя и фамилия</p>
|
||
<p class="text-accent font-semibold">
|
||
{userData.name}
|
||
{userData.surname}
|
||
</p>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Валюта</p>
|
||
<p class="text-accent font-semibold">{userData?.code}</p>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Торговля</p>
|
||
<p
|
||
class={userData["can_trade"] === "t"
|
||
? "text-primary font-semibold"
|
||
: "text-error font-semibold"}
|
||
>
|
||
{userData["can_trade"] === "t" ? "Активна" : "Отключена"}
|
||
</p>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Доступ к выплатам</p>
|
||
<p
|
||
class={userData["is_withdrawal_available"] === "t"
|
||
? "text-primary font-semibold"
|
||
: "text-error font-semibold"}
|
||
>
|
||
{userData["is_withdrawal_available"] === "t"
|
||
? "Активен"
|
||
: "Отключен"}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="w-full flex flex-col">
|
||
<div
|
||
class="bg-base-300 rounded-t-box p-4 pb-2 border-2 border-b-0 border-base-300"
|
||
>
|
||
<p class="text-lg font-semibold leading-none">
|
||
Информация о финансах
|
||
</p>
|
||
</div>
|
||
<div
|
||
class="flex flex-col p-4 gap-2 border-2 border-t-0 rounded-b-box border-base-300"
|
||
>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Баланс</p>
|
||
<p class="text-accent font-semibold">
|
||
{toValidNumberFormat(userData.balance)}
|
||
{userData?.code}
|
||
</p>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Страховка</p>
|
||
<p class="text-accent font-semibold">{userData?.insurance} USDT</p>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Ставка</p>
|
||
<p class="text-accent font-semibold">{userData.bid}%</p>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Ставка на выплаты</p>
|
||
<p class="text-accent font-semibold">{userData.payout_bid}%</p>
|
||
</div>
|
||
<div class="flex flex-col">
|
||
<p class="opacity-50 text-xs">Кошелёк пополнения</p>
|
||
<div class="flex gap-2">
|
||
<p class="text-accent font-semibold">
|
||
{userData.deposit_address}
|
||
</p>
|
||
<CopyButton data={userData.deposit_address} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="w-full flex justify-start gap-2 overflow-auto">
|
||
<button
|
||
class="btn btn-neutral w-[100px] btn-outline rounded-lg btn-sm"
|
||
on:click={() => {
|
||
reloadUserInfo();
|
||
}}
|
||
disabled={userInfoReloading}
|
||
>
|
||
{#if !userInfoReloading}
|
||
{"Обновить"}
|
||
{:else}
|
||
<span class="loading loading-spinner"></span>
|
||
{/if}
|
||
</button>
|
||
<a
|
||
href={window.location.pathname.replace("profile", "change")}
|
||
class="btn btn-info btn-outline rounded-lg btn-sm">Изменить</a
|
||
>
|
||
<a
|
||
href={`/admin/change-balance/${token}`}
|
||
class="btn btn-neutral btn-outline rounded-lg btn-sm">Изменить баланс</a
|
||
>
|
||
<button
|
||
on:click={() => {
|
||
if (confirm("Вы уверены, что хотите удалить пользователя?"))
|
||
deleteUser();
|
||
}}
|
||
class="btn btn-error btn-outline rounded-lg btn-sm">Удалить</button
|
||
>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex items-center gap-4">
|
||
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
|
||
<h1 class="text-2xl font-semibold">Реквизиты пользователя</h1>
|
||
<button disabled class="btn btn-accent">Добавить реквизит</button>
|
||
</div>
|
||
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
|
||
<div class="w-full flex flex-col justify-center items-center">
|
||
<Pagination
|
||
totalPages={reqsMaxPage}
|
||
pageChangedCallback={(n) => {
|
||
reqsCurrentPage = n;
|
||
getUserRequisites();
|
||
}}
|
||
disableButtons={false}
|
||
css={"btn-neutral mt-2"}
|
||
/>
|
||
<p class="opacity-50 text-xs mt-1">Всего страниц: {reqsMaxPage}</p>
|
||
</div>
|
||
</div>
|
||
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
|
||
<div class="overflow-x-auto mt-4">
|
||
<table class="table">
|
||
<!-- head -->
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Статус</th>
|
||
<th>Банк</th>
|
||
<th>СБП</th>
|
||
<th>Карта</th>
|
||
<th>Телефон</th>
|
||
<th>Имя</th>
|
||
<th>Девайс</th>
|
||
<th>Объем {userData.code}</th>
|
||
<th>Лимит {userData.code}</th>
|
||
<th>Опции</th>
|
||
<!-- <th></th> -->
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{#each userRequisites as requisite}
|
||
<tr class="hover:bg-neutral group">
|
||
<td class="font-semibold">{requisite["uuid"]}</td>
|
||
<td class=""
|
||
>{requisite["status"] === "t" ? "активен" : "отключен"}</td
|
||
>
|
||
<td>{requisite["bank_name"]}</td>
|
||
<td>{requisite["is_sbp"] === "t" ? "да" : "нет"}</td>
|
||
<td>{requisite["cardnumber"]}</td>
|
||
<td>{requisite["phone"]}</td>
|
||
<td>{requisite["name"]}</td>
|
||
<td>{requisite["device_uuid"]}</td>
|
||
<td>{toValidNumberFormat(requisite["total_volume"])}</td>
|
||
<td>{toValidNumberFormat(requisite["daily_volume_limit"])}</td>
|
||
<td class="flex gap-2">
|
||
<a
|
||
href={"/admin/user/edit/req/" +
|
||
token +
|
||
"/" +
|
||
requisite["uuid"] +
|
||
"_" +
|
||
userData.code}
|
||
class="btn btn-info">Изменить</a
|
||
>
|
||
<button class="btn btn-error">Удалить</button>
|
||
</td>
|
||
</tr>
|
||
{/each}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- <div class="flex gap-4">
|
||
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
|
||
<h1 class="text-2xl font-semibold">Девайсы пользователя</h1>
|
||
</div>
|
||
<div class="w-full flex flex-col p-4 rounded-box bg-base-300"></div> -->
|
||
|
||
<div class="flex items-center gap-4">
|
||
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
|
||
<h1 class="text-2xl font-semibold">Текущие заявки</h1>
|
||
<!-- <button href={"/user/neworder/"+token} disabled class="btn btn-accent">Добавить заявку</button> -->
|
||
</div>
|
||
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
|
||
<div class="w-full flex flex-col justify-center items-center">
|
||
<!-- <div class="flex items-center gap-2">
|
||
<input bind:value={findFieldValue} type="text" class="input input-bordered">
|
||
<button on:click={()=>{showFoundedOrderWindow = true;}} class="btn btn-outline">Найти</button>
|
||
</div> -->
|
||
<Pagination
|
||
totalPages={numPagesUserOrders}
|
||
pageChangedCallback={(n) => {
|
||
currentPageUserOrders = n;
|
||
getUserOrders();
|
||
}}
|
||
disableButtons={disablePagesUserOrders}
|
||
css={"btn-neutral mt-2"}
|
||
/>
|
||
<p class="opacity-50 text-xs mt-1">Всего страниц: {numPagesUserOrders}</p>
|
||
<select
|
||
bind:value={ordersFilter}
|
||
class="select select-bordered min-w-[230px] mt-2"
|
||
>
|
||
<!-- <option value="-1"> Все выплаты </option> -->
|
||
<option value="1"> {orderStatusMap["1"]} </option>
|
||
<option value="2"> {orderStatusMap["2"]} </option>
|
||
<option value="3"> {orderStatusMap["3"]} </option>
|
||
</select>
|
||
</div>
|
||
<div class="overflow-x-auto mt-4">
|
||
<table class="table">
|
||
<!-- head -->
|
||
<thead>
|
||
<tr>
|
||
<!-- <th>ID заявки</th> -->
|
||
<th>UUID</th>
|
||
<th>ID клиента</th>
|
||
<th>Статус</th>
|
||
<th>Сумма</th>
|
||
<th>Тип заявки</th>
|
||
<!-- <th>Курс</th> -->
|
||
<!-- <th>Название реквизита</th> -->
|
||
<!-- <th>СБП</th> -->
|
||
<th>Мерчант</th>
|
||
<th>Время создания</th>
|
||
<th>Время закрытия</th>
|
||
<!-- <th>Опции</th> -->
|
||
<!-- <th></th> -->
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{#each userOrders as order}
|
||
<tr class="hover:bg-neutral group">
|
||
<!-- <td class="font-semibold">{order["id"]}</td> -->
|
||
<td class="font-semibold">{order["uuid"]}</td>
|
||
<td class="font-semibold">{order["external_user_id"]}</td>
|
||
<td class="">{order["status"]}</td>
|
||
<td>{toValidNumberFormat(order["summa"])} {userData?.code}</td>
|
||
<td>{orderTypesMap[order["order_type"]]}</td>
|
||
<!-- <td>{order["rate"]} {userData?.code}</td> -->
|
||
<!-- <td>{order["is_sbp"] === "t" ? "да" : "нет"}</td> -->
|
||
<td>{order["merchant"]}</td>
|
||
<td>{order["creation_time"]}</td>
|
||
<td>{order["closetime"]}</td>
|
||
<!-- <td>
|
||
<button class="btn btn-outline">Закрыть ручками</button>
|
||
</td> -->
|
||
<!-- <td class="flex gap-2">
|
||
<a href="" class="btn btn-info">Изменить</a>
|
||
<button class="btn btn-error">Удалить</button>
|
||
</td> -->
|
||
</tr>
|
||
{/each}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex gap-4">
|
||
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
|
||
<h1 class="text-2xl font-semibold">Заявки на пополнение</h1>
|
||
</div>
|
||
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
|
||
<div class="w-full flex flex-col justify-center items-center">
|
||
<Pagination
|
||
totalPages={numOfPagesUserDeposits}
|
||
pageChangedCallback={(n) => {
|
||
currentPageUserDeposits = n;
|
||
getUserDeposits();
|
||
}}
|
||
disableButtons={disablePagesUserDeposits}
|
||
css={"btn-neutral"}
|
||
/>
|
||
<p class="opacity-50 text-xs mt-1">
|
||
Всего страниц: {numOfPagesUserDeposits}
|
||
</p>
|
||
</div>
|
||
<div class="overflow-x-auto mt-4">
|
||
<table class="table">
|
||
<!-- head -->
|
||
<thead>
|
||
<tr>
|
||
<th>Статус</th>
|
||
<th>Сумма</th>
|
||
<th>Время создания</th>
|
||
<!-- <th>Опции</th> -->
|
||
<!-- <th></th> -->
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{#each userDeposits as depo}
|
||
<tr class="hover:bg-neutral group">
|
||
<td class="">{depoStatusMap[depo["pending"]]}</td>
|
||
<td>{toValidNumberFormat(depo["amount"])} USDT</td>
|
||
<td>{depo["creationtime"]}</td>
|
||
<!-- <td class="flex gap-2">
|
||
<a href="" class="btn btn-info">Изменить</a>
|
||
<button class="btn btn-error">Удалить</button>
|
||
</td> -->
|
||
</tr>
|
||
{/each}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{#if showAddRef}
|
||
<div class="fixed inset-0 bg-black opacity-60"></div>
|
||
<div class="fixed inset-0 bg-transparent flex justify-center items-center">
|
||
<div class="bg-base-300 rounded-md p-4 w-full max-w-[400px]"></div>
|
||
</div>
|
||
{/if}
|
||
|
||
<!-- {#if showFoundedOrderWindow}
|
||
<div class="fixed z-[1000] inset-0 flex justify-center items-center bg-black bg-opacity-50">
|
||
<div class="bg-base-100 rounded-md p-4 flex flex-col">
|
||
<button class="btn btn-outline" on:click={()=>{showFoundedOrderWindow = false;}}>Закрыть</button>
|
||
<button class="btn btn-outline" on:click={()=>{showFoundedOrderWindow = false;}}>Закрыть</button>
|
||
</div>
|
||
</div>
|
||
{/if} -->
|