- 0 -
This commit is contained in:
parent
8a7be273be
commit
f6ca40aeda
47
src/lib/tools/requests/requests.js
Normal file
47
src/lib/tools/requests/requests.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { serializeError } from "serialize-error";
|
||||||
|
|
||||||
|
|
||||||
|
function defaultResponseProcessor(response, dataProcessor) {
|
||||||
|
return {error: false, data: dataProcessor(response.data), status: response.status};
|
||||||
|
}
|
||||||
|
function defaultErrorProcessor(error) {
|
||||||
|
return {error: true, data: serializeError(error), status: (error.code === "ERR_NETWORK" ? 502:serializeError(error).status)};
|
||||||
|
}
|
||||||
|
function defaultDataResponseProcessor(data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let BasicURLValue = "https://hostapay.trade/api/v1/";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function makePost(url, data, options = undefined,
|
||||||
|
responseProcessor = defaultResponseProcessor,
|
||||||
|
dataResponseProcessor = defaultDataResponseProcessor,
|
||||||
|
errorProcessor = defaultErrorProcessor)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const res = await axios.post(BasicURLValue + url, data, options);
|
||||||
|
return responseProcessor(res, dataResponseProcessor);
|
||||||
|
} catch (error) {
|
||||||
|
return errorProcessor(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeGet(url, options = undefined,
|
||||||
|
responseProcessor = defaultResponseProcessor,
|
||||||
|
dataResponseProcessor = defaultDataResponseProcessor,
|
||||||
|
errorProcessor = defaultErrorProcessor)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const res = await axios.get(BasicURLValue + url, options);
|
||||||
|
return responseProcessor(res, dataResponseProcessor);
|
||||||
|
} catch (error) {
|
||||||
|
return errorProcessor(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {BasicURLValue, makePost, makeGet, defaultResponseProcessor, defaultDataResponseProcessor, defaultErrorProcessor};
|
2
src/routes/+layout.js
Normal file
2
src/routes/+layout.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const prerender = true;
|
||||||
|
export const ssr = false;
|
67
src/routes/+layout.svelte
Normal file
67
src/routes/+layout.svelte
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<script>
|
||||||
|
import { SvelteToast } from "@zerodevx/svelte-toast";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import "../app.css";
|
||||||
|
import Navbar from "$lib/ui-components/navbar.svelte";
|
||||||
|
import { checkAuth, checkAuthSync } from "$lib/auth/Auth";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools";
|
||||||
|
import { AuthStorage } from "$lib/tools/storages/auth-storage";
|
||||||
|
|
||||||
|
let loggedIn = false;
|
||||||
|
let authCheckInProgress = false;
|
||||||
|
|
||||||
|
|
||||||
|
AuthStorage.subscribe((s)=>{
|
||||||
|
loggedIn = s.logged;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
|
||||||
|
if (!checkAuthSync() && !window.location.pathname.toLocaleLowerCase().includes("/login")) {
|
||||||
|
redirect("/admin/login");
|
||||||
|
}
|
||||||
|
else if (checkAuthSync())
|
||||||
|
{
|
||||||
|
loggedIn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if loggedIn}
|
||||||
|
<div class="fixed left-0 right-0 top-0 flex z-10">
|
||||||
|
<Navbar />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="flex justify-center flex-shrink-0 w-full h-[calc(100vh-52px-68px)] mt-[68px] overflow-auto p-4">
|
||||||
|
<div class="flex w-full md:max-w-[90%] lg:max-w-[1080px]">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class="footer footer-center bg-base-300 text-base-content p-4">
|
||||||
|
<aside>
|
||||||
|
<p>
|
||||||
|
Copyright © {new Date().getFullYear()} - All right reserved
|
||||||
|
</p>
|
||||||
|
</aside>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<SvelteToast
|
||||||
|
options={{
|
||||||
|
duration: 2000, // duration of progress bar tween to the `next` value
|
||||||
|
initial: 1, // initial progress bar value
|
||||||
|
next: 0, // next progress value
|
||||||
|
pausable: false, // pause progress bar tween on mouse hover
|
||||||
|
dismissable: true, // allow dismiss with close button
|
||||||
|
reversed: false, // insert new toast to bottom of stack
|
||||||
|
intro: { x: 256 }, // toast intro fly animation settings
|
||||||
|
theme: {
|
||||||
|
// "--toastColor": "var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)))",
|
||||||
|
// "--toastBackground": "var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)))",
|
||||||
|
// "--toastBarBackground": "#2F855A",
|
||||||
|
}, // css var overrides
|
||||||
|
classes: [], // user-defined classes
|
||||||
|
}}
|
||||||
|
/>
|
12
src/routes/+page.svelte
Normal file
12
src/routes/+page.svelte
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script>
|
||||||
|
import { checkAuth, checkAuthSync } from "$lib/auth/Auth";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
onMount(()=>{
|
||||||
|
if(checkAuthSync())
|
||||||
|
redirect("/admin/userslist");
|
||||||
|
else
|
||||||
|
redirect("/admin/login");
|
||||||
|
});
|
||||||
|
</script>
|
342
src/routes/disputes/+page.svelte
Normal file
342
src/routes/disputes/+page.svelte
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
<script>
|
||||||
|
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";
|
||||||
|
|
||||||
|
const disputesStatusMap = {
|
||||||
|
"4": "Требует проверки",
|
||||||
|
"5": "На проверке у трейдера",
|
||||||
|
"6": "Принят",
|
||||||
|
"7": "Отклонён",
|
||||||
|
"8": "Чек на проверке"
|
||||||
|
};
|
||||||
|
|
||||||
|
const disputesStatusMapColors = {
|
||||||
|
"4": "text-white",
|
||||||
|
"5": "text-warning",
|
||||||
|
"6": "text-primary",
|
||||||
|
"7": "text-error",
|
||||||
|
"8": "text-warning",
|
||||||
|
};
|
||||||
|
|
||||||
|
//admin/getDisputes
|
||||||
|
let disputes = [];
|
||||||
|
let numOfPagesDisputes = 1;
|
||||||
|
let currentPageDisputes = 1;
|
||||||
|
let disablePagesDisputes = false;
|
||||||
|
let currentDisputesFilter = -1;
|
||||||
|
|
||||||
|
async function getDisputes() {
|
||||||
|
disablePagesDisputes = true;
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/getDisputes",
|
||||||
|
{
|
||||||
|
page: 1,
|
||||||
|
filter: currentDisputesFilter
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
disablePagesDisputes = false;
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось получить споры");
|
||||||
|
disablePagesDisputes = false;
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
disputes = result.data.disputes;
|
||||||
|
console.log(disputes);
|
||||||
|
numOfPagesDisputes = result.data.pages;
|
||||||
|
disablePagesDisputes = false;
|
||||||
|
}
|
||||||
|
getDisputes();
|
||||||
|
|
||||||
|
let selectedDispute = {
|
||||||
|
amount: "",
|
||||||
|
is_sbp: "",
|
||||||
|
name: "",
|
||||||
|
order_creationtime: "",
|
||||||
|
order_id: "",
|
||||||
|
order_rate: "",
|
||||||
|
order_requisites_id: "",
|
||||||
|
order_status: "",
|
||||||
|
receipt: "",
|
||||||
|
requisite_cardnumber: "",
|
||||||
|
requisite_phone: "",
|
||||||
|
dispute_uuid: "",
|
||||||
|
new_amount: "",
|
||||||
|
creation_time: "",
|
||||||
|
out_code: "",
|
||||||
|
in_code: ""
|
||||||
|
};
|
||||||
|
let showFullInfo = false;
|
||||||
|
|
||||||
|
async function acceptDispute() {
|
||||||
|
//admin/changeDispute
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/changeDispute",
|
||||||
|
{
|
||||||
|
action: selectedDispute.order_status === "8" ? 3:1,
|
||||||
|
order_id: selectedDispute.order_id,
|
||||||
|
dispute_uuid: selectedDispute.dispute_uuid
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось принять спор");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sayInfo("Спор одобрен!");
|
||||||
|
showFullInfo = false;
|
||||||
|
getDisputes();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rejectDispute() {
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/changeDispute",
|
||||||
|
{
|
||||||
|
action: selectedDispute.order_status === "8" ? 4:2,
|
||||||
|
order_id: selectedDispute.order_id,
|
||||||
|
dispute_uuid: selectedDispute.dispute_uuid
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось отклонить спор");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sayInfo("Спор отклонён!");
|
||||||
|
showFullInfo = false;
|
||||||
|
getDisputes();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mimeToExtension(mimeType) {
|
||||||
|
const mimeTypes = {
|
||||||
|
"application/pdf": "pdf",
|
||||||
|
"application/zip": "zip",
|
||||||
|
"image/jpeg": "jpg",
|
||||||
|
"image/jpg": "jpg",
|
||||||
|
"image/png": "png",
|
||||||
|
"text/plain": "txt",
|
||||||
|
"application/msword": "doc",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
||||||
|
"docx",
|
||||||
|
// Добавьте дополнительные MIME-типы по необходимости
|
||||||
|
};
|
||||||
|
return mimeTypes[mimeType] || "bin"; // Если тип неизвестен, возвращаем .bin
|
||||||
|
}
|
||||||
|
</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 p-4">
|
||||||
|
<select
|
||||||
|
on:change={(e)=>{
|
||||||
|
currentDisputesFilter = Number(e.target.value);
|
||||||
|
getDisputes();
|
||||||
|
}}
|
||||||
|
class="select select-bordered w-full text-base"
|
||||||
|
>
|
||||||
|
<option selected value="-1">Показать все споры</option>
|
||||||
|
<option value="7">Отклонённые</option>
|
||||||
|
<option value="6">Принятые</option>
|
||||||
|
<option value="5">На проверке у трейдера</option>
|
||||||
|
<option value="4">Требует проверки</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex flex-col justify-center items-center">
|
||||||
|
<Pagination
|
||||||
|
totalPages={numOfPagesDisputes}
|
||||||
|
pageChangedCallback={(n) => {
|
||||||
|
currentPageDisputes = n;
|
||||||
|
getDisputes();
|
||||||
|
}}
|
||||||
|
disableButtons={disablePagesDisputes}
|
||||||
|
css={"btn-neutral"}
|
||||||
|
/>
|
||||||
|
<p class="opacity-50 text-xs mt-1">
|
||||||
|
Всего страниц: {numOfPagesDisputes}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-auto mt-4 flex flex-col">
|
||||||
|
{#if !disablePagesDisputes}
|
||||||
|
<table class="table">
|
||||||
|
<!-- head -->
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<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 disputes as dispute}
|
||||||
|
<tr class="hover:bg-neutral group">
|
||||||
|
<td class="font-semibold">{dispute["order_id"]}</td>
|
||||||
|
<td class={disputesStatusMapColors[dispute["order_status"]]}
|
||||||
|
>{disputesStatusMap[dispute["order_status"]]}</td
|
||||||
|
>
|
||||||
|
<td>{dispute["is_sbp"] === "t" ? "да" : "нет"}</td>
|
||||||
|
<td>{dispute["requisite_cardnumber"]}</td>
|
||||||
|
<td>{dispute["requisite_phone"]}</td>
|
||||||
|
<td>{dispute["order_rate"]}</td>
|
||||||
|
<td>{dispute["amount"]}</td>
|
||||||
|
<td>{dispute["order_creationtime"]}</td>
|
||||||
|
<td
|
||||||
|
on:click={() => {
|
||||||
|
selectedDispute = dispute;
|
||||||
|
showFullInfo = true;
|
||||||
|
}}
|
||||||
|
class="flex gap-2"
|
||||||
|
>
|
||||||
|
<button class="btn btn-info">Подробнее</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{:else}
|
||||||
|
<span class="loading loading-spinner self-center"></span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if showFullInfo}
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 flex justify-center items-center md:p-32 p-8 pt-[68px] bg-black bg-opacity-50"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col w-full max-w-[600px] p-4 rounded-box bg-base-300">
|
||||||
|
<p class="self-center opacity-60">ID: {selectedDispute.dispute_uuid}</p>
|
||||||
|
<p
|
||||||
|
class={"self-center text-3xl " +
|
||||||
|
disputesStatusMapColors[selectedDispute["order_status"]]}
|
||||||
|
>
|
||||||
|
{disputesStatusMap[selectedDispute["order_status"]]}
|
||||||
|
</p>
|
||||||
|
<p class="self-center opacity-60">{selectedDispute.order_creationtime}</p>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">{selectedDispute.name}</p>
|
||||||
|
<p class="">Карта: {selectedDispute.requisite_cardnumber}</p>
|
||||||
|
<p class="">Телефон: {selectedDispute.requisite_phone}</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">Курс</p>
|
||||||
|
<p>{selectedDispute.order_rate} {selectedDispute.out_code}</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">Сумма в споре</p>
|
||||||
|
<p>
|
||||||
|
{(Number(selectedDispute.new_amount) / Number(selectedDispute.order_rate)).toFixed(2)} {selectedDispute.out_code} ({Number(selectedDispute.new_amount)} USDT)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">Сумма в заявке</p>
|
||||||
|
<p>
|
||||||
|
{Number(selectedDispute.order_rate) * Number(selectedDispute.amount)} {selectedDispute.out_code} ({Number(selectedDispute.amount)} USDT)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{#if selectedDispute["order_status"] === "4" || selectedDispute["order_status"] === "8"}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
acceptDispute();
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-primary mt-4"
|
||||||
|
>
|
||||||
|
Одобрить
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
rejectDispute();
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-error mt-1"
|
||||||
|
>
|
||||||
|
Отклонить
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{#if selectedDispute["order_status"] !== "4"}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
axios
|
||||||
|
.get(
|
||||||
|
"https://hostapay.trade/api/v1/loadFile?dispute=" +
|
||||||
|
selectedDispute["order_id"],
|
||||||
|
{
|
||||||
|
responseType: "blob",
|
||||||
|
headers: makeAuthHeaderForAxios(getAuthInfo()?.a).headers,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(function (response) {
|
||||||
|
// Создаем ссылку для скачивания
|
||||||
|
const url = window.URL.createObjectURL(
|
||||||
|
new Blob([response.data])
|
||||||
|
);
|
||||||
|
const contentType = response?.data?.type; // Извлекаем MIME-тип из Blob
|
||||||
|
let extension = mimeToExtension(contentType); // Получаем расширение из функции
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.style.display = "none";
|
||||||
|
a.href = url;
|
||||||
|
a.download = `Чек_${Date.now()}.${extension}`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// Очищаем
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
document.body.removeChild(a);
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-info mt-4"
|
||||||
|
>
|
||||||
|
Скачать чек
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
showFullInfo = false;
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-ghost mt-4"
|
||||||
|
>
|
||||||
|
Закрыть
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
118
src/routes/login/+page.svelte
Normal file
118
src/routes/login/+page.svelte
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<script>
|
||||||
|
import { makePost } from "$lib/tools/requests/requests";
|
||||||
|
import { sayError, sayInfo, sayWarning } from "$lib/tools/toaster/Toaster";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { checkAuth, saveAuthInfo } from "$lib/auth/Auth";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools";
|
||||||
|
import { AuthStorage } from "$lib/tools/storages/auth-storage";
|
||||||
|
|
||||||
|
//6da8a96a-7253-45e2-a3c1-e00d5ecdc65d - valid
|
||||||
|
let tokenValid = true;
|
||||||
|
let totpValid = true;
|
||||||
|
let showLoadingSpinner = false;
|
||||||
|
|
||||||
|
let input_token = null;
|
||||||
|
let input_totp = null;
|
||||||
|
async function sendLogin() {
|
||||||
|
if (input_token.value === "" || input_totp.value === "") {
|
||||||
|
sayError("Заполните все поля!");
|
||||||
|
tokenValid = input_token.value !== "";
|
||||||
|
totpValid = input_totp.value !== "";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
tokenValid = true;
|
||||||
|
totpValid = true;
|
||||||
|
showLoadingSpinner = true;
|
||||||
|
const result = await makePost("client/login", {
|
||||||
|
user_token: input_token.value,
|
||||||
|
totp: input_totp.value,
|
||||||
|
});
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Ошибка входа!");
|
||||||
|
tokenValid = false;
|
||||||
|
totpValid = false;
|
||||||
|
showLoadingSpinner = false;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
saveAuthInfo(result.data.token);
|
||||||
|
AuthStorage.update((s) => {
|
||||||
|
s.logged = true;
|
||||||
|
});
|
||||||
|
redirect("/admin/");
|
||||||
|
showLoadingSpinner = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let checkAuthInProgress = true;
|
||||||
|
// onMount(async () => {
|
||||||
|
// let result = await checkAuth();
|
||||||
|
// if (result && window.location.pathname === "/login") {
|
||||||
|
// window.location.href = "/";
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// checkAuthInProgress = false;
|
||||||
|
// });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full h-full flex items-center justify-center">
|
||||||
|
<div
|
||||||
|
class="bg-base-300 w-full h-full max-w-[500px] max-h-[300px] flex flex-col justify-center gap-2 p-4 rounded-box"
|
||||||
|
>
|
||||||
|
<h1 class="text-4xl self-center mb-4">Вход</h1>
|
||||||
|
<label
|
||||||
|
class={"input input-bordered flex items-center gap-2 " +
|
||||||
|
(tokenValid ? "input-warning" : "input-error")}
|
||||||
|
>
|
||||||
|
Токен:
|
||||||
|
<input
|
||||||
|
bind:this={input_token}
|
||||||
|
type="text"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
class={"input input-bordered flex items-center gap-2 " +
|
||||||
|
(totpValid ? "input-warning" : "input-error")}
|
||||||
|
>
|
||||||
|
2FA Код:
|
||||||
|
<input
|
||||||
|
bind:this={input_totp}
|
||||||
|
type="text"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<!-- <button class="btn-link btn max-w-[140px] self-center">У меня нет 2FA</button> -->
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
sendLogin();
|
||||||
|
}}
|
||||||
|
class="btn btn-primary text-lg"
|
||||||
|
>
|
||||||
|
Войти
|
||||||
|
{#if showLoadingSpinner}
|
||||||
|
<svg
|
||||||
|
class="animate-spin -ml-1 mr-3 h-5 w-5 text-black"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
class="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
class="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
214
src/routes/newuser/+page.svelte
Normal file
214
src/routes/newuser/+page.svelte
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
<script>
|
||||||
|
import { isStringEmptyOrSpaces } from "$lib/tools/strings/Strings";
|
||||||
|
import { makePost, makeGet } from "$lib/tools/requests/requests";
|
||||||
|
import {
|
||||||
|
checkAuthSync,
|
||||||
|
getAuthInfo,
|
||||||
|
makeAuthHeaderForAxios,
|
||||||
|
} from "$lib/auth/Auth";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools";
|
||||||
|
|
||||||
|
let value_name = "";
|
||||||
|
let value_surname = "";
|
||||||
|
let value_balance = 0;
|
||||||
|
let value_role = "-1";
|
||||||
|
let value_bid = 0;
|
||||||
|
let value_trading = false;
|
||||||
|
let value_currency_code = "-1";
|
||||||
|
|
||||||
|
$: checkFields(
|
||||||
|
value_name,
|
||||||
|
value_surname,
|
||||||
|
value_balance,
|
||||||
|
value_role,
|
||||||
|
value_bid,
|
||||||
|
value_currency_code
|
||||||
|
);
|
||||||
|
|
||||||
|
function checkFields(name, surname, balance, role, bid, code) {
|
||||||
|
if(isStringEmptyOrSpaces(code) || code === "-1")
|
||||||
|
{
|
||||||
|
canCreateUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isStringEmptyOrSpaces(name) || name.length < 3) {
|
||||||
|
canCreateUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((bid !== 0 && !bid) || bid < 0) {
|
||||||
|
canCreateUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isStringEmptyOrSpaces(surname) || surname.length < 3) {
|
||||||
|
canCreateUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (role === "-1" || isStringEmptyOrSpaces(role)) {
|
||||||
|
canCreateUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((balance !== 0 && !balance) || balance < 0) {
|
||||||
|
canCreateUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
canCreateUser = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let canCreateUser = false;
|
||||||
|
let loadingUserToken = false;
|
||||||
|
let userToken = "";
|
||||||
|
|
||||||
|
let temp_userToken = "";
|
||||||
|
|
||||||
|
async function createUser() {
|
||||||
|
loadingUserToken = true;
|
||||||
|
const result = await makePost(
|
||||||
|
"createUser",
|
||||||
|
{
|
||||||
|
"code": value_currency_code
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось создать пользователя");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
temp_userToken = result.data.token;
|
||||||
|
setupNewUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupNewUser() {
|
||||||
|
//todo
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/changeUser",
|
||||||
|
{
|
||||||
|
token: temp_userToken,
|
||||||
|
name: value_name,
|
||||||
|
surname: value_surname,
|
||||||
|
balance: value_balance.toString(),
|
||||||
|
rate: "0",
|
||||||
|
can_trade: value_trading ? "true":"false",
|
||||||
|
can_trade_global: value_trading ? "true":"false",
|
||||||
|
role: value_role,
|
||||||
|
bid: value_bid.toString(),
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if(result.status === 401)
|
||||||
|
{
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
// window.location.href = "/";
|
||||||
|
redirect("/admin/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(result.error)
|
||||||
|
{
|
||||||
|
sayError("Не удалось настроить пользователя");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userToken = temp_userToken;
|
||||||
|
loadingUserToken = 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="w-full flex flex-col gap-4 p-4 rounded-box bg-base-300">
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Имя:
|
||||||
|
<input
|
||||||
|
bind:value={value_name}
|
||||||
|
type="text"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Фамилия:
|
||||||
|
<input
|
||||||
|
bind:value={value_surname}
|
||||||
|
type="text"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Баланс (USDT):
|
||||||
|
<input
|
||||||
|
bind:value={value_balance}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Ставка (%):
|
||||||
|
<input
|
||||||
|
bind:value={value_bid}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
bind:value={value_role}
|
||||||
|
class="select select-bordered w-full text-base"
|
||||||
|
>
|
||||||
|
<option disabled selected value="-1">Права доступа</option>
|
||||||
|
<option value="1">Пользователь</option>
|
||||||
|
<option value="2">Модератор</option>
|
||||||
|
<option value="4">Администратор</option>
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
bind:value={value_currency_code}
|
||||||
|
class="select select-bordered w-full text-base"
|
||||||
|
>
|
||||||
|
<option disabled selected value="-1">Валюта</option>
|
||||||
|
<option value="KGS">KGS</option>
|
||||||
|
<!-- <option value="2">Модератор</option>
|
||||||
|
<option value="4">Администратор</option> -->
|
||||||
|
</select>
|
||||||
|
<div class="flex p-4 items-center gap-2">
|
||||||
|
<p>Торговля:</p>
|
||||||
|
<input
|
||||||
|
bind:checked={value_trading}
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle toggle-primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
createUser();
|
||||||
|
}}
|
||||||
|
class={"btn text-base " +
|
||||||
|
(loadingUserToken ? "btn-warning " : "btn-primary ") +
|
||||||
|
(userToken !== "" ? "disabled:text-info" : "disabled:text-warning") +
|
||||||
|
(canCreateUser ? "" : "disabled:text-opacity-50")}
|
||||||
|
disabled={!canCreateUser || loadingUserToken || userToken !== ""}
|
||||||
|
>
|
||||||
|
{#if loadingUserToken}
|
||||||
|
{"Ожидайте токен"}
|
||||||
|
<span class="loading loading-spinner"></span>
|
||||||
|
{:else if userToken === ""}
|
||||||
|
{"Создать пользователя"}
|
||||||
|
{:else if canCreateUser}
|
||||||
|
{"Пользователь создан"}
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
{#if userToken !== ""}
|
||||||
|
<div class="w-full flex p-4">
|
||||||
|
<p class="text-xl">
|
||||||
|
Токен пользователя: <span class="text-info text-xl">{userToken}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
522
src/routes/payouts/+page.svelte
Normal file
522
src/routes/payouts/+page.svelte
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
<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";
|
||||||
|
|
||||||
|
const payoutsStatusMap = {
|
||||||
|
"0": "Открыта",
|
||||||
|
"1": "Ожидает оплаты",
|
||||||
|
"2": "Оплачена",
|
||||||
|
"3": "Отказано",
|
||||||
|
};
|
||||||
|
|
||||||
|
const payoutsStatusMapColors = {
|
||||||
|
"0": "text-white",
|
||||||
|
"1": "text-warning",
|
||||||
|
"2": "text-success",
|
||||||
|
"3": "text-error",
|
||||||
|
};
|
||||||
|
|
||||||
|
//admin/getDisputes
|
||||||
|
// @ts-ignore
|
||||||
|
let payouts = [];
|
||||||
|
let numOfPagesPayouts = 1;
|
||||||
|
let currentPagePayouts = 1;
|
||||||
|
let disablePagesPayouts = false;
|
||||||
|
let currentPayoutsFilter = 0;
|
||||||
|
|
||||||
|
async function getPayouts() {
|
||||||
|
disablePagesPayouts = true;
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/payouts",
|
||||||
|
{
|
||||||
|
type: 0,
|
||||||
|
page: currentPagePayouts - 1,
|
||||||
|
filter: currentPayoutsFilter,
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a),
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
disablePagesPayouts = false;
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось получить выплаты");
|
||||||
|
disablePagesPayouts = false;
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payouts = result.data.payouts;
|
||||||
|
console.log(payouts);
|
||||||
|
numOfPagesPayouts = result.data.pages === 0 ? 1:result.data?.pages;
|
||||||
|
disablePagesPayouts = false;
|
||||||
|
}
|
||||||
|
getPayouts();
|
||||||
|
|
||||||
|
let selectedPayout = {
|
||||||
|
amount: "",
|
||||||
|
code: "",
|
||||||
|
creation_time: "",
|
||||||
|
currency_id: "",
|
||||||
|
customer_ip: "",
|
||||||
|
pan: "",
|
||||||
|
rate: "",
|
||||||
|
receipt: "",
|
||||||
|
status: "0",
|
||||||
|
uuid: "",
|
||||||
|
};
|
||||||
|
let showFullInfo = false;
|
||||||
|
|
||||||
|
// async function acceptPayout() {
|
||||||
|
// //admin/changeDispute
|
||||||
|
// const result = await makePost(
|
||||||
|
// "admin/changeDispute",
|
||||||
|
// {
|
||||||
|
// action: selectedPayout.order_status === "8" ? 3 : 1,
|
||||||
|
// order_id: selectedPayout.order_id,
|
||||||
|
// dispute_uuid: selectedPayout.dispute_uuid,
|
||||||
|
// },
|
||||||
|
// // @ts-ignore
|
||||||
|
// makeAuthHeaderForAxios(getAuthInfo()?.a),
|
||||||
|
// );
|
||||||
|
// console.log(result);
|
||||||
|
// if (result.status === 401) {
|
||||||
|
// sayError("Данные авторизации устарели");
|
||||||
|
// redirect("/admin/");
|
||||||
|
// // disablePagesUserDeposits = false;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (result.error) {
|
||||||
|
// sayError("Не удалось принять выплату");
|
||||||
|
// // disablePagesUserDeposits = false;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// sayInfo("Выплата одобрена!");
|
||||||
|
// showFullInfo = false;
|
||||||
|
// getPayouts();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async function rejectPayout() {
|
||||||
|
// const result = await makePost(
|
||||||
|
// "admin/changeDispute",
|
||||||
|
// {
|
||||||
|
// action: selectedPayout.order_status === "8" ? 4 : 2,
|
||||||
|
// order_id: selectedPayout.order_id,
|
||||||
|
// dispute_uuid: selectedPayout.dispute_uuid,
|
||||||
|
// },
|
||||||
|
// // @ts-ignore
|
||||||
|
// makeAuthHeaderForAxios(getAuthInfo()?.a),
|
||||||
|
// );
|
||||||
|
// console.log(result);
|
||||||
|
// if (result.status === 401) {
|
||||||
|
// sayError("Данные авторизации устарели");
|
||||||
|
// redirect("/admin/");
|
||||||
|
// // disablePagesUserDeposits = false;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (result.error) {
|
||||||
|
// sayError("Не удалось отклонить выплату");
|
||||||
|
// // disablePagesUserDeposits = false;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// sayInfo("Выплата отклонена!");
|
||||||
|
// showFullInfo = false;
|
||||||
|
// getPayouts();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
function mimeToExtension(mimeType) {
|
||||||
|
const mimeTypes = {
|
||||||
|
"application/pdf": "pdf",
|
||||||
|
"application/zip": "zip",
|
||||||
|
"image/jpeg": "jpg",
|
||||||
|
"image/jpg": "jpg",
|
||||||
|
"image/png": "png",
|
||||||
|
"text/plain": "txt",
|
||||||
|
"application/msword": "doc",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
||||||
|
"docx",
|
||||||
|
// Добавьте дополнительные MIME-типы по необходимости
|
||||||
|
};
|
||||||
|
// @ts-ignore
|
||||||
|
return mimeTypes[mimeType] || "bin"; // Если тип неизвестен, возвращаем .bin
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkFields() {
|
||||||
|
if(!isStringEmptyOrSpaces(newPayoutTraderUUID) && newPayoutTraderUUID.length > 10)
|
||||||
|
{
|
||||||
|
allowToSave = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(newPayoutStatus !== selectedPayout.status)
|
||||||
|
{
|
||||||
|
allowToSave = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
allowToSave = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newPayoutTraderUUID= "";
|
||||||
|
let newPayoutStatus = selectedPayout.status;
|
||||||
|
let allowToSave = false;
|
||||||
|
// $: selectedPayout, newPayoutStatus = selectedPayout.status;
|
||||||
|
$: newPayoutTraderUUID, newPayoutStatus, checkFields();
|
||||||
|
|
||||||
|
let awaitChanging = false;
|
||||||
|
async function changePayout() {
|
||||||
|
awaitChanging = true;
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/payouts",
|
||||||
|
{
|
||||||
|
type: 1,
|
||||||
|
uuid: selectedPayout.uuid,
|
||||||
|
trader_uuid: (isStringEmptyOrSpaces(newPayoutTraderUUID) || newPayoutTraderUUID.length < 10) ? selectedPayout.traider_uuid:newPayoutTraderUUID,
|
||||||
|
status: Number(newPayoutStatus)
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a),
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
awaitChanging = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось изменить выплату");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
awaitChanging = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sayInfo("Выплата успешно изменена!");
|
||||||
|
showFullInfo = false;
|
||||||
|
getPayouts();
|
||||||
|
awaitChanging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let numOfOpened = 0;
|
||||||
|
let numOfAccepted = 0;
|
||||||
|
let numOfCanceled = 0;
|
||||||
|
let numOfPayed = 0;
|
||||||
|
async function getStats() {
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/payouts",
|
||||||
|
{
|
||||||
|
type: 3
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a),
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
awaitChanging = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось получить статистику выплат");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
// awaitChanging = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
numOfOpened = result.data?.opened;
|
||||||
|
numOfAccepted = result.data?.accepted;
|
||||||
|
numOfCanceled = result.data?.canceled;
|
||||||
|
numOfPayed = result.data?.payed;
|
||||||
|
// sayInfo("Выплата успешно изменена!");
|
||||||
|
// showFullInfo = false;
|
||||||
|
// getPayouts();
|
||||||
|
// awaitChanging = false;
|
||||||
|
}
|
||||||
|
getStats();
|
||||||
|
|
||||||
|
|
||||||
|
let awaitChangingNull = false;
|
||||||
|
async function nullificatePayout() {
|
||||||
|
awaitChangingNull = true;
|
||||||
|
const result = await makePost(
|
||||||
|
"admin/payouts",
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
uuid: selectedPayout.uuid,
|
||||||
|
// trader_uuid: (isStringEmptyOrSpaces(newPayoutTraderUUID) || newPayoutTraderUUID.length < 10) ? selectedPayout.traider_uuid:newPayoutTraderUUID,
|
||||||
|
// status: Number(newPayoutStatus)
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a),
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
awaitChangingNull = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось изменить выплату");
|
||||||
|
// disablePagesUserDeposits = false;
|
||||||
|
awaitChangingNull = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sayInfo("Выплата успешно обнулена!");
|
||||||
|
showFullInfo = false;
|
||||||
|
getPayouts();
|
||||||
|
awaitChangingNull = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full flex flex-col gap-8">
|
||||||
|
<div class="w-full flex justify-center flex-wrap">
|
||||||
|
<div class="stats shadow">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title text-warning">Открыто выплат</div>
|
||||||
|
<div class="stat-value text-warning text-6xl">{numOfOpened}</div>
|
||||||
|
<!-- <div class="stat-desc">21% more than last month</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stats shadow">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title text-info">Принято выплат</div>
|
||||||
|
<div class="stat-value text-info text-6xl">{numOfAccepted}</div>
|
||||||
|
<!-- <div class="stat-desc">21% more than last month</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stats shadow">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title text-success">Оплачено сегодня</div>
|
||||||
|
<div class="stat-value text-success text-6xl">{numOfPayed}</div>
|
||||||
|
<!-- <div class="stat-desc">21% more than last month</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stats shadow">
|
||||||
|
<div class="stat">
|
||||||
|
<div class="stat-title text-error">Отказов сегодня</div>
|
||||||
|
<div class="stat-value text-error text-6xl">{numOfCanceled}</div>
|
||||||
|
<!-- <div class="stat-desc">21% more than last month</div> -->
|
||||||
|
</div>
|
||||||
|
</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">
|
||||||
|
<div class="flex p-4">
|
||||||
|
<select
|
||||||
|
on:change={(e) => {
|
||||||
|
currentPayoutsFilter = Number(e.target.value);
|
||||||
|
getPayouts();
|
||||||
|
}}
|
||||||
|
class="select select-bordered w-full text-base"
|
||||||
|
>
|
||||||
|
<option selected value="0">Показать все выплаты</option>
|
||||||
|
<option value="2">Оплаченные</option>
|
||||||
|
<option value="3">Отклонённые</option>
|
||||||
|
<option value="1">Принятые</option>
|
||||||
|
<!-- <option value="4">Требует проверки</option> -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex flex-col justify-center items-center">
|
||||||
|
<Pagination
|
||||||
|
totalPages={numOfPagesPayouts}
|
||||||
|
pageChangedCallback={(n) => {
|
||||||
|
currentPagePayouts = n;
|
||||||
|
getPayouts();
|
||||||
|
}}
|
||||||
|
disableButtons={disablePagesPayouts}
|
||||||
|
css={"btn-neutral"}
|
||||||
|
/>
|
||||||
|
<p class="opacity-50 text-xs mt-1">
|
||||||
|
Всего страниц: {numOfPagesPayouts}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-auto mt-4 flex flex-col">
|
||||||
|
{#if !disablePagesPayouts}
|
||||||
|
<table class="table">
|
||||||
|
<!-- head -->
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Статус</th>
|
||||||
|
<th>Сумма</th>
|
||||||
|
<th>Карта</th>
|
||||||
|
<th>Курс</th>
|
||||||
|
<th>Время создания</th>
|
||||||
|
<th>Опции</th>
|
||||||
|
<!-- <th></th> -->
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each payouts as payout}
|
||||||
|
<tr class="hover:bg-neutral group">
|
||||||
|
<td class="font-semibold">{payout["uuid"]}</td>
|
||||||
|
<td class={payoutsStatusMapColors[payout["status"]]}
|
||||||
|
>{payoutsStatusMap[payout["status"]]}</td
|
||||||
|
>
|
||||||
|
<!-- <td>{payout["is_sbp"] === "t" ? "да" : "нет"}</td> -->
|
||||||
|
<td>{payout["amount"]} {payout["code"]}</td>
|
||||||
|
<td>{payout["pan"]}</td>
|
||||||
|
<td>{payout["rate"]} {payout["code"]}</td>
|
||||||
|
<!-- <td>{payout["amount"]}</td> -->
|
||||||
|
<td>{payout["creation_time"]}</td>
|
||||||
|
<td
|
||||||
|
on:click={() => {
|
||||||
|
selectedPayout = payout;
|
||||||
|
newPayoutStatus = payout.status;
|
||||||
|
showFullInfo = true;
|
||||||
|
}}
|
||||||
|
class="flex gap-2"
|
||||||
|
>
|
||||||
|
<button class="btn btn-info">Подробнее</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{:else}
|
||||||
|
<span class="loading loading-spinner self-center"></span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if showFullInfo}
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 overflow-auto flex flex-col items-center md:p-32 p-8 pt-[68px] bg-black bg-opacity-50"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col w-full max-w-[600px] p-4 rounded-box bg-base-300">
|
||||||
|
<p class="self-center opacity-60">ID: {selectedPayout.uuid}</p>
|
||||||
|
<p
|
||||||
|
class={"self-center text-3xl " +
|
||||||
|
payoutsStatusMapColors[selectedPayout["status"]]}
|
||||||
|
>
|
||||||
|
Выплата {payoutsStatusMap[selectedPayout["status"]]}
|
||||||
|
</p>
|
||||||
|
<p class="self-center opacity-60">{selectedPayout.creation_time}</p>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<!-- <p class="text-lg font-bold">{selectedPayout.name}</p> -->
|
||||||
|
<p class="">IP заказчика: {selectedPayout.customer_ip}</p>
|
||||||
|
<p class="">Карта: {selectedPayout.pan}</p>
|
||||||
|
<p class="">ID трейдера: {selectedPayout.traider_uuid === "" ? "---":selectedPayout.traider_uuid}</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">Сумма</p>
|
||||||
|
<p>
|
||||||
|
{selectedPayout.amount}
|
||||||
|
{selectedPayout.code} ({(
|
||||||
|
Number(selectedPayout.amount) /
|
||||||
|
Number(selectedPayout.rate)
|
||||||
|
).toFixed(2)} USDT)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">Курс</p>
|
||||||
|
<p>{selectedPayout.rate} {selectedPayout.code}</p>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">ID нового трейдера:</p>
|
||||||
|
<p class="text-sm opacity-50">Оставьте пустым, чтобы оставить прежнего трейдера</p>
|
||||||
|
<input bind:value={newPayoutTraderUUID} type="text" placeholder="ID" class="input input-bordered w-full max-w-xs" />
|
||||||
|
</div>
|
||||||
|
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
|
||||||
|
<p class="text-lg font-bold">Изменить статус:</p>
|
||||||
|
<select
|
||||||
|
bind:value={newPayoutStatus}
|
||||||
|
class="select select-bordered w-full text-base"
|
||||||
|
>
|
||||||
|
<option value="0">Открыта</option>
|
||||||
|
<option value="2">Оплачена</option>
|
||||||
|
<option value="3">Отклонена</option>
|
||||||
|
<option value="1">Принята</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{#if selectedPayout["status"] === "2"}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
axios
|
||||||
|
.get(
|
||||||
|
"https://hostapay.trade/api/v1/loadFile?dispute=" +
|
||||||
|
selectedPayout["order_id"],
|
||||||
|
{
|
||||||
|
responseType: "blob",
|
||||||
|
headers: makeAuthHeaderForAxios(getAuthInfo()?.a).headers,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then(function (response) {
|
||||||
|
// Создаем ссылку для скачивания
|
||||||
|
const url = window.URL.createObjectURL(
|
||||||
|
new Blob([response.data]),
|
||||||
|
);
|
||||||
|
const contentType = response?.data?.type; // Извлекаем MIME-тип из Blob
|
||||||
|
let extension = mimeToExtension(contentType); // Получаем расширение из функции
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.style.display = "none";
|
||||||
|
a.href = url;
|
||||||
|
a.download = `Чек_${Date.now()}.${extension}`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// Очищаем
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
document.body.removeChild(a);
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-info mt-4"
|
||||||
|
>
|
||||||
|
Скачать чек
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
changePayout();
|
||||||
|
// showFullInfo = false;
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-ghost mt-4 btn-success disabled:bg-transparent"
|
||||||
|
disabled={!allowToSave}
|
||||||
|
>
|
||||||
|
Сорханить
|
||||||
|
{#if awaitChanging}
|
||||||
|
<span class="loading loading-spinner loading-md"></span>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
nullificatePayout();
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-error mt-4"
|
||||||
|
>
|
||||||
|
Обнулить выплату
|
||||||
|
{#if awaitChangingNull}
|
||||||
|
<span class="loading loading-spinner loading-md"></span>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
showFullInfo = false;
|
||||||
|
}}
|
||||||
|
class="btn btn-outline btn-ghost mt-4"
|
||||||
|
>
|
||||||
|
Закрыть
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
35
src/routes/user/change/[userToken]/+page.js
Normal file
35
src/routes/user/change/[userToken]/+page.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
|
||||||
|
import { makePost } from "$lib/tools/requests/requests";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools";
|
||||||
|
|
||||||
|
export async function load({ params }) {
|
||||||
|
if(!browser) return;
|
||||||
|
const { userToken } = params;
|
||||||
|
let userResult = await makePost(
|
||||||
|
"getUser",
|
||||||
|
{
|
||||||
|
token: userToken,
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (userResult.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
// window.location.href = "/";
|
||||||
|
redirect("/admin/");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (userResult.error) {
|
||||||
|
sayError("Не удалось настроить пользователя");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
userResult["can_trade"] = userResult["can_trade"] === "t" ? true:false;
|
||||||
|
userResult["can_trade_global"] = userResult["can_trade_global"] === "t" ? true:false;
|
||||||
|
userResult["balance"] = Number(userResult["balance"]);
|
||||||
|
userResult["bid"] = Number(userResult["bid"]);
|
||||||
|
return {
|
||||||
|
token: userToken,
|
||||||
|
userData: userResult.data
|
||||||
|
};
|
||||||
|
}
|
153
src/routes/user/change/[userToken]/+page.svelte
Normal file
153
src/routes/user/change/[userToken]/+page.svelte
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<script>
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
|
||||||
|
import { makePost } from "$lib/tools/requests/requests.js";
|
||||||
|
import { isStringEmptyOrSpaces } from "$lib/tools/strings/Strings";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster.js";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools.js";
|
||||||
|
import { jwtDecode } from "jwt-decode";
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
let { userData } = data;
|
||||||
|
let { token } = data;
|
||||||
|
let value_role = jwtDecode(getAuthInfo()?.a).role.toString();
|
||||||
|
|
||||||
|
let changingUser = false;
|
||||||
|
let canChangeUser = true;
|
||||||
|
|
||||||
|
$: checkFields(
|
||||||
|
userData.name,
|
||||||
|
userData.surname,
|
||||||
|
userData.balance,
|
||||||
|
value_role,
|
||||||
|
userData.bid
|
||||||
|
);
|
||||||
|
|
||||||
|
function checkFields(name, surname, balance, role, bid) {
|
||||||
|
// balance = Number(balance);
|
||||||
|
// bid = Number(balance);
|
||||||
|
if (isStringEmptyOrSpaces(name) || name.length < 3) {
|
||||||
|
canChangeUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((bid !== 0 && !bid) || bid < 0) {
|
||||||
|
canChangeUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isStringEmptyOrSpaces(surname) || surname.length < 3) {
|
||||||
|
canChangeUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (role === "-1") {
|
||||||
|
canChangeUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((balance !== 0 && !balance) || balance < 0) {
|
||||||
|
canChangeUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
canChangeUser = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeUserInfo() {
|
||||||
|
changingUser = true;
|
||||||
|
const userResult = await makePost(
|
||||||
|
"admin/changeUser",
|
||||||
|
{
|
||||||
|
token: token,
|
||||||
|
name: userData.name,
|
||||||
|
surname: userData.surname,
|
||||||
|
balance: userData.balance.toString(),
|
||||||
|
rate: "0",
|
||||||
|
can_trade: userData["can_trade"] ? "true":"false",
|
||||||
|
can_trade_global: userData["can_trade_global"] ? "true":"false",
|
||||||
|
role: value_role,
|
||||||
|
bid: userData.bid.toString(),
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (userResult.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
// window.location.href = "/";
|
||||||
|
redirect("/admin/");
|
||||||
|
changingUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (userResult.error) {
|
||||||
|
sayError("Не удалось изменить пользователя");
|
||||||
|
changingUser = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById("ref")?.click();
|
||||||
|
}
|
||||||
|
</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 p-4 rounded-box bg-base-300">
|
||||||
|
<div class="w-full flex flex-col gap-4 p-4 rounded-box bg-base-300">
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Имя:
|
||||||
|
<input
|
||||||
|
bind:value={userData.name}
|
||||||
|
type="text"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Фамилия:
|
||||||
|
<input
|
||||||
|
bind:value={userData.surname}
|
||||||
|
type="text"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Баланс (USDT):
|
||||||
|
<input
|
||||||
|
bind:value={userData.balance}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class={"input input-bordered flex items-center gap-2 "}>
|
||||||
|
Ставка (%):
|
||||||
|
<input
|
||||||
|
bind:value={userData.bid}
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
class="grow text-lg text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
bind:value={value_role}
|
||||||
|
class="select select-bordered w-full text-base"
|
||||||
|
>
|
||||||
|
<option disabled selected value="-1">Права доступа</option>
|
||||||
|
<option value="1">Пользователь</option>
|
||||||
|
<option value="2">Модератор</option>
|
||||||
|
<option value="4">Администратор</option>
|
||||||
|
</select>
|
||||||
|
<div class="flex p-4 items-center gap-2">
|
||||||
|
<p>Торговля:</p>
|
||||||
|
<input
|
||||||
|
bind:checked={userData["can_trade"]}
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle toggle-primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button on:click={()=>{
|
||||||
|
changeUserInfo();
|
||||||
|
}} class="btn btn-primary text-base" disabled={changingUser || !canChangeUser}>
|
||||||
|
Сохранить
|
||||||
|
{#if changingUser}
|
||||||
|
<span class="loading loading-spinner"></span>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<a href={window.location.pathname.replace("change", "profile")} class="hidden" id="ref"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
16
src/routes/user/edit/req/[userToken]/[reqID]/+page.js
Normal file
16
src/routes/user/edit/req/[userToken]/[reqID]/+page.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
|
||||||
|
import { makePost } from "$lib/tools/requests/requests";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster";
|
||||||
|
|
||||||
|
export async function load({ params }) {
|
||||||
|
if(!browser) return;
|
||||||
|
const { userToken } = params;
|
||||||
|
const { reqID } = params;
|
||||||
|
let spt = reqID.split("_");
|
||||||
|
return {
|
||||||
|
token: userToken,
|
||||||
|
reqID: spt[0],
|
||||||
|
currency: spt[1]
|
||||||
|
};
|
||||||
|
}
|
179
src/routes/user/edit/req/[userToken]/[reqID]/+page.svelte
Normal file
179
src/routes/user/edit/req/[userToken]/[reqID]/+page.svelte
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<script>
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
|
||||||
|
import { makeGet, makePost } from "$lib/tools/requests/requests.js";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster.js";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools.js";
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
let { reqID } = data;
|
||||||
|
let {currency} = data;
|
||||||
|
let { token } = data;
|
||||||
|
|
||||||
|
let reqInfo = [];
|
||||||
|
let value_bankName;
|
||||||
|
let value_cardNumber;
|
||||||
|
let value_phone;
|
||||||
|
let value_status;
|
||||||
|
let value_limit;
|
||||||
|
|
||||||
|
let canChange = false;
|
||||||
|
|
||||||
|
$: reqInfo, bindValues();
|
||||||
|
$: value_cardNumber, value_phone, value_status, checkFields();
|
||||||
|
|
||||||
|
function bindValues() {
|
||||||
|
value_bankName = reqInfo[0]?.bank_name;
|
||||||
|
value_cardNumber = reqInfo[0]?.cardnumber;
|
||||||
|
value_phone = reqInfo[0]?.phone;
|
||||||
|
value_status = reqInfo[0]?.status === "t";
|
||||||
|
value_limit = Number(reqInfo[0]?.daily_volume_limit);
|
||||||
|
value_bankName = reqInfo[0]?.bank_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkFields() {
|
||||||
|
if (value_cardNumber?.length !== 16) {
|
||||||
|
canChange = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value_phone?.length !== 11) {
|
||||||
|
canChange = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value_limit < 0) {
|
||||||
|
canChange = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canChange = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getReqInfo() {
|
||||||
|
const result = await makePost(
|
||||||
|
"getRequisite",
|
||||||
|
{
|
||||||
|
token: token,
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
// window.location.href = "/";
|
||||||
|
redirect("/admin/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось получить реквизиты пользователя");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(result.data);
|
||||||
|
reqInfo = result.data.filter((s) => s.id === reqID);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeRequisite() {
|
||||||
|
//changeRequisite
|
||||||
|
canChange = false;
|
||||||
|
const result = await makePost(
|
||||||
|
"changeRequisite",
|
||||||
|
{
|
||||||
|
...reqInfo[0],
|
||||||
|
bankname: value_bankName,
|
||||||
|
cardnumber: value_cardNumber,
|
||||||
|
phone: value_phone,
|
||||||
|
status: value_status ? "true" : "false",
|
||||||
|
limit: value_limit.toString(),
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
// window.location.href = "/";
|
||||||
|
redirect("/admin/");
|
||||||
|
// canChange = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось изменить реквизит пользователя");
|
||||||
|
canChange = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById("ref")?.click();
|
||||||
|
// canChange = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeBanks = [];
|
||||||
|
async function getActiveBanks() {
|
||||||
|
let res = await makeGet(
|
||||||
|
"client/banksList",
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (res.error) {
|
||||||
|
sayError("Не удалось получить банки. Ошибка #4143");
|
||||||
|
} else {
|
||||||
|
console.log(res.data);
|
||||||
|
activeBanks = res.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser) {
|
||||||
|
getReqInfo();
|
||||||
|
getActiveBanks();
|
||||||
|
}
|
||||||
|
</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">Изменение реквизита (ID: {reqID})</h1>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex flex-col p-4 gap-2 rounded-box bg-base-300">
|
||||||
|
{#each reqInfo as req}
|
||||||
|
<select
|
||||||
|
bind:value={value_bankName}
|
||||||
|
class="select select-bordered w-full text-base"
|
||||||
|
>
|
||||||
|
<option disabled selected value="-1">Банк</option>
|
||||||
|
{#each activeBanks as bank}
|
||||||
|
<option value={bank.id}>{bank.name}</option>
|
||||||
|
{/each}
|
||||||
|
<!-- <option value="sber">Сбер</option> -->
|
||||||
|
<!-- <option value="2">Модератор</option>
|
||||||
|
<option value="4">Администратор</option> -->
|
||||||
|
</select>
|
||||||
|
<label class="input input-bordered flex items-center gap-2">
|
||||||
|
Номер карты
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="grow text-info"
|
||||||
|
bind:value={value_cardNumber}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="input input-bordered flex items-center gap-2">
|
||||||
|
Телефон
|
||||||
|
<input type="text" class="grow text-info" bind:value={value_phone} />
|
||||||
|
</label>
|
||||||
|
<label class="input input-bordered flex items-center gap-2">
|
||||||
|
Лимит ({currency})
|
||||||
|
<input type="number" class="grow text-info" bind:value={value_limit} />
|
||||||
|
</label>
|
||||||
|
<div class="flex p-4 items-center gap-2">
|
||||||
|
<p>Активный:</p>
|
||||||
|
<input
|
||||||
|
bind:checked={value_status}
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle toggle-primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
changeRequisite();
|
||||||
|
}}
|
||||||
|
class="btn btn-primary"
|
||||||
|
disabled={!canChange}>Сохранить</button
|
||||||
|
>
|
||||||
|
<a href={"/admin/user/profile/" + token} class="hidden" id="ref"></a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
12
src/routes/user/neworder/[userToken]/+page.js
Normal file
12
src/routes/user/neworder/[userToken]/+page.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
|
||||||
|
import { makePost } from "$lib/tools/requests/requests";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster";
|
||||||
|
|
||||||
|
export async function load({ params }) {
|
||||||
|
if(!browser) return;
|
||||||
|
const { userToken } = params;
|
||||||
|
return {
|
||||||
|
token: userToken,
|
||||||
|
};
|
||||||
|
}
|
49
src/routes/user/neworder/[userToken]/+page.svelte
Normal file
49
src/routes/user/neworder/[userToken]/+page.svelte
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<script>
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
|
||||||
|
import { makePost } from "$lib/tools/requests/requests.js";
|
||||||
|
import { isStringEmptyOrSpaces } from "$lib/tools/strings/Strings";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster.js";
|
||||||
|
import { jwtDecode } from "jwt-decode";
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
let { token } = data;
|
||||||
|
|
||||||
|
let numOfOrders = 0;
|
||||||
|
</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 p-4 rounded-box bg-base-300">
|
||||||
|
<label class="input input-bordered flex items-center gap-2 max-w-lg">
|
||||||
|
Количество заявок:
|
||||||
|
<input
|
||||||
|
bind:value={numOfOrders}
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
class="grow text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{#each { length: numOfOrders } as _, index}
|
||||||
|
<div class="w-full flex flex-wrap items-center gap-2 p-4">
|
||||||
|
<p>Заявка #{index+1}</p>
|
||||||
|
<select class="select select-bordered w-full text-base">
|
||||||
|
<option disabled selected value="-1">Реквизит</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
<option value="3">3</option>
|
||||||
|
</select>
|
||||||
|
<label class="w-full input input-bordered flex items-center gap-2">
|
||||||
|
Сумма (USDT):
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
class="grow text-info"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
31
src/routes/user/profile/[userToken]/+page.js
Normal file
31
src/routes/user/profile/[userToken]/+page.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
|
||||||
|
import { makePost } from "$lib/tools/requests/requests";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools";
|
||||||
|
|
||||||
|
export async function load({ params }) {
|
||||||
|
if(!browser) return;
|
||||||
|
const { userToken } = params;
|
||||||
|
const userResult = await makePost(
|
||||||
|
"getUser",
|
||||||
|
{
|
||||||
|
token: userToken,
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (userResult.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
// window.location.href = "/";
|
||||||
|
redirect("/admin/");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (userResult.error) {
|
||||||
|
sayError("Не удалось настроить пользователя");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
token: userToken,
|
||||||
|
userData: userResult.data
|
||||||
|
};
|
||||||
|
}
|
391
src/routes/user/profile/[userToken]/+page.svelte
Normal file
391
src/routes/user/profile/[userToken]/+page.svelte
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
<script>
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
|
||||||
|
import { makePost } from "$lib/tools/requests/requests.js";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster.js";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools.js";
|
||||||
|
import Pagination from "$lib/ui-components/pagination.svelte";
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
let { userData } = data;
|
||||||
|
let { token } = data;
|
||||||
|
|
||||||
|
let userInfoReloading = false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
async function getUserOrders() {
|
||||||
|
disablePagesUserOrders = true;
|
||||||
|
const result = await makePost(
|
||||||
|
"getUserOrders",
|
||||||
|
{
|
||||||
|
token: token,
|
||||||
|
page: currentPageUserOrders - 1,
|
||||||
|
orderBy: "id",
|
||||||
|
statuses_to_send: 2,
|
||||||
|
},
|
||||||
|
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 userRequisites = [];
|
||||||
|
async function getUserRequisites() {
|
||||||
|
const result = await makePost(
|
||||||
|
"getRequisite",
|
||||||
|
{
|
||||||
|
token: token,
|
||||||
|
},
|
||||||
|
makeAuthHeaderForAxios(getAuthInfo()?.a)
|
||||||
|
);
|
||||||
|
if (result.status === 401) {
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
redirect("/admin/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.error) {
|
||||||
|
sayError("Не удалось получить реквизиты пользователя");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userRequisites = result.data;
|
||||||
|
if(!Array.isArray(userDeposits)) userRequisites = [];
|
||||||
|
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) {
|
||||||
|
getUserOrders();
|
||||||
|
getUserRequisites();
|
||||||
|
getUserDeposits();
|
||||||
|
}
|
||||||
|
</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 p-4 rounded-box bg-base-300">
|
||||||
|
<div class="flex gap-2 p-2 items-center text-lg">
|
||||||
|
<p class="font-bold">Токен:</p>
|
||||||
|
<p class="text-info">{token}</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 p-2 items-center text-lg">
|
||||||
|
<p class="font-bold">Имя и фамилия:</p>
|
||||||
|
<p class="text-info">{userData.name} {userData.surname}</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 p-2 items-center text-lg">
|
||||||
|
<p class="font-bold">Валюта:</p>
|
||||||
|
<p class="text-info">{userData?.code}</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 p-2 items-center text-lg">
|
||||||
|
<p class="font-bold">Баланс:</p>
|
||||||
|
<p class="text-info">{userData.balance} USDT</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 p-2 items-center text-lg">
|
||||||
|
<p class="font-bold">Заморожено:</p>
|
||||||
|
<p class="text-info">{userData?.insurance} USDT</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 p-2 items-center text-lg">
|
||||||
|
<p class="font-bold">Ставка:</p>
|
||||||
|
<p class="text-info">{userData.bid} %</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 p-2 items-center text-lg">
|
||||||
|
<p class="font-bold">Торговля:</p>
|
||||||
|
<p class={userData["can_trade"] === "t" ? "text-primary" : "text-error"}>
|
||||||
|
{userData["can_trade"] === "t" ? "Активна" : "Отключена"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex gap-4 mt-8">
|
||||||
|
<button
|
||||||
|
class="btn btn-neutral w-[100px]"
|
||||||
|
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">Изменить</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
deleteUser();
|
||||||
|
}}
|
||||||
|
class="btn btn-error ml-auto">Удалить</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="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["id"]}</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_id"]}</td>
|
||||||
|
<td>{requisite["daily_volume"]}</td>
|
||||||
|
<td>{requisite["daily_volume_limit"]}</td>
|
||||||
|
<td class="flex gap-2">
|
||||||
|
<a
|
||||||
|
href={"/admin/user/edit/req/" + token + "/" + requisite["id"] + "_" + 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">
|
||||||
|
<Pagination
|
||||||
|
totalPages={numPagesUserOrders}
|
||||||
|
pageChangedCallback={(n) => {
|
||||||
|
currentPageUserOrders = n;
|
||||||
|
getUserOrders();
|
||||||
|
}}
|
||||||
|
disableButtons={disablePagesUserOrders}
|
||||||
|
css={"btn-neutral"}
|
||||||
|
/>
|
||||||
|
<p class="opacity-50 text-xs mt-1">Всего страниц: {numPagesUserOrders}</p>
|
||||||
|
</div>
|
||||||
|
<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>Опции</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="">{order["status"]}</td>
|
||||||
|
<td>{order["summa"]} USDT</td>
|
||||||
|
<td>{order["rate"]} RUB</td>
|
||||||
|
<td>{order["is_sbp"] === "t" ? "да" : "нет"}</td>
|
||||||
|
<td>{order["merchant_id"]}</td>
|
||||||
|
<td>{order["creationtime"]}</td>
|
||||||
|
<td>{order["closetime"]}</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>{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>
|
73
src/routes/userslist/+page.svelte
Normal file
73
src/routes/userslist/+page.svelte
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<script>
|
||||||
|
import { browser } from "$app/environment";
|
||||||
|
import { checkAuthSync, getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
|
||||||
|
import { makeGet } from "$lib/tools/requests/requests";
|
||||||
|
import { sayError } from "$lib/tools/toaster/Toaster";
|
||||||
|
import { redirect } from "$lib/tools/url/URLTools";
|
||||||
|
|
||||||
|
let users = [];
|
||||||
|
let usersReady = false;
|
||||||
|
|
||||||
|
async function getUsers() {
|
||||||
|
if(checkAuthSync())
|
||||||
|
{
|
||||||
|
const result = await makeGet("getUsers", makeAuthHeaderForAxios(getAuthInfo()?.a));
|
||||||
|
users = result.data;
|
||||||
|
usersReady = true;
|
||||||
|
// console.log(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sayError("Данные авторизации устарели");
|
||||||
|
// window.location.href = "/";
|
||||||
|
redirect("/admin/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(browser)
|
||||||
|
{
|
||||||
|
getUsers();
|
||||||
|
}
|
||||||
|
</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 p-4 rounded-box bg-base-300">
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
{#if usersReady}
|
||||||
|
<table class="table">
|
||||||
|
<!-- head -->
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Токен</th>
|
||||||
|
<th>Имя</th>
|
||||||
|
<th>Баланс</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- row 1 -->
|
||||||
|
{#each users as user}
|
||||||
|
<tr class="hover:bg-neutral group">
|
||||||
|
<th class="font-normal">{user["token"]}</th>
|
||||||
|
<td class="font-semibold">{user["name"]} {user["surname"]}</td>
|
||||||
|
<td>{user["balance"]} USDT</td>
|
||||||
|
<td>
|
||||||
|
<a href={"/admin/user/profile/"+user["token"]} class="btn btn-outline btn-info group-hover:btn-warning">Профиль</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{:else}
|
||||||
|
<div class="flex p-4 w-full justify-center items-center">
|
||||||
|
<span class="loading loading-spinner"></span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
x
Reference in New Issue
Block a user