15 Commits

Author SHA1 Message Date
3d4858bcbb version: v0.0.1 2025-04-15 11:26:10 +03:00
b2feaac3e1 feat: withTitle modif 2025-04-15 11:23:48 +03:00
db9132de4d feat: pseudo-auth 2025-04-14 21:10:04 +03:00
ae9bea6c7c feat: pseudo-login 2025-04-14 21:01:33 +03:00
7f80f4790d feat: input styles 2025-04-14 20:45:46 +03:00
5d11cf0dcd feat: button styles 2025-04-14 20:45:34 +03:00
3a64f039af feat: task styles 2025-04-14 20:45:18 +03:00
2e908d01f1 feat: login styles 2025-04-14 20:45:07 +03:00
9cd3acd73c feat: menu styles 2025-04-09 12:11:09 +03:00
7c6f21081f feat: 404 page 2025-04-07 17:06:08 +03:00
e6ab12f957 feat: page titles 2025-04-07 17:06:01 +03:00
a6eec16309 feat: change default path after login 2025-04-07 16:38:17 +03:00
2ef4b967f3 feat: styles update 2025-04-07 16:37:59 +03:00
083a0f27c0 feat: scrollbar styles 2025-04-07 16:06:28 +03:00
554ee083ab feat: profile styles 2025-04-07 15:59:04 +03:00
26 changed files with 235 additions and 62 deletions

View File

@@ -4,6 +4,7 @@
"": { "": {
"name": "anti-hvost", "name": "anti-hvost",
"dependencies": { "dependencies": {
"@heroicons/react": "^2.2.0",
"@preact/signals": "^2.0.2", "@preact/signals": "^2.0.2",
"@tailwindcss/postcss": "^4.0.17", "@tailwindcss/postcss": "^4.0.17",
"@tailwindcss/vite": "^4.0.17", "@tailwindcss/vite": "^4.0.17",
@@ -147,6 +148,8 @@
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.7", "", { "dependencies": { "@eslint/core": "^0.12.0", "levn": "^0.4.1" } }, "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g=="], "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.7", "", { "dependencies": { "@eslint/core": "^0.12.0", "levn": "^0.4.1" } }, "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g=="],
"@heroicons/react": ["@heroicons/react@2.2.0", "", { "peerDependencies": { "react": ">= 16 || ^19.0.0-rc" } }, "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
@@ -583,6 +586,8 @@
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
"require-relative": ["require-relative@0.8.7", "", {}, "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg=="], "require-relative": ["require-relative@0.8.7", "", {}, "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg=="],

View File

@@ -1,7 +1,7 @@
{ {
"name": "anti-hvost", "name": "anti-hvost",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -9,6 +9,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@heroicons/react": "^2.2.0",
"@preact/signals": "^2.0.2", "@preact/signals": "^2.0.2",
"@tailwindcss/postcss": "^4.0.17", "@tailwindcss/postcss": "^4.0.17",
"@tailwindcss/vite": "^4.0.17", "@tailwindcss/vite": "^4.0.17",

View File

@@ -1,13 +1,16 @@
import { FunctionComponent } from "preact"; import { FunctionComponent } from "preact";
import { ErrorBoundary, lazy, LocationProvider, Route, Router, useLocation } from "preact-iso"; import { ErrorBoundary, lazy, LocationProvider, Route, Router, useLocation } from "preact-iso";
import "preact/debug"; import "preact/debug";
import Page404 from "./pages/404";
import LoginPage from "./pages/login"; import LoginPage from "./pages/login";
import { AppProvider } from "./providers/AuthProvider"; import { AppProvider, useAppContext } from "./providers/AuthProvider";
const HomePage: FunctionComponent = () => { const HomePage: FunctionComponent = () => {
const { route } = useLocation(); const { route } = useLocation();
route("/login"); const { isLoggedIn } = useAppContext();
return <div>Redirecting to login...</div>; if (isLoggedIn.value) route("/profile/tasks", true);
else route("/login", true);
return <div>Redirecting...</div>;
}; };
export function App() { export function App() {
@@ -19,7 +22,7 @@ export function App() {
<Route path="/" component={HomePage} /> <Route path="/" component={HomePage} />
<Route path="/login" component={LoginPage} /> <Route path="/login" component={LoginPage} />
<Route path="/profile/*" component={lazy(() => import("./pages/profile"))} /> <Route path="/profile/*" component={lazy(() => import("./pages/profile"))} />
<Route default component={() => <h1>404</h1>} /> <Route default component={() => <Page404 />} />
</Router> </Router>
</ErrorBoundary> </ErrorBoundary>
</LocationProvider> </LocationProvider>

View File

@@ -1,9 +1,13 @@
@reference "../index.scss"; @reference "../index.scss";
#container {
@apply flex min-h-auto w-screen flex-col items-center gap-4 px-2 py-3 md:min-h-screen md:w-[30rem];
}
.menu_container { .menu_container {
@apply fixed right-0 bottom-0 left-0 flex min-h-auto flex-row items-center gap-1 border-t border-t-gray-500 bg-white px-1 py-5 md:sticky md:top-0 md:right-0 md:bottom-auto md:left-auto md:min-h-screen md:w-auto md:flex-col md:gap-4 md:border-t-0 md:border-l md:border-l-gray-500 md:px-5 md:py-5; @apply fixed right-0 bottom-0 left-0 flex h-[4rem] w-full flex-1 flex-row items-center gap-1 rounded-[44px] bg-[rgba(167,213,246,0.3)] px-1 py-5 md:sticky md:top-0 md:right-0 md:bottom-auto md:left-auto md:h-full md:flex-col md:gap-4 md:border-t-0 md:px-5 md:py-5;
} }
.menu_item { .menu_item {
@apply w-full cursor-pointer rounded-md px-3 py-2 text-center hover:bg-gray-200 md:px-6; @apply flex w-full cursor-pointer flex-row items-center gap-3 rounded-full px-3 py-2 text-center hover:bg-gray-200 md:px-6;
} }

View File

@@ -1,4 +1,6 @@
import { FunctionComponent } from "preact"; import { cn } from "@/utils/class-merge";
import { CalendarDaysIcon, ListBulletIcon, UserIcon } from "@heroicons/react/24/solid";
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";
@@ -6,9 +8,10 @@ import classes from "./menu.module.scss";
interface MenuItemProps { interface MenuItemProps {
title: string; title: string;
link: string; link: string;
icon: h.JSX.Element;
} }
const MenuItem: FunctionComponent<MenuItemProps> = ({ title, link }: MenuItemProps) => { const MenuItem: FunctionComponent<MenuItemProps> = ({ title, link, icon }: MenuItemProps) => {
const { route, path } = useLocation(); const { route, path } = useLocation();
const active = path === link; const active = path === link;
const menuItemClasses = tv({ const menuItemClasses = tv({
@@ -16,7 +19,7 @@ const MenuItem: FunctionComponent<MenuItemProps> = ({ title, link }: MenuItemPro
variants: { variants: {
activity: { activity: {
active: "bg-gray-200", active: "bg-gray-200",
inactive: "bg-gray-100", inactive: "bg-white",
}, },
}, },
defaultVariants: { defaultVariants: {
@@ -25,37 +28,71 @@ const MenuItem: FunctionComponent<MenuItemProps> = ({ title, link }: MenuItemPro
}); });
return ( return (
<div class={menuItemClasses({ activity: active ? "active" : "inactive" })} onClick={() => route(link, true)}> <div class={menuItemClasses({ activity: active ? "active" : "inactive" })} onClick={() => route(link, true)}>
{icon}
{title} {title}
</div> </div>
); );
}; };
const Avatar: FunctionComponent = () => {
const { route, path } = useLocation();
//TODO: Move styles to scss module
return (
<button
onClick={() => route("/profile/settings")}
class={cn("hidden h-32 w-full cursor-pointer overflow-hidden transition-[height] md:block", {
"h-0": path === "/profile/settings",
})}
>
<div
class={cn(
"h-full flex-row items-center justify-around rounded-[44px] bg-[linear-gradient(180.00deg,rgba(249,134,143,0.5)_3.053%,rgb(228,242,252)_96.183%)] px-5 py-5 md:flex"
)}
>
<div class="my-5 aspect-square h-full rounded-full bg-white"></div>
<div class="flex flex-col items-center justify-center">
<p class="text-3xl font-semibold">Никнейм</p>
<p class="text-xl font-light">Статус</p>
</div>
</div>
</button>
);
};
interface MenuItems { interface MenuItems {
title: string; title: string;
link: string; link: string;
icon: h.JSX.Element;
} }
const Menu: FunctionComponent = () => { const Menu: FunctionComponent = () => {
//TODO: Move links to enum
const menu_items: MenuItems[] = [ const menu_items: MenuItems[] = [
{
title: "Профиль",
link: "/profile/settings",
},
{ {
title: "Задачи", title: "Задачи",
link: "/profile/tasks", link: "/profile/tasks",
icon: <ListBulletIcon class="size-10" />,
}, },
{ {
title: "Календарь", title: "Календарь",
link: "/profile/calendar", link: "/profile/calendar",
icon: <CalendarDaysIcon class="size-10" />,
},
{
title: "Профиль",
link: "/profile/settings",
icon: <UserIcon class="size-10" />,
}, },
]; ];
return ( return (
<div id={classes.container}>
<Avatar />
<div class={classes.menu_container}> <div class={classes.menu_container}>
{menu_items.map(({ title, link }) => ( {menu_items.map(({ title, link, icon }) => (
<MenuItem title={title} link={link} /> <MenuItem title={title} link={link} icon={icon} />
))} ))}
</div> </div>
</div>
); );
}; };

View File

@@ -1,5 +1,5 @@
@reference "../index.scss"; @reference "../index.scss";
.task { .task {
@apply flex h-24 w-[300px] flex-col items-center justify-center rounded-md border-2 text-xl md:w-[500px]; @apply flex h-24 w-[300px] cursor-pointer flex-row items-center justify-start gap-4 rounded-[39px] bg-[rgba(251,194,199,0.53)] px-5 py-6 text-xl shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-transform hover:scale-[1.05] hover:bg-[rgba(251,194,199,0.7)] active:scale-[1.05] md:w-[500px];
} }

View File

@@ -6,7 +6,15 @@ interface TaskProps {
} }
const Task: FunctionComponent<TaskProps> = ({ name }: TaskProps) => { const Task: FunctionComponent<TaskProps> = ({ name }: TaskProps) => {
return <div class={classes.task}>{name}</div>; return (
// Временное действие для тестирования
<button onClick={() => alert(name)}>
<div class={classes.task}>
<div class="aspect-square h-full rounded-full border bg-white"></div>
{name}
</div>
</button>
);
}; };
export default Task; export default Task;

View File

@@ -1,5 +1,5 @@
@reference '../../index.scss'; @reference '../../index.scss';
.button { .button {
@apply rounded-2xl border-2 px-4 py-3 font-semibold text-white transition-colors hover:cursor-pointer; @apply rounded-[23px] px-4 py-3 text-xl text-black shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-colors hover:cursor-pointer;
} }

View File

@@ -5,8 +5,8 @@ const button = tv({
base: classes.button, base: classes.button,
variants: { variants: {
color: { color: {
primary: "bg-blue-400 hover:bg-blue-500", primary: "bg-[rgba(206,232,251,0.7)] hover:bg-[rgba(206,232,251,0.9)]",
secondary: "bg-red-400 hover:bg-red-500", secondary: "bg-[rgba(255,251,197,0.68)] hover:bg-[rgba(255,251,197,0.9)]",
}, },
}, },
}); });

View File

@@ -1,5 +1,5 @@
@reference "../../index.scss"; @reference "../../index.scss";
.input_field { .input_field {
@apply rounded-md border border-gray-300 p-2 placeholder:transition focus:outline-0 focus:placeholder:opacity-25; @apply rounded-[23px] border border-gray-300 bg-white p-2 leading-8 placeholder:transition focus:outline-0 focus:placeholder:opacity-25;
} }

View File

@@ -0,0 +1,12 @@
import { FunctionComponent } from "preact";
import { useEffect } from "preact/hooks";
export const withTitle = <P,>(title: string, WrappedComponent: FunctionComponent<P>): FunctionComponent<P> => {
const ComponentWithTitle: FunctionComponent<P> = (props) => {
useEffect(() => {
document.title = title;
}, []);
return <WrappedComponent {...props} />;
};
return ComponentWithTitle;
};

7
src/enums/urls.ts Normal file
View File

@@ -0,0 +1,7 @@
export enum UrlsTitle {
LOGIN = "Авторизация",
PROFILE = "Профиль",
TASKS = "Задачи",
CALENDAR = "Календарь",
PAGE404 = "404",
}

View File

@@ -3,3 +3,19 @@
:root { :root {
font-family: "Montserrat", sans-serif; font-family: "Montserrat", sans-serif;
} }
@layer base {
::-webkit-scrollbar {
@apply w-2;
}
::-webkit-scrollbar-track {
@apply bg-gray-100;
}
::-webkit-scrollbar-thumb {
@apply rounded-full bg-gray-300;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-gray-500;
}
}

View File

@@ -0,0 +1,9 @@
@reference "../index.scss";
#container {
@apply flex h-screen w-full flex-col items-center justify-center;
}
#main_container {
@apply flex flex-col items-center gap-8;
}

27
src/pages/404.tsx Normal file
View File

@@ -0,0 +1,27 @@
import Button from "@/components/ui/Button";
import { withTitle } from "@/constructors/Component";
import { UrlsTitle } from "@/enums/urls";
import { FunctionComponent } from "preact";
import { useLocation } from "preact-iso";
import classes from "./404.module.scss";
const Page404: FunctionComponent = () => {
const { route } = useLocation();
return (
<div id={classes.container}>
<div id={classes.main_container}>
<p class="text-6xl font-semibold">404</p>
<Button
onClick={() => {
route("/", true);
}}
color="secondary"
>
На главную
</Button>
</div>
</div>
);
};
export default withTitle(UrlsTitle.PAGE404, Page404);

View File

@@ -1,3 +1,5 @@
import { withTitle } from "@/constructors/Component";
import { UrlsTitle } from "@/enums/urls";
import { cn } from "@/utils/class-merge"; import { cn } from "@/utils/class-merge";
import { FunctionComponent, h } from "preact"; import { FunctionComponent, h } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@@ -247,4 +249,4 @@ const BigCalendar: FunctionComponent<BigCalendarProps> = ({
); );
}; };
export default BigCalendar; export default withTitle(UrlsTitle.CALENDAR, BigCalendar);

View File

@@ -5,7 +5,7 @@
} }
.login_card { .login_card {
@apply flex w-[95%] min-w-[300px] flex-col justify-center gap-2 rounded-md border-gray-400 p-5 shadow-md md:w-[350px]; @apply flex min-h-[50vh] w-[95%] min-w-[300px] flex-col justify-around gap-2 rounded-[103px] bg-[linear-gradient(180.00deg,_rgba(239,251,194,0.53),rgb(206,232,251)_100%)] p-7 shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] md:w-[350px];
} }
.login_card_name { .login_card_name {

View File

@@ -1,5 +1,7 @@
import Button from "@/components/ui/Button"; import Button from "@/components/ui/Button";
import Input from "@/components/ui/Input"; import Input from "@/components/ui/Input";
import { withTitle } from "@/constructors/Component";
import { UrlsTitle } from "@/enums/urls";
import { useAppContext } from "@/providers/AuthProvider"; import { useAppContext } from "@/providers/AuthProvider";
import { FunctionComponent } from "preact"; import { FunctionComponent } from "preact";
import { useLocation } from "preact-iso"; import { useLocation } from "preact-iso";
@@ -11,19 +13,21 @@ const LoginPage: FunctionComponent = () => {
<div class={classes.login_container}> <div class={classes.login_container}>
<div class={classes.login_card}> <div class={classes.login_card}>
<p class={classes.login_card_name}>Антихвост</p> <p class={classes.login_card_name}>Антихвост</p>
<Input placeholder="Login" textAlign="center" /> <Input placeholder="Логин" textAlign="center" />
<Input isPassword placeholder="Password" textAlign="center" /> <Input isPassword placeholder="Пароль" textAlign="center" />
<Button <Button
color="secondary"
onClick={() => { onClick={() => {
isLoggedIn.value = true; isLoggedIn.value = true;
route("/profile/settings", true); localStorage.setItem("loggedIn", "true");
route("/profile/tasks", true);
}} }}
> >
Login Войти
</Button> </Button>
</div> </div>
</div> </div>
); );
}; };
export default LoginPage; export default withTitle(UrlsTitle.LOGIN, LoginPage);

View File

@@ -1 +1,13 @@
@reference "../index.scss"; @reference "../index.scss";
#main_container {
@apply flex h-screen flex-col md:flex-row;
}
#router_container {
@apply flex-1 overflow-y-auto px-3 pb-[6rem] break-all md:pb-5;
}
#menu_container {
@apply md:sticky md:top-0 md:h-screen;
}

View File

@@ -1,12 +1,16 @@
import Menu from "@/components/menu"; import Menu from "@/components/menu";
import { useAppContext } from "@/providers/AuthProvider";
import { FunctionComponent } from "preact"; import { FunctionComponent } from "preact";
import { lazy, Route, Router, useLocation } from "preact-iso"; import { lazy, Route, Router, useLocation } from "preact-iso";
import ids from "./profile.module.scss";
const ProfilePage: FunctionComponent = () => { const ProfilePage: FunctionComponent = () => {
const { route } = useLocation(); const { route } = useLocation();
return ( const { isLoggedIn } = useAppContext();
<div class="flex h-screen flex-col md:flex-row"> if (!isLoggedIn.value) route("/login", true);
<div class="flex-1 overflow-y-auto px-3 pb-20 break-all md:pb-0"> return isLoggedIn.value ? (
<div id={ids.main_container}>
<div id={ids.router_container}>
<Router> <Router>
<Route path="/settings" component={lazy(() => import("./profile_settings"))} /> <Route path="/settings" component={lazy(() => import("./profile_settings"))} />
<Route path="/tasks" component={lazy(() => import("./profile_tasks"))} /> <Route path="/tasks" component={lazy(() => import("./profile_tasks"))} />
@@ -20,10 +24,12 @@ const ProfilePage: FunctionComponent = () => {
/> />
</Router> </Router>
</div> </div>
<div className="md:sticky md:top-0 md:h-screen"> <div id={ids.menu_container}>
<Menu /> <Menu />
</div> </div>
</div> </div>
) : (
<p>Redirecting...</p>
); );
}; };

View File

@@ -1,16 +1,24 @@
@reference "../index.scss"; @reference "../index.scss";
.container { .container {
@apply flex h-full w-full flex-col items-center gap-4 px-6 pt-3; @apply flex h-full w-full flex-col items-center px-6 pt-3 md:flex-row md:items-start;
} }
.header_block { #avatar {
@apply flex h-[12rem] w-full flex-row justify-between; @apply flex aspect-square h-40 flex-col items-center justify-center rounded-md border;
}
.profile_container {
@apply flex h-full w-full flex-col items-center;
}
.settings_block__buttons {
@apply flex w-[300px] flex-col gap-10;
} }
.header_block__name { .header_block__name {
@apply mt-5 flex w-full flex-col items-center justify-start gap-3; @apply mt-5 flex h-fit w-full flex-col items-center justify-start gap-3;
} }
.settings_block { .settings_block {
@apply flex h-full w-full flex-col items-center justify-center; @apply mt-12 flex h-full w-full flex-col items-center justify-start md:mt-0 md:justify-center;
} }

View File

@@ -1,4 +1,6 @@
import Button from "@/components/ui/Button"; import Button from "@/components/ui/Button";
import { withTitle } from "@/constructors/Component";
import { UrlsTitle } from "@/enums/urls";
import { useAppContext } from "@/providers/AuthProvider"; import { useAppContext } from "@/providers/AuthProvider";
import { FunctionComponent } from "preact"; import { FunctionComponent } from "preact";
import { useLocation } from "preact-iso"; import { useLocation } from "preact-iso";
@@ -9,22 +11,22 @@ const ProfileSettings: FunctionComponent = () => {
const { route } = useLocation(); const { route } = useLocation();
return ( return (
<div class={classes.container}> <div class={classes.container}>
<div class={classes.header_block}> <div id={classes.avatar}>Аватар</div>
{/* Вынести классы в стили */} <div class={classes.profile_container}>
<div class="flex aspect-square h-full flex-col items-center justify-center rounded-md border">Аватар</div>
<div class={classes.header_block__name}> <div class={classes.header_block__name}>
<p class="text-5xl font-semibold">Никнейм</p> <p class="text-5xl font-semibold">Никнейм</p>
<p class="text-2xl font-light">Статус</p> <p class="text-2xl font-light">Статус</p>
</div> </div>
</div>
<div class={classes.settings_block}> <div class={classes.settings_block}>
<div class="flex w-[300px] flex-col gap-10"> <div class={classes.settings_block__buttons}>
<Button>Сменить тему</Button> <Button>Сменить тему</Button>
<Button>Настройки</Button> <Button>Настройки</Button>
<Button <Button
color="secondary" color="secondary"
onClick={() => { onClick={() => {
isLoggedIn.value = false; isLoggedIn.value = false;
localStorage.setItem("loggedIn", "false");
route("/login", true); route("/login", true);
}} }}
> >
@@ -33,7 +35,8 @@ const ProfileSettings: FunctionComponent = () => {
</div> </div>
</div> </div>
</div> </div>
</div>
); );
}; };
export default ProfileSettings; export default withTitle(UrlsTitle.PROFILE, ProfileSettings);

View File

@@ -1,7 +1,7 @@
@reference "../index.scss"; @reference "../index.scss";
.container { .container {
@apply flex h-full w-full flex-col items-center gap-4 px-6 pt-3; @apply flex h-fit w-full flex-col items-center gap-4 px-6 pt-3;
} }
.header { .header {

View File

@@ -1,4 +1,6 @@
import Task from "@/components/task"; import Task from "@/components/task";
import { withTitle } from "@/constructors/Component";
import { UrlsTitle } from "@/enums/urls";
import { FunctionComponent } from "preact"; import { FunctionComponent } from "preact";
import { useMemo } from "preact/hooks"; import { useMemo } from "preact/hooks";
import classes from "./profile_tasks.module.scss"; import classes from "./profile_tasks.module.scss";
@@ -23,4 +25,4 @@ const ProfileTasks: FunctionComponent = () => {
); );
}; };
export default ProfileTasks; export default withTitle(UrlsTitle.TASKS, ProfileTasks);

View File

@@ -1,3 +1,4 @@
import { stringToBoolean } from "@/utils/converter";
import { signal, Signal } from "@preact/signals"; import { signal, Signal } from "@preact/signals";
import { createContext, JSX } from "preact"; import { createContext, JSX } from "preact";
import { useContext } from "preact/hooks"; import { useContext } from "preact/hooks";
@@ -5,14 +6,15 @@ import { useContext } from "preact/hooks";
interface AppContextValue { interface AppContextValue {
isLoggedIn: Signal<boolean>; isLoggedIn: Signal<boolean>;
} }
const ininitialValue = stringToBoolean(localStorage.getItem("loggedIn"));
const AppContext = createContext<AppContextValue>({ const AppContext = createContext<AppContextValue>({
isLoggedIn: signal(false), isLoggedIn: signal(ininitialValue),
}); });
const AppProvider = ({ children }: { children: JSX.Element }) => { const AppProvider = ({ children }: { children: JSX.Element }) => {
const value: AppContextValue = { const value: AppContextValue = {
isLoggedIn: signal(false), isLoggedIn: signal(ininitialValue),
}; };
return <AppContext.Provider value={value}>{children}</AppContext.Provider>; return <AppContext.Provider value={value}>{children}</AppContext.Provider>;

5
src/utils/converter.ts Normal file
View File

@@ -0,0 +1,5 @@
export const stringToBoolean = (value: string | null): boolean => {
if (value === "true") return true;
if (value === "false") return false;
return false;
};