feat: status-system
This commit is contained in:
@@ -4,6 +4,8 @@ import { FunctionComponent, h } from "preact";
|
|||||||
import { useLocation } from "preact-iso";
|
import { useLocation } from "preact-iso";
|
||||||
import { tv } from "tailwind-variants";
|
import { tv } from "tailwind-variants";
|
||||||
import classes from "./menu.module.scss";
|
import classes from "./menu.module.scss";
|
||||||
|
import { calculatePoints, getCurrentStatus } from "@/utils/status-system";
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
interface MenuItemProps {
|
interface MenuItemProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -34,9 +36,33 @@ const MenuItem: FunctionComponent<MenuItemProps> = ({ title, link, icon }: MenuI
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const Avatar: FunctionComponent = () => {
|
const Avatar: FunctionComponent = () => {
|
||||||
|
const [status, setStatus] = useState("");
|
||||||
const { route, path } = useLocation();
|
const { route, path } = useLocation();
|
||||||
//TODO: Move styles to scss module
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateStatus = () => {
|
||||||
|
const tasks = JSON.parse(localStorage.getItem("tasks") || "[]");
|
||||||
|
const completedTasks = tasks.filter((task: { checked: boolean }) => task.checked).length;
|
||||||
|
const points = calculatePoints(completedTasks);
|
||||||
|
setStatus(getCurrentStatus(points));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial update
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
|
// Update when tasks change
|
||||||
|
const handleStorage = (e: StorageEvent) => {
|
||||||
|
if (e.key === "tasks") {
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('storage', handleStorage);
|
||||||
|
return () => window.removeEventListener('storage', handleStorage);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => route("/profile/settings")}
|
onClick={() => route("/profile/settings")}
|
||||||
@@ -52,7 +78,7 @@ const Avatar: FunctionComponent = () => {
|
|||||||
<div class="my-5 aspect-square h-full rounded-full bg-white"></div>
|
<div class="my-5 aspect-square h-full rounded-full bg-white"></div>
|
||||||
<div class="flex flex-col items-center justify-center">
|
<div class="flex flex-col items-center justify-center">
|
||||||
<p class="text-3xl font-semibold">никнейм</p>
|
<p class="text-3xl font-semibold">никнейм</p>
|
||||||
<div class="rounded-[1rem] bg-white px-5 leading-5 font-light italic">статус</div>
|
<div class="rounded-[1rem] bg-white px-5 leading-5 font-light italic">{status}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ const markStyle = tv({
|
|||||||
|
|
||||||
const Task: FunctionComponent<TaskProps> = ({ name, checked = false, onClick = () => {}, onMarkClick = () => {} }) => {
|
const Task: FunctionComponent<TaskProps> = ({ name, checked = false, onClick = () => {}, onMarkClick = () => {} }) => {
|
||||||
return (
|
return (
|
||||||
// Временное действие для тестирования
|
|
||||||
<div class="w-[95%]">
|
<div class="w-[95%]">
|
||||||
<div class={classes.task} onClick={onClick}>
|
<div class={classes.task} onClick={onClick}>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
|
||||||
:root {
|
:root {
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
font-family: "Montserrat", sans-serif;
|
font-family: "Montserrat", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,22 +3,47 @@ import { withTitle } from "@/constructors/Component";
|
|||||||
import { UrlsTitle } from "@/enums/urls";
|
import { UrlsTitle } from "@/enums/urls";
|
||||||
import { useAppContext } from "@/providers/AuthProvider";
|
import { useAppContext } from "@/providers/AuthProvider";
|
||||||
import { cn } from "@/utils/class-merge";
|
import { cn } from "@/utils/class-merge";
|
||||||
|
import { calculatePoints, getCurrentStatus } from "@/utils/status-system";
|
||||||
import { FunctionComponent } from "preact";
|
import { FunctionComponent } from "preact";
|
||||||
import { useLocation } from "preact-iso";
|
import { useLocation } from "preact-iso";
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import classes from "./profile_settings.module.scss";
|
import classes from "./profile_settings.module.scss";
|
||||||
|
|
||||||
const ProfileSettings: FunctionComponent = () => {
|
const ProfileSettings: FunctionComponent = () => {
|
||||||
const { isLoggedIn } = useAppContext();
|
const { isLoggedIn } = useAppContext();
|
||||||
const { route } = useLocation();
|
const { route } = useLocation();
|
||||||
const status = 12;
|
const [status, setStatus] = useState(0);
|
||||||
const maxStatus = 100;
|
const maxStatus = 100;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateStatus = () => {
|
||||||
|
const tasks = JSON.parse(localStorage.getItem("tasks") || "[]");
|
||||||
|
const completedTasks = tasks.filter((task: { checked: boolean }) => task.checked).length;
|
||||||
|
const points = calculatePoints(completedTasks);
|
||||||
|
setStatus(points);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial update
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
|
// Update when tasks change
|
||||||
|
const handleStorage = (e: StorageEvent) => {
|
||||||
|
if (e.key === "tasks") {
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('storage', handleStorage);
|
||||||
|
return () => window.removeEventListener('storage', handleStorage);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={classes.container}>
|
<div class={classes.container}>
|
||||||
<div class="flex w-full flex-col items-center rounded-[4rem] bg-[linear-gradient(180.00deg,rgb(251,194,199),rgba(206,232,251,0.72)_100%)] px-7 py-5 shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] md:flex-row">
|
<div class="flex w-full flex-col items-center rounded-[4rem] bg-[linear-gradient(180.00deg,rgb(251,194,199),rgba(206,232,251,0.72)_100%)] px-7 py-5 shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] md:flex-row">
|
||||||
<div id={classes.avatar}>Аватар</div>
|
<div id={classes.avatar}>Аватар</div>
|
||||||
<div class={classes.header_block__name}>
|
<div class={classes.header_block__name}>
|
||||||
<p class="text-4xl font-semibold">Никнейм</p>
|
<p class="text-4xl font-semibold">Никнейм</p>
|
||||||
<p class="text-2xl font-light">Статус</p>
|
<p class="text-2xl font-light">{getCurrentStatus(status)}</p>
|
||||||
<div class="h-1.5 w-full overflow-hidden rounded-2xl bg-white">
|
<div class="h-1.5 w-full overflow-hidden rounded-2xl bg-white">
|
||||||
<div
|
<div
|
||||||
class={cn("relative top-0 left-0 h-2 bg-black")}
|
class={cn("relative top-0 left-0 h-2 bg-black")}
|
||||||
|
|||||||
26
src/utils/status-system.ts
Normal file
26
src/utils/status-system.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export interface StatusLevel {
|
||||||
|
name: string;
|
||||||
|
minPoints: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const STATUS_LEVELS: StatusLevel[] = [
|
||||||
|
{ name: "Завтра точно начну", minPoints: 0 },
|
||||||
|
{ name: "Всё по плану (плана нет)", minPoints: 25 },
|
||||||
|
{ name: "Гений прокрастинации (но всё сделал)", minPoints: 50 },
|
||||||
|
{ name: "Хвостоуничтожитель 3000", minPoints: 75 },
|
||||||
|
{ name: "Легенда планирования", minPoints: 90 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const POINTS_PER_TASK = 5;
|
||||||
|
|
||||||
|
export const calculatePoints = (completedTasks: number): number => {
|
||||||
|
return Math.min(completedTasks * POINTS_PER_TASK, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCurrentStatus = (points: number): string => {
|
||||||
|
const status = STATUS_LEVELS
|
||||||
|
.slice()
|
||||||
|
.reverse()
|
||||||
|
.find(level => points >= level.minPoints);
|
||||||
|
return status?.name || STATUS_LEVELS[0].name;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user