add dashboard statistic
This commit is contained in:
@@ -31,7 +31,8 @@ export default async function RootLayout({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queryClient = getQueryClient();
|
const queryClient = getQueryClient();
|
||||||
await prefetchLayoutQueries(queryClient);
|
|
||||||
|
await prefetchLayoutQueries(queryClient);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||||
|
|||||||
@@ -0,0 +1,284 @@
|
|||||||
|
'use server'
|
||||||
|
|
||||||
|
import { API_BASE_URL } from '@/app/actions/definitions';
|
||||||
|
import { getSessionData } from '@/app/actions/session';
|
||||||
|
|
||||||
|
export async function fetchTariffStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30020,
|
||||||
|
message_body: {
|
||||||
|
active_only: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchUserFilesTopStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30019,
|
||||||
|
message_body: {
|
||||||
|
top: 5
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchUserGeneralStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30021,
|
||||||
|
message_body: {
|
||||||
|
active_only: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchSubscribeGeneralStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30024,
|
||||||
|
message_body: {
|
||||||
|
active_only: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchProtectedContentGeneralStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30022,
|
||||||
|
message_body: {
|
||||||
|
active_only: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchViolationGeneralStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30023,
|
||||||
|
message_body: {
|
||||||
|
active_only: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchTokensGeneralStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30025,
|
||||||
|
message_body: {
|
||||||
|
active_only: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchIncomeGeneralStatistic() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 30026,
|
||||||
|
message_body: {
|
||||||
|
active_only: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,7 +46,6 @@ export async function fetchUsesData(page: number, size: number, sortBy?: string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchUsesInfo(userInfo: number) {
|
export async function fetchUsesInfo(userInfo: number) {
|
||||||
console.log('fetchUsesInfo');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_URL}/api/admin/get-users`, {
|
const response = await fetch(`${API_BASE_URL}/api/admin/get-users`, {
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchIncomeGeneralStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
interface IncomePerUser {
|
||||||
|
p5: number;
|
||||||
|
p50: number;
|
||||||
|
p95: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IncomeGeneralStatistic {
|
||||||
|
total_income: number;
|
||||||
|
total_available: number;
|
||||||
|
total_hold: number;
|
||||||
|
income_per_user: IncomePerUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useIncomeGeneralStatistic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['incomeGeneralStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchIncomeGeneralStatistic()
|
||||||
|
},
|
||||||
|
select: (data: IncomeGeneralStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchProtectedContentGeneralStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
interface FilesPerUserPercentiles {
|
||||||
|
p5: number;
|
||||||
|
p50: number;
|
||||||
|
p95: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SizePerUserPercentiles {
|
||||||
|
p5_bytes: number;
|
||||||
|
p50_bytes: number;
|
||||||
|
p95_bytes: number;
|
||||||
|
p5_mb: number;
|
||||||
|
p50_mb: number;
|
||||||
|
p95_mb: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProtectedContentGeneralStatistic {
|
||||||
|
total_files: number;
|
||||||
|
total_size_bytes: number;
|
||||||
|
total_size_gb: number;
|
||||||
|
files_per_user_percentiles: FilesPerUserPercentiles;
|
||||||
|
size_per_user_percentiles: SizePerUserPercentiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useProtectedContentGeneralStatistic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['protectedContentGeneralStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchProtectedContentGeneralStatistic()
|
||||||
|
},
|
||||||
|
select: (data: ProtectedContentGeneralStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchSubscribeGeneralStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
interface SubscriptionStats {
|
||||||
|
current_active_subscriptions: number;
|
||||||
|
first_time_subscriptions: number;
|
||||||
|
renewals: number;
|
||||||
|
upgrades: number;
|
||||||
|
downgrades: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubscribeGeneralStatistic {
|
||||||
|
monthly: SubscriptionStats;
|
||||||
|
yearly: SubscriptionStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSubscribeGeneralStatistic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['subscribeGeneralStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchSubscribeGeneralStatistic()
|
||||||
|
},
|
||||||
|
select: (data: SubscribeGeneralStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchTariffStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
interface TariffItem {
|
||||||
|
tariff_name: string;
|
||||||
|
user_count: number;
|
||||||
|
usage_percent: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TariffStatistic {
|
||||||
|
tariffs: TariffItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMonitoringStats = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['tariffStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchTariffStatistic()
|
||||||
|
},
|
||||||
|
select: (data: TariffStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchTokensGeneralStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
interface BoughtPerUser {
|
||||||
|
p5: number;
|
||||||
|
p50: number;
|
||||||
|
p95: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpentPerUser {
|
||||||
|
p5: number;
|
||||||
|
p50: number;
|
||||||
|
p95: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TokensGeneralStatistic {
|
||||||
|
total_bought: number;
|
||||||
|
total_spent: number;
|
||||||
|
bought_per_user: BoughtPerUser;
|
||||||
|
spent_per_user: SpentPerUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useTokensGeneralStatistic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['tokensGeneralStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchTokensGeneralStatistic()
|
||||||
|
},
|
||||||
|
select: (data: TokensGeneralStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchUserFilesTopStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
interface UserItem {
|
||||||
|
full_name: string;
|
||||||
|
user_id: number;
|
||||||
|
count: number;
|
||||||
|
files_size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserFilesTopStatistic {
|
||||||
|
tariffs: UserItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserFilesTopStatistic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['userFilesTopStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchUserFilesTopStatistic()
|
||||||
|
},
|
||||||
|
select: (data: UserFilesTopStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchUserGeneralStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
|
||||||
|
interface UserGeneralStatistic {
|
||||||
|
total_users: number;
|
||||||
|
new_last_30_days: number;
|
||||||
|
new_previous_30_days: number;
|
||||||
|
growth_percent: number;
|
||||||
|
new_current_month: number;
|
||||||
|
new_previous_month: number;
|
||||||
|
month_growth_percent: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserGeneralStatistic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['userGeneralStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchUserGeneralStatistic()
|
||||||
|
},
|
||||||
|
select: (data: UserGeneralStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { fetchViolationGeneralStatistic } from '@/app/actions/statisticActions';
|
||||||
|
|
||||||
|
interface ViolationGeneralStatistic {
|
||||||
|
total_violations: number;
|
||||||
|
total_complaints: number;
|
||||||
|
total_law_cases: number;
|
||||||
|
complaints_without_case: number;
|
||||||
|
violations_with_complaint: number;
|
||||||
|
violations_without_complaint: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useViolationGeneralStatistic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['violationGeneralStatistic'],
|
||||||
|
queryFn: () => {
|
||||||
|
return fetchViolationGeneralStatistic()
|
||||||
|
},
|
||||||
|
select: (data: ViolationGeneralStatistic | null) => {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: false
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export function convertBytes(bytes: number) {
|
export function convertBytes(bytes: number | undefined) {
|
||||||
if (!bytes) {
|
if (!bytes) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,40 @@
|
|||||||
import { QueryClient } from '@tanstack/react-query';
|
import { QueryClient } from '@tanstack/react-query';
|
||||||
|
import { fetchTokensGeneralStatistic, fetchProtectedContentGeneralStatistic, fetchTariffStatistic, fetchUserFilesTopStatistic, fetchUserGeneralStatistic, fetchViolationGeneralStatistic } from '@/app/actions/statisticActions';
|
||||||
|
import { useSubscribeGeneralStatistic } from '@/app/hooks/react-query/dashboard/useSubscribeGeneralStatistic';
|
||||||
|
|
||||||
export async function prefetchLayoutQueries(queryClient: QueryClient) {
|
export async function prefetchLayoutQueries(queryClient: QueryClient) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
queryClient.prefetchQuery({
|
queryClient.prefetchQuery({
|
||||||
queryKey: ['userData'],
|
queryKey: ['incomeGeneralStatistic'],
|
||||||
queryFn: () => {
|
queryFn: () => fetchTokensGeneralStatistic()
|
||||||
return {
|
}),
|
||||||
test: 'testFetchUserData'
|
queryClient.prefetchQuery({
|
||||||
}
|
queryKey: ['protectedContentGeneralStatistic'],
|
||||||
}
|
queryFn: () => fetchProtectedContentGeneralStatistic()
|
||||||
})
|
}),
|
||||||
|
queryClient.prefetchQuery({
|
||||||
|
queryKey: ['subscribeGeneralStatistic'],
|
||||||
|
queryFn: () => useSubscribeGeneralStatistic()
|
||||||
|
}),
|
||||||
|
queryClient.prefetchQuery({
|
||||||
|
queryKey: ['tariffStatistic'],
|
||||||
|
queryFn: () => fetchTariffStatistic()
|
||||||
|
}),
|
||||||
|
queryClient.prefetchQuery({
|
||||||
|
queryKey: ['tokensGeneralStatistic'],
|
||||||
|
queryFn: () => fetchTokensGeneralStatistic()
|
||||||
|
}),
|
||||||
|
queryClient.prefetchQuery({
|
||||||
|
queryKey: ['userFilesTopStatistic'],
|
||||||
|
queryFn: () => fetchUserFilesTopStatistic()
|
||||||
|
}),
|
||||||
|
queryClient.prefetchQuery({
|
||||||
|
queryKey: ['userGeneralStatistic'],
|
||||||
|
queryFn: () => fetchUserGeneralStatistic()
|
||||||
|
}),
|
||||||
|
queryClient.prefetchQuery({
|
||||||
|
queryKey: ['violationGeneralStatistic'],
|
||||||
|
queryFn: () => fetchViolationGeneralStatistic()
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -478,6 +478,18 @@
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
&.user-content {
|
||||||
|
grid-template-columns: minmax(240px, 1fr) minmax(240px, 1fr) 2fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1540px) {
|
||||||
|
grid-template-columns: repeat(2, minmax(240px, 1fr));
|
||||||
|
|
||||||
|
&.user-content {
|
||||||
|
grid-template-columns: repeat(2, minmax(240px, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.stat-card {
|
.stat-card {
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -511,24 +523,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stat-card-value {
|
.stat-card-value {
|
||||||
width: 40px;
|
/* width: 40px; */
|
||||||
|
width: auto;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 20px;
|
font-size: 16px;
|
||||||
|
padding: 0 10px;
|
||||||
background: #eff6ff;
|
background: #eff6ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .stat-card-value {
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin-bottom: 4px;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.stat-card-label {
|
.stat-card-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
@@ -1310,6 +1316,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
|
|
||||||
&-active,
|
&-active,
|
||||||
&-verified {
|
&-verified {
|
||||||
color: #166534;
|
color: #166534;
|
||||||
|
|||||||
@@ -1,18 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useProtectedContentGeneralStatistic } from '@/app/hooks/react-query/dashboard/useProtectedContentGeneralStatistic'
|
||||||
|
import { convertBytes } from '@/app/lib/convertBytes';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
export default function GeneralInfoContent() {
|
export default function GeneralInfoContent() {
|
||||||
|
const { data, isLoading, isError } = useProtectedContentGeneralStatistic();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Защищенный контент (месяц/год)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Защищенный контент (месяц/год)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4
|
<h4 className="stats-title">
|
||||||
className="stats-title"
|
|
||||||
>
|
|
||||||
Защищенный контент (месяц/год)
|
Защищенный контент (месяц/год)
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className="stats-grid">
|
<div className="stats-grid user-content">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего файлов этом месяце</span>
|
{/* <span className="stat-card-title">Всего файлов этом месяце</span> */}
|
||||||
<div className="stat-card-value">0</div>
|
<span className="stat-card-title">Всего файлов</span>
|
||||||
|
<div className="stat-card-value">{data?.total_files || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
@@ -22,8 +50,9 @@ export default function GeneralInfoContent() {
|
|||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего занято места в этом месяце</span>
|
{/* <span className="stat-card-title">Всего занято места в этом месяце</span> */}
|
||||||
<div className="stat-card-value">0</div>
|
<span className="stat-card-title">Всего занято места</span>
|
||||||
|
<div className="stat-card-value">{convertBytes(data?.total_size_bytes)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
@@ -34,11 +63,19 @@ export default function GeneralInfoContent() {
|
|||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Файлов на пользователя</span>
|
<span className="stat-card-title">Файлов на пользователя</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">
|
||||||
|
{data?.files_per_user_percentiles?.p5 || 0}/
|
||||||
|
{data?.files_per_user_percentiles?.p50 || 0}/
|
||||||
|
{data?.files_per_user_percentiles?.p95 || 0}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Места на диске на пользователя</span>
|
<span className="stat-card-title">Места на диске на пользователя</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">
|
||||||
|
{convertBytes(data?.size_per_user_percentiles?.p5_bytes)} /
|
||||||
|
{convertBytes(data?.size_per_user_percentiles?.p50_bytes)} /
|
||||||
|
{convertBytes(data?.size_per_user_percentiles?.p95_bytes)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,44 +1,67 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useIncomeGeneralStatistic } from '@/app/hooks/react-query/dashboard/useIncomeGeneralStatistic';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
export default function GeneralInfoIncome() {
|
export default function GeneralInfoIncome() {
|
||||||
|
const { data, isLoading, isError } = useIncomeGeneralStatistic();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Доход (день/месяц)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Доход (день/месяц)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4
|
<h4 className="stats-title">
|
||||||
className="stats-title"
|
|
||||||
>
|
|
||||||
Доход (день/месяц)
|
Доход (день/месяц)
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className="stats-grid">
|
<div className="stats-grid">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего заработано в этом месяце</span>
|
<span className="stat-card-title">Всего заработано</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.total_income || 0} ₽</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">Доступно</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.total_available || 0} ₽</div>
|
||||||
|
</div>
|
||||||
|
<div className="stat-card-group">
|
||||||
|
<span className="stat-card-title">Заморожено</span>
|
||||||
|
<div className="stat-card-value">{data?.total_hold || 0} ₽</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего заработано на любого пользователя в этом месяце</span>
|
<span className="stat-card-title">Всего заработано на пользователя</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">
|
||||||
|
{data?.income_per_user?.p5 || 0}/
|
||||||
|
{data?.income_per_user?.p50 || 0}/
|
||||||
|
{data?.income_per_user?.p95 || 0}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">0/0/0</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="stat-card">
|
|
||||||
<div className="stat-card-group">
|
|
||||||
<span className="stat-card-title">Всего заработано на платного пользователя в этом месяце</span>
|
|
||||||
<div className="stat-card-value">0</div>
|
|
||||||
</div>
|
|
||||||
<div className="stat-card-group">
|
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
|
||||||
<div className="stat-card-value">0</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,66 +1,91 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useSubscribeGeneralStatistic } from '@/app/hooks/react-query/dashboard/useSubscribeGeneralStatistic';
|
||||||
|
|
||||||
export default function GeneralInfoSubscriptions() {
|
export default function GeneralInfoSubscriptions() {
|
||||||
|
const { data, isLoading, isError } = useSubscribeGeneralStatistic();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Активные подписчики (месяц/год)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Активные подписчики (месяц/год)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4
|
<h4 className="stats-title">Активные подписчики (месяц/год)</h4>
|
||||||
className="stats-title"
|
|
||||||
>
|
|
||||||
Активные подписчики (месяц/год)
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div className="stats-grid">
|
<div className="stats-grid">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Текущие платные подписки</span>
|
<span className="stat-card-title">Текущие платные подписки</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">
|
||||||
|
{(data?.monthly?.current_active_subscriptions || 0) + (data?.yearly?.current_active_subscriptions || 0)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">оформили впервые в этом месяце</span>
|
<span className="stat-card-title">оформили впервые в этом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.monthly?.first_time_subscriptions || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">в прошлом месяце</span>
|
<span className="stat-card-title">в прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.yearly?.first_time_subscriptions || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">продлили подпискув этом месяце</span>
|
<span className="stat-card-title">продлили подписку в этом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.monthly?.renewals || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">в прошлом месяце</span>
|
<span className="stat-card-title">в прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.yearly?.renewals || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">сделали апгрейдв этом месяце</span>
|
<span className="stat-card-title">сделали апгрейд в этом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.monthly?.upgrades || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">в прошлом месяце</span>
|
<span className="stat-card-title">в прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.yearly?.upgrades || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
{/* <div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">сделали даунгрейдв этом месяце</span>
|
<span className="stat-card-title">сделали даунгрейд в этом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.monthly?.downgrades || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">в прошлом месяце</span>
|
<span className="stat-card-title">в прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.yearly?.downgrades || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,45 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useTokensGeneralStatistic } from '@/app/hooks/react-query/dashboard/useTokensGeneralStatistic';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
export default function GeneralInfoTokens() {
|
export default function GeneralInfoTokens() {
|
||||||
|
const { data, isLoading, isError } = useTokensGeneralStatistic();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Токены (день/месяц)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Токены (день/месяц)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4
|
<h4 className="stats-title">
|
||||||
className="stats-title"
|
|
||||||
>
|
|
||||||
Токены (день/месяц)
|
Токены (день/месяц)
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className="stats-grid">
|
<div className="stats-grid">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего купленов этом месяце</span>
|
{/* <span className="stat-card-title">Всего куплено в этом месяце</span> */}
|
||||||
<div className="stat-card-value">0</div>
|
<span className="stat-card-title">Всего куплено</span>
|
||||||
|
<div className="stat-card-value">{data?.total_bought || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
@@ -22,8 +49,9 @@ export default function GeneralInfoTokens() {
|
|||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего потраченов этом месяце</span>
|
{/* <span className="stat-card-title">Всего потрачено в этом месяце</span> */}
|
||||||
<div className="stat-card-value">0</div>
|
<span className="stat-card-title">Всего потрачено</span>
|
||||||
|
<div className="stat-card-value">{data?.total_spent || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
@@ -34,22 +62,30 @@ export default function GeneralInfoTokens() {
|
|||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Куплено на пользователя в этом месяце</span>
|
<span className="stat-card-title">Куплено на пользователя в этом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">
|
||||||
|
{data?.bought_per_user?.p5 || 0}/
|
||||||
|
{data?.bought_per_user?.p50 || 0}/
|
||||||
|
{data?.bought_per_user?.p95 || 0}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">0/0/0</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Потрачено на пользователя в этом месяце</span>
|
<span className="stat-card-title">Потрачено на пользователя в этом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">
|
||||||
|
{data?.spent_per_user?.p5 || 0}/
|
||||||
|
{data?.spent_per_user?.p50 || 0}/
|
||||||
|
{data?.spent_per_user?.p95 || 0}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">0/0/0</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,51 +1,87 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useUserGeneralStatistic } from '@/app/hooks/react-query/dashboard/useUserGeneralStatistic';
|
||||||
|
|
||||||
export default function GeneralInfoUsers() {
|
export default function GeneralInfoUsers() {
|
||||||
|
const { data, isLoading, isError } = useUserGeneralStatistic();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Кол-во пользователей и динамика</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Кол-во пользователей и динамика</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderDelta = (percent: number) => {
|
||||||
|
const isPositive = percent > 0;
|
||||||
|
const isNegative = percent < 0;
|
||||||
|
const sign = isPositive ? '+' : '';
|
||||||
|
const color = isPositive ? '#10b981' : isNegative ? '#ef4444' : '#6b7280';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="stat-card-value" style={{ color }}>
|
||||||
|
{sign}{percent}%
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4
|
<h4 className="stats-title">Кол-во пользователей и динамика</h4>
|
||||||
className="stats-title"
|
|
||||||
>
|
|
||||||
Кол-во пользователей и динамика
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div className="stats-grid">
|
<div className="stats-grid">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего пользователей</span>
|
<span className="stat-card-title">Всего пользователей</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.total_users || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Новых за 30 дней</span>
|
<span className="stat-card-title">Новых за 30 дней</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.new_last_30_days || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Новых за предыдущие 30 дней</span>
|
<span className="stat-card-title">Новых за предыдущие 30 дней</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.new_previous_30_days || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Дельта</span>
|
<span className="stat-card-title">Дельта</span>
|
||||||
<div className="stat-card-value">0</div>
|
{renderDelta(data?.growth_percent || 0)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Новых за текущий месяц</span>
|
<span className="stat-card-title">Новых за текущий месяц</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.new_current_month || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Новых за прошлый месяц</span>
|
<span className="stat-card-title">Новых за прошлый месяц</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.new_previous_month || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Дельта</span>
|
<span className="stat-card-title">Дельта</span>
|
||||||
<div className="stat-card-value">0</div>
|
{renderDelta(data?.month_growth_percent || 0)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,43 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useViolationGeneralStatistic } from '@/app/hooks/react-query/dashboard/useViolationGeneralStatistic';
|
||||||
|
|
||||||
export default function GeneralInfoViolations() {
|
export default function GeneralInfoViolations() {
|
||||||
|
const { data, isLoading, isError } = useViolationGeneralStatistic();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Нарушения (день/месяц)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4 className="stats-title">Нарушения (день/месяц)</h4>
|
||||||
|
<div className="stats-grid">
|
||||||
|
<div className="stat-card">Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4
|
<h4 className="stats-title">
|
||||||
className="stats-title"
|
|
||||||
>
|
|
||||||
Нарушения (день/месяц)
|
Нарушения (день/месяц)
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div className="stats-grid">
|
<div className="stats-grid">
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Всего нарушений в этом месяце</span>
|
<span className="stat-card-title">Всего совпадений</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">{data?.total_violations || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
@@ -22,9 +47,20 @@ export default function GeneralInfoViolations() {
|
|||||||
|
|
||||||
<div className="stat-card">
|
<div className="stat-card">
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">Подано жалоб в этом месяце</span>
|
<span className="stat-card-title">Всего подано жалоб</span>
|
||||||
|
<div className="stat-card-value">{data?.total_complaints || 0}</div>
|
||||||
|
</div>
|
||||||
|
<div className="stat-card-group">
|
||||||
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">0</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="stat-card">
|
||||||
|
<div className="stat-card-group">
|
||||||
|
<span className="stat-card-title">Всего подано претензий</span>
|
||||||
|
<div className="stat-card-value">{data?.total_law_cases || 0}</div>
|
||||||
|
</div>
|
||||||
<div className="stat-card-group">
|
<div className="stat-card-group">
|
||||||
<span className="stat-card-title">В прошлом месяце</span>
|
<span className="stat-card-title">В прошлом месяце</span>
|
||||||
<div className="stat-card-value">0</div>
|
<div className="stat-card-value">0</div>
|
||||||
|
|||||||
@@ -1,4 +1,38 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useMonitoringStats } from '@/app/hooks/react-query/dashboard/useTariffStatistic';
|
||||||
|
|
||||||
export default function SubscriptionsStatistic() {
|
export default function SubscriptionsStatistic() {
|
||||||
|
const { data, isLoading, isError } = useMonitoringStats();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="admin-content">
|
||||||
|
<div className="content-header">
|
||||||
|
<h3>Статистика подписок</h3>
|
||||||
|
</div>
|
||||||
|
<div className="content-body">
|
||||||
|
<div>Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div className="admin-content">
|
||||||
|
<div className="content-header">
|
||||||
|
<h3>Статистика подписок</h3>
|
||||||
|
</div>
|
||||||
|
<div className="content-body">
|
||||||
|
<div>Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tariffs = data?.tariffs || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="admin-content">
|
<div className="admin-content">
|
||||||
<div className="content-header">
|
<div className="content-header">
|
||||||
@@ -14,36 +48,28 @@ export default function SubscriptionsStatistic() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
{tariffs.length > 0 ? (
|
||||||
<td>
|
tariffs.map((tariff, index) => (
|
||||||
<span className="badge badge-primary">
|
<tr key={index}>
|
||||||
СТАРТ
|
<td>
|
||||||
</span>
|
<span className={`badge badge-primary`}>
|
||||||
</td>
|
{tariff.tariff_name}
|
||||||
<td>0</td>
|
</span>
|
||||||
<td>0%</td>
|
</td>
|
||||||
</tr>
|
<td>{tariff.user_count}</td>
|
||||||
<tr>
|
<td>{tariff.usage_percent.toFixed(2)}%</td>
|
||||||
<td>
|
</tr>
|
||||||
<span className="badge badge-primary">
|
))
|
||||||
ДЕМО
|
) : (
|
||||||
</span>
|
<tr>
|
||||||
</td>
|
<td colSpan={3} style={{ textAlign: 'center' }}>
|
||||||
<td>0</td>
|
Нет данных
|
||||||
<td>0%</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
)}
|
||||||
<td>
|
|
||||||
<span className="badge badge-primary">
|
|
||||||
ПРОФИ
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>0</td>
|
|
||||||
<td>0%</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,37 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useUserFilesTopStatistic } from '@/app/hooks/react-query/dashboard/useUserFilesTopStatistic';
|
||||||
|
import { convertBytes } from '@/app/lib/convertBytes';
|
||||||
|
|
||||||
export default function UsersTop() {
|
export default function UsersTop() {
|
||||||
|
const { data, isLoading, isError } = useUserFilesTopStatistic();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="admin-content users-top">
|
||||||
|
<div className="content-header">
|
||||||
|
<h3>Топ пользователей</h3>
|
||||||
|
</div>
|
||||||
|
<div className="content-body">
|
||||||
|
<div>Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div className="admin-content users-top">
|
||||||
|
<div className="content-header">
|
||||||
|
<h3>Топ пользователей</h3>
|
||||||
|
</div>
|
||||||
|
<div className="content-body">
|
||||||
|
<div>Ошибка при загрузке данных</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="admin-content users-top">
|
<div className="admin-content users-top">
|
||||||
<div className="content-header">
|
<div className="content-header">
|
||||||
@@ -14,55 +47,29 @@ export default function UsersTop() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{Array.isArray(data) && data.length > 0 ? (
|
||||||
<tr>
|
data.map((user, index) => (
|
||||||
<td>
|
<tr key={user.user_id || index}>
|
||||||
<div className="users-top-user">Сидоров Станислав Андреевич</div>
|
<td>
|
||||||
<div className="users-top-mail">Info@pro-ctrl.ru</div>
|
<div className="users-top-user">{user.full_name}</div>
|
||||||
</td>
|
<div className="users-top-mail">
|
||||||
<td>0</td>
|
ID: {user.user_id}
|
||||||
<td>0 MB</td>
|
</div>
|
||||||
</tr>
|
</td>
|
||||||
|
<td>{user.count}</td>
|
||||||
<tr>
|
<td>{convertBytes(user.files_size)}</td>
|
||||||
<td>
|
</tr>
|
||||||
<div className="users-top-user">Валерка</div>
|
))
|
||||||
<div className="users-top-mail">va@no-copy.ru</div>
|
) : (
|
||||||
</td>
|
<tr>
|
||||||
<td>0</td>
|
<td colSpan={3} style={{ textAlign: 'center' }}>
|
||||||
<td>0 MB</td>
|
Нет данных
|
||||||
</tr>
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
)}
|
||||||
<td>
|
|
||||||
<div className="users-top-user">Игорь Викторович</div>
|
|
||||||
<div className="users-top-mail">igor@pro-ctrl.ru</div>
|
|
||||||
</td>
|
|
||||||
<td>0</td>
|
|
||||||
<td>0 MB</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div className="users-top-user">Синицын Игорь Владимирович</div>
|
|
||||||
<div className="users-top-mail">admin@no-copy.ru</div>
|
|
||||||
</td>
|
|
||||||
<td>0</td>
|
|
||||||
<td>0 MB</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div className="users-top-user">test test</div>
|
|
||||||
<div className="users-top-mail">test@no-copy.ru</div>
|
|
||||||
</td>
|
|
||||||
<td>0</td>
|
|
||||||
<td>0 MB</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user