538 lines
16 KiB
Svelte
538 lines
16 KiB
Svelte
<script>
|
||
// @ts-nocheck
|
||
|
||
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
|
||
import { makePost } from "$lib/tools/requests/requests";
|
||
import { sayError, sayInfo } from "$lib/tools/toaster/Toaster";
|
||
import Pagination from "$lib/ui-components/pagination.svelte";
|
||
import { redirect } from "$lib/tools/url/URLTools";
|
||
import axios from "axios";
|
||
import { isStringEmptyOrSpaces } from "$lib/tools/strings/Strings";
|
||
import {
|
||
ChevronLeftIcon,
|
||
ChevronRightIcon,
|
||
CopyIcon,
|
||
} from "svelte-feather-icons";
|
||
import { se } from "date-fns/locale";
|
||
import Chart1 from "$lib/ui-components/chart1.svelte";
|
||
|
||
let merchants = [];
|
||
let value_merchantsFilter = "true";
|
||
let showMerchants = false;
|
||
let value_sumMerchant = 0;
|
||
// let value_geoMerchants = "1";
|
||
|
||
$: if (value_merchantsFilter.length > 0 || value_geoMerchants.length > 0) {
|
||
getMerchants();
|
||
}
|
||
|
||
async function getMerchants() {
|
||
showMerchants = false;
|
||
let res = await makePost(
|
||
"admin/merchants",
|
||
{
|
||
// id: Number(value_geoMerchants),
|
||
active: value_merchantsFilter === "true",
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
// console.log(res);
|
||
merchants = res.data.data;
|
||
console.log(merchants);
|
||
if (Array.isArray(merchants) === false) {
|
||
merchants = [];
|
||
}
|
||
showMerchants = true;
|
||
selectedMerchant = "";
|
||
}
|
||
|
||
let selectedMerchant = "";
|
||
let value_startTime = "0";
|
||
let value_endTime = "0";
|
||
|
||
function checkTime() {
|
||
if (isStringEmptyOrSpaces(value_endTime) || value_endTime === "0") {
|
||
// showStats = true;
|
||
return;
|
||
}
|
||
if (isStringEmptyOrSpaces(value_startTime) || value_startTime === "0") {
|
||
// showStats = true;
|
||
return;
|
||
}
|
||
|
||
getStatistics();
|
||
}
|
||
|
||
$: value_startTime, value_endTime, checkTime();
|
||
|
||
let statsResult = {};
|
||
let showStats = false;
|
||
let disablePayButton = false;
|
||
let showPaySpin = false;
|
||
let merchantsGeos = [];
|
||
async function getStatistics() {
|
||
showStats = false;
|
||
let res = await makePost(
|
||
"admin/merchantPayouts",
|
||
{
|
||
date_from: new Date(value_startTime)
|
||
.toISOString()
|
||
.replace(".000Z", "Z"),
|
||
date_to: new Date(value_endTime).toISOString().replace(".000Z", "Z"),
|
||
merchant_id: Number(selectedMerchant),
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
statsResult = res.data;
|
||
res = await makePost(
|
||
"admin/merchantsGeo",
|
||
{
|
||
id: Number(selectedMerchant),
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
merchantsGeos = res.data.data;
|
||
console.log("geos", merchantsGeos);
|
||
|
||
showStats = true;
|
||
}
|
||
|
||
async function payToMerchant() {
|
||
if (value_sumMerchant <= 0) {
|
||
return;
|
||
}
|
||
showPaySpin = true;
|
||
disablePayButton = true;
|
||
let res = await makePost(
|
||
"admin/decreaseMerchantBalance",
|
||
{
|
||
id: selectedMerchant,
|
||
amount: value_sumMerchant + "",
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
// console.log(res);
|
||
if (res.error) {
|
||
sayError("Что-то пошло не так");
|
||
showPaySpin = false;
|
||
disablePayButton = false;
|
||
return;
|
||
}
|
||
sayInfo("Успешно!");
|
||
showPaySpin = false;
|
||
disablePayButton = false;
|
||
getMerchants();
|
||
}
|
||
|
||
const settlesStatusMap = {
|
||
"0": "Открыт",
|
||
"1": "Закрыт",
|
||
};
|
||
|
||
let settles = [];
|
||
let showSettlesLoading = false;
|
||
let settlesFilter = "-1";
|
||
let settlesCurrentPage = 1;
|
||
let maxPages = 1;
|
||
|
||
$: if (selectedMerchant !== "") {
|
||
settlesCurrentPage = 1;
|
||
getMerchantSettles();
|
||
console.log(selectedMerchant);
|
||
}
|
||
|
||
$: if (settlesFilter.length > 0) {
|
||
settlesCurrentPage = 1;
|
||
getMerchantSettles();
|
||
}
|
||
|
||
async function getMerchantSettles() {
|
||
showSettlesLoading = true;
|
||
const res = await makePost(
|
||
"admin/loadSettles",
|
||
{
|
||
page: settlesCurrentPage,
|
||
status: settlesFilter === "-1" ? undefined : settlesFilter,
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
|
||
if (res.error) {
|
||
showSettlesLoading = false;
|
||
sayError("Не удалось полуть сеттлы мерча");
|
||
return;
|
||
}
|
||
|
||
showSettlesLoading = false;
|
||
settles = res.data["data"] ?? [];
|
||
maxPages = Number(res.data["pages"]) ? Number(res.data["pages"]) : 1;
|
||
}
|
||
|
||
let showChangeSettleWindow = false;
|
||
let selectedSettleNewSum = 0;
|
||
let selectedSettleNewHash = "";
|
||
let selectedSettleId = "";
|
||
let showLoadingChangeSettle = false;
|
||
|
||
async function changeSettle(id) {
|
||
showLoadingChangeSettle = true;
|
||
const res = await makePost(
|
||
"admin/updateSettle",
|
||
{
|
||
id,
|
||
amount: selectedSettleNewSum.toString(),
|
||
tx_hash: selectedSettleNewHash,
|
||
},
|
||
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||
);
|
||
|
||
if (res.error) {
|
||
sayError("Не удалось изменить сеттл");
|
||
showLoadingChangeSettle = false;
|
||
return;
|
||
}
|
||
|
||
sayInfo("Сеттл изменён!");
|
||
showChangeSettleWindow = false;
|
||
showLoadingChangeSettle = false;
|
||
getMerchantSettles();
|
||
}
|
||
</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="w-full flex flex-col bg-base-300 p-4 rounded-box">
|
||
<div class="flex flex-col p-2">
|
||
<!-- <p>Гео</p>
|
||
<select
|
||
bind:value={value_geoMerchants}
|
||
class="select select-bordered w-full text-base"
|
||
>
|
||
<option selected value="1">Киргизстан</option>
|
||
<option value="false">Неактивные</option>
|
||
</select> -->
|
||
</div>
|
||
<div class="flex flex-col p-2">
|
||
<p>Статус</p>
|
||
<select
|
||
bind:value={value_merchantsFilter}
|
||
class="select select-bordered w-full text-base"
|
||
>
|
||
<option selected value="true">Активные</option>
|
||
<option value="false">Неактивные</option>
|
||
</select>
|
||
</div>
|
||
<div class="overflow-x-auto mt-4 flex flex-col">
|
||
{#if showMerchants}
|
||
<table class="table">
|
||
<!-- head -->
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Интеграция</th>
|
||
<th>Имя</th>
|
||
<!-- <th>Баланс (USDT)</th>
|
||
<th>Комиссия</th>
|
||
<th>Курс на выплаты</th> -->
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{#each merchants as merch}
|
||
<tr
|
||
on:click={() => {
|
||
if (selectedMerchant !== merch["id"]) {
|
||
selectedMerchant = merch["id"];
|
||
getStatistics();
|
||
console.log(selectedMerchant);
|
||
}
|
||
}}
|
||
class={"group " +
|
||
(selectedMerchant === merch["id"]
|
||
? "bg-secondary text-black"
|
||
: "hover:bg-neutral")}
|
||
>
|
||
<td class="font-semibold">{merch["id"]}</td>
|
||
<td>{merch["integration_type"]}</td>
|
||
<td>{merch["name"]}</td>
|
||
<!-- <td>{merch["balance"]}</td>
|
||
<td>{merch["rate"]}%</td>
|
||
<td>{merch["payout_rate"]}</td> -->
|
||
</tr>
|
||
{/each}
|
||
</tbody>
|
||
</table>
|
||
{:else}
|
||
<span class="loading loading-spinner self-center mt-4"></span>
|
||
{/if}
|
||
</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 bg-base-300 p-4 rounded-box">
|
||
{#if selectedMerchant === ""}
|
||
<p class="text-center">
|
||
Выберите мерчанта из таблицы, чтобы посмотреть его статистику
|
||
</p>
|
||
{:else}
|
||
<div class="flex flex-col p-2">
|
||
<div class="overflow-x-auto">
|
||
<table class="table">
|
||
<!-- head -->
|
||
<thead>
|
||
<tr>
|
||
<th>Регион</th>
|
||
<th>Статус</th>
|
||
<th>Баланс</th>
|
||
<!-- <th>Тип баланса</th> -->
|
||
<th>Pay In</th>
|
||
<th>Pay Out</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{#each merchantsGeos as merch}
|
||
<tr>
|
||
<td class="font-semibold">{merch["code"]}</td>
|
||
<td>{merch["is_active"] === "t" ? "Активен" : "Отключен"}</td>
|
||
<td
|
||
>{merch["balance"]}
|
||
{merch["balance_type"] === "0" ? "USDT" : merch["code"]}</td
|
||
>
|
||
<!-- <td>{merch["balance_type"] === "0" ? "crypto":"fiat"}</td> -->
|
||
<td>{merch["payin"]}%</td>
|
||
<td>{merch["payout"]}%</td>
|
||
</tr>
|
||
{/each}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="mt-4"></div>
|
||
{#key merchantsGeos}
|
||
{#each merchantsGeos as merch}
|
||
<p class="text-2xl font-bold">Объем для региона {merch["code"]}:</p>
|
||
<Chart1
|
||
currencyCode={merch["code"]}
|
||
merchantId={selectedMerchant}
|
||
/>
|
||
{/each}
|
||
{/key}
|
||
<p class="mt-4 text-2xl font-bold">
|
||
Показать статистику за период для ID: {selectedMerchant}
|
||
</p>
|
||
<div class="mt-3">
|
||
C: <input
|
||
bind:value={value_startTime}
|
||
type="datetime-local"
|
||
class="input input-bordered input-sm max-w-[200px]"
|
||
/>
|
||
</div>
|
||
<div class="mt-1">
|
||
По: <input
|
||
bind:value={value_endTime}
|
||
type="datetime-local"
|
||
class="input input-bordered input-sm max-w-[200px]"
|
||
/>
|
||
</div>
|
||
{#if statsResult?.total !== undefined}
|
||
{#if showStats}
|
||
<p class="mt-4 text-lg text-primary">
|
||
Сумма по закрытым заявкам: {statsResult?.success} USDT
|
||
</p>
|
||
<p class="text-lg text-primary">
|
||
Сумма по поступившим заявкам: {statsResult?.total} USDT
|
||
</p>
|
||
{:else}
|
||
<span class="loading loading-spinner self-center mt-4"></span>
|
||
{/if}
|
||
{/if}
|
||
<!-- <p class="mt-4">Уменьшение баланса</p>
|
||
<input
|
||
bind:value={value_sumMerchant}
|
||
type="number"
|
||
class="input input-bordered max-w-[200px] mt-1"
|
||
/>
|
||
<button
|
||
on:click={() => {
|
||
payToMerchant();
|
||
}}
|
||
disabled={disablePayButton}
|
||
class="btn btn-secondary flex justify-center items-center mt-1"
|
||
>Уменьшить
|
||
{#if showPaySpin}
|
||
<span class="loading loading-spinner self-center"></span>
|
||
{/if}
|
||
</button> -->
|
||
</div>
|
||
<p class="text-2xl font-bold mt-4">Сеттлы</p>
|
||
<select
|
||
bind:value={settlesFilter}
|
||
class="select select-bordered max-w-[320px] self-center my-2"
|
||
>
|
||
<option value="-1">Все сеттлы</option>
|
||
<option value="0">Открытые</option>
|
||
<option value="1">Закрытые</option>
|
||
</select>
|
||
{#if showSettlesLoading}
|
||
<span class="loading loading-lg self-center mt-2" />
|
||
{:else}
|
||
<div class="overflow-x-auto">
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th> Опции </th>
|
||
<th> ID </th>
|
||
<th> Статус </th>
|
||
<th> Мерчант </th>
|
||
<th> Регион </th>
|
||
<th> Сумма </th>
|
||
<th> Сумма сеттла от мерча</th>
|
||
<th> Адрес кошелька </th>
|
||
<th> Хэш транзакции </th>
|
||
<th> Время создания </th>
|
||
<th> Время закрытия </th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{#each settles as settle}
|
||
<tr class="hover:bg-neutral group">
|
||
<td>
|
||
<button
|
||
on:click={() => {
|
||
selectedSettleNewSum = Number(settle["amount"]);
|
||
selectedSettleNewHash = settle["tx_hash"];
|
||
selectedSettleId = settle["id"];
|
||
showChangeSettleWindow = true;
|
||
}}
|
||
class="btn btn-info">Изменить</button
|
||
>
|
||
</td>
|
||
<td>
|
||
{settle["id"]}
|
||
</td>
|
||
<td>
|
||
{settlesStatusMap[settle["status"]]}
|
||
</td>
|
||
<td>
|
||
{settle["name"]}
|
||
</td>
|
||
<td>
|
||
{settle["code"]}
|
||
</td>
|
||
<td>
|
||
{settle["amount"]} USDT
|
||
</td>
|
||
<td>
|
||
{settle["merchant_amount"]} USDT
|
||
</td>
|
||
<td class="">
|
||
<p>{settle["settle_address"]}</p>
|
||
{#if settle["settle_address"].length > 2}
|
||
<button
|
||
on:click={() => {
|
||
navigator.clipboard.writeText(
|
||
settle["settle_address"]
|
||
);
|
||
sayInfo("Скопировано");
|
||
}}
|
||
class="btn btn-xs"><CopyIcon size={"12"} /></button
|
||
>
|
||
{/if}
|
||
</td>
|
||
<td class="">
|
||
<p>{settle["tx_hash"]}</p>
|
||
{#if settle["tx_hash"].length > 2}
|
||
<button
|
||
on:click={() => {
|
||
navigator.clipboard.writeText(settle["tx_hash"]);
|
||
sayInfo("Скопировано");
|
||
}}
|
||
class="btn btn-xs"><CopyIcon size={"12"} /></button
|
||
>
|
||
{/if}
|
||
</td>
|
||
<td>
|
||
{settle["creationtime"]}
|
||
</td>
|
||
<td>
|
||
{settle["closetime"]}
|
||
</td>
|
||
</tr>
|
||
{/each}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{/if}
|
||
<div class="flex w-full justify-center items-center mt-2">
|
||
<button
|
||
class="btn"
|
||
on:click={() => {
|
||
if (settlesCurrentPage <= 1) return;
|
||
settlesCurrentPage -= 1;
|
||
getMerchantSettles();
|
||
}}
|
||
>
|
||
<ChevronLeftIcon size={"24"} /></button
|
||
>
|
||
<div class="btn cursor-default hover:cursor-default">
|
||
Страница {settlesCurrentPage}
|
||
</div>
|
||
<button
|
||
class="btn"
|
||
on:click={() => {
|
||
if (settlesCurrentPage >= maxPages) return;
|
||
settlesCurrentPage += 1;
|
||
getMerchantSettles();
|
||
}}
|
||
>
|
||
<ChevronRightIcon size={"24"} />
|
||
</button>
|
||
</div>
|
||
<p class="text-sm opacity-50 self-center">Всего страниц: {maxPages}</p>
|
||
{/if}
|
||
</div>
|
||
</div>
|
||
|
||
{#if showChangeSettleWindow}
|
||
<div
|
||
class="fixed z-[100] inset-0 flex justify-center items-center bg-base-300 bg-opacity-90"
|
||
>
|
||
<div class="flex flex-col p-4 rounded-md bg-base-300">
|
||
<p>ID: {selectedSettleId}</p>
|
||
<p class="text-sm mt-4">Новая сумма (USDT)</p>
|
||
<input
|
||
bind:value={selectedSettleNewSum}
|
||
type="number"
|
||
class="input input-bordered"
|
||
/>
|
||
<p class="mt-4 text-sm">Новый хэш</p>
|
||
<input
|
||
bind:value={selectedSettleNewHash}
|
||
type="text"
|
||
class="input input-bordered"
|
||
/>
|
||
<button
|
||
on:click={() => {
|
||
changeSettle(selectedSettleId);
|
||
}}
|
||
class="btn btn-success mt-2"
|
||
>
|
||
{#if showLoadingChangeSettle}
|
||
<span class="loading loading-xs" />
|
||
{:else}
|
||
{"Сохранить изменения"}
|
||
{/if}
|
||
</button>
|
||
<button
|
||
on:click={() => {
|
||
showChangeSettleWindow = false;
|
||
}}
|
||
class="btn btn-outline mt-1">Отмена</button
|
||
>
|
||
</div>
|
||
</div>
|
||
{/if}
|