mirror of
https://github.com/Radiquum/AniX.git
synced 2025-04-30 09:59:41 +05:00
feat: add user lists statistic with donut chart
This commit is contained in:
parent
5c23f204da
commit
6e8f03e7b6
9 changed files with 276 additions and 35 deletions
|
@ -172,18 +172,31 @@ export function sinceUnixDate(unixInSeconds: number) {
|
|||
);
|
||||
}
|
||||
|
||||
export function minutesToTime(min: number) {
|
||||
export function minutesToTime(
|
||||
min: number,
|
||||
type?: "full" | "daysOnly" | "daysHours"
|
||||
) {
|
||||
const d = Math.floor(min / 1440); // 60*24
|
||||
const h = Math.floor((min - d * 1440) / 60);
|
||||
const m = Math.round(min % 60);
|
||||
|
||||
var dDisplay =
|
||||
d > 0 ? `${d} ${numberDeclension(d, "день", "дня", "дней")}, ` : "";
|
||||
d > 0 ? `${d} ${numberDeclension(d, "день", "дня", "дней")}` : "";
|
||||
var hDisplay =
|
||||
h > 0 ? `${h} ${numberDeclension(h, "час", "часа", "часов")}, ` : "";
|
||||
h > 0 ? `${h} ${numberDeclension(h, "час", "часа", "часов")}` : "";
|
||||
var mDisplay =
|
||||
m > 0 ? `${m} ${numberDeclension(m, "минута", "минуты", "минут")}` : "";
|
||||
return dDisplay + hDisplay + mDisplay;
|
||||
|
||||
if (type == "daysOnly") {
|
||||
if (d > 0) return dDisplay;
|
||||
return "? дней";
|
||||
} else if (type == "daysHours") {
|
||||
if (d > 0 && h > 0) return dDisplay + ", " + hDisplay;
|
||||
if (h > 0) return hDisplay;
|
||||
if (m > 0) return mDisplay;
|
||||
} else {
|
||||
return `${dDisplay}${h > 0 && ", " + hDisplay}${m > 0 && ", " + mDisplay}`;
|
||||
}
|
||||
}
|
||||
|
||||
const StatusList: Record<string, null | number> = {
|
||||
|
|
|
@ -11,7 +11,7 @@ export function ProfileActivity(props: {
|
|||
friendsCount: number;
|
||||
}) {
|
||||
return (
|
||||
<Card className="w-full md:w-[325px]">
|
||||
<Card className="h-full">
|
||||
<h1 className="text-2xl font-bold">Активность</h1>
|
||||
<div className="flex items-center gap-4 text-lg">
|
||||
<div>
|
||||
|
|
101
app/components/Profile/Profile.Stats.tsx
Normal file
101
app/components/Profile/Profile.Stats.tsx
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { Card } from "flowbite-react";
|
||||
import Link from "next/link";
|
||||
import ApexCharts from "apexcharts";
|
||||
import { useEffect } from "react";
|
||||
import { minutesToTime } from "#/api/utils";
|
||||
|
||||
export const ProfileStats = (props: {
|
||||
lists: Array<number>;
|
||||
watched_count: number;
|
||||
watched_time: number;
|
||||
}) => {
|
||||
const getChartOptions = () => {
|
||||
return {
|
||||
series: props.lists,
|
||||
colors: ["#66bb6c", "#b566bb", "#5c6cc0", "#ffca28", "#ef5450"],
|
||||
chart: {
|
||||
height: 240,
|
||||
width: "100%",
|
||||
type: "donut",
|
||||
},
|
||||
stroke: {
|
||||
colors: ["transparent"],
|
||||
lineCap: "",
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
labels: [`Смотрю`, `В планах`, `Просмотрено`, `Отложено`, `Брошено`],
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 640,
|
||||
options: {
|
||||
chart: {
|
||||
height: 200,
|
||||
width: 200,
|
||||
type: "donut",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
useEffect(() => {
|
||||
if (
|
||||
document.getElementById("donut-chart") &&
|
||||
typeof ApexCharts !== "undefined"
|
||||
) {
|
||||
const chart = new ApexCharts(
|
||||
document.getElementById("donut-chart"),
|
||||
getChartOptions()
|
||||
);
|
||||
chart.render();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Card className="h-full font-light">
|
||||
<h1 className="text-2xl font-bold">Статистика</h1>
|
||||
<div className="flex items-center">
|
||||
<div>
|
||||
<p className="align-center whitespace-nowrap">
|
||||
<span className="inline-block rounded w-4 h-4 bg-[#66bb6c]"></span>{" "}
|
||||
Смотрю <span className="font-bold">{props.lists[0]}</span>
|
||||
</p>
|
||||
<p className="align-center whitespace-nowrap">
|
||||
<span className="inline-block rounded w-4 h-4 bg-[#b566bb]"></span>{" "}
|
||||
В планах <span className="font-bold">{props.lists[1]}</span>
|
||||
</p>
|
||||
<p className="align-center whitespace-nowrap">
|
||||
<span className="inline-block rounded w-4 h-4 bg-[#5c6cc0]"></span>{" "}
|
||||
Просмотрено <span className="font-bold">{props.lists[2]}</span>
|
||||
</p>
|
||||
<p className="align-center whitespace-nowrap">
|
||||
<span className="inline-block rounded w-4 h-4 bg-[#ffca28]"></span>{" "}
|
||||
Отложено <span className="font-bold">{props.lists[3]}</span>
|
||||
</p>
|
||||
<p className="align-center whitespace-nowrap">
|
||||
<span className="inline-block rounded w-4 h-4 bg-[#ef5450]"></span>{" "}
|
||||
Брошено <span className="font-bold">{props.lists[4]}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div id="donut-chart"></div>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
Просмотрено серий:{" "}
|
||||
<span className="font-bold">{props.watched_count}</span>
|
||||
</p>
|
||||
<p>
|
||||
Время просмотра:{" "}
|
||||
<span className="font-bold">
|
||||
~{minutesToTime(props.watched_time, "daysHours")}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
|
@ -36,7 +36,7 @@ export const ProfileUser = (props: {
|
|||
const router = useRouter();
|
||||
console.log(props.chips);
|
||||
return (
|
||||
<Card className="w-full sm:w-[512px]">
|
||||
<Card className="h-full">
|
||||
{props.chips.hasChips && (
|
||||
<div className="flex gap-1 overflow-x-auto scrollbar-thin">
|
||||
{props.chips.isMyProfile && (
|
||||
|
|
|
@ -9,6 +9,7 @@ import { ProfileUser } from "#/components/Profile/Profile.User";
|
|||
import { ProfileBannedBanner } from "#/components/Profile/ProfileBannedBanner";
|
||||
import { ProfilePrivacyBanner } from "#/components/Profile/Profile.PrivacyBanner";
|
||||
import { ProfileActivity } from "#/components/Profile/Profile.Activity";
|
||||
import { ProfileStats } from "#/components/Profile/Profile.Stats";
|
||||
|
||||
export const ProfilePage = (props: any) => {
|
||||
const authUser = useUserStore((state) => state);
|
||||
|
@ -96,38 +97,57 @@ export const ProfilePage = (props: any) => {
|
|||
<ProfilePrivacyBanner is_privacy={isPrivacy} />
|
||||
</div>
|
||||
<div
|
||||
className={`flex flex-wrap gap-2 ${
|
||||
className={`grid grid-cols-[100%] xl:grid-cols-[630px,626px] 2xl:grid-cols-[777px,743px] gap-2 ${
|
||||
isPrivacy || user.is_banned || user.is_perm_banned ? "mt-4" : ""
|
||||
}`}
|
||||
>
|
||||
<ProfileUser
|
||||
isOnline={user.is_online}
|
||||
avatar={user.avatar}
|
||||
login={user.login}
|
||||
status={user.status}
|
||||
socials={{
|
||||
isPrivate: user.is_social_hidden,
|
||||
hasSocials: hasSocials,
|
||||
socials: socials,
|
||||
}}
|
||||
chips={{
|
||||
hasChips: hasChips,
|
||||
isMyProfile: isMyProfile,
|
||||
isVerified: user.is_verified,
|
||||
isSponsor: user.is_sponsor,
|
||||
isBlocked: user.is_blocked,
|
||||
roles: user.roles,
|
||||
}}
|
||||
rating={user.rating_score}
|
||||
/>
|
||||
{!user.is_stats_hidden && (
|
||||
<ProfileActivity
|
||||
profile_id={user.id}
|
||||
commentCount={user.comment_count}
|
||||
videoCount={user.video_count}
|
||||
collectionCount={user.collection_count}
|
||||
friendsCount={user.friend_count}
|
||||
<div className="[grid-column:1] [grid-row:1]">
|
||||
<ProfileUser
|
||||
isOnline={user.is_online}
|
||||
avatar={user.avatar}
|
||||
login={user.login}
|
||||
status={user.status}
|
||||
socials={{
|
||||
isPrivate: user.is_social_hidden,
|
||||
hasSocials: hasSocials,
|
||||
socials: socials,
|
||||
}}
|
||||
chips={{
|
||||
hasChips: hasChips,
|
||||
isMyProfile: isMyProfile,
|
||||
isVerified: user.is_verified,
|
||||
isSponsor: user.is_sponsor,
|
||||
isBlocked: user.is_blocked,
|
||||
roles: user.roles,
|
||||
}}
|
||||
rating={user.rating_score}
|
||||
/>
|
||||
</div>
|
||||
{!user.is_counts_hidden && (
|
||||
<div className="[grid-column:1] [grid-row:2]">
|
||||
<ProfileActivity
|
||||
profile_id={user.id}
|
||||
commentCount={user.comment_count}
|
||||
videoCount={user.video_count}
|
||||
collectionCount={user.collection_count}
|
||||
friendsCount={user.friend_count}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!user.is_stats_hidden && (
|
||||
<div className="[grid-column:1] xl:[grid-column:2] xl:[grid-row:span_2]">
|
||||
<ProfileStats
|
||||
lists={[
|
||||
user.watching_count,
|
||||
user.plan_count,
|
||||
user.completed_count,
|
||||
user.hold_on_count,
|
||||
user.dropped_count,
|
||||
]}
|
||||
watched_count={user.watched_episode_count}
|
||||
watched_time={user.watched_time}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue