Compare commits

...

4 Commits

Author SHA1 Message Date
5288637d19 feat: pseudo login 2025-04-16 15:00:44 +03:00
ae73b5d256 feat: input params 2025-04-16 14:33:55 +03:00
a2b1f761ac feat: some style fixes 2025-04-16 14:33:43 +03:00
a00ec135f1 feat: empty tasks page style 2025-04-16 14:13:34 +03:00
7 changed files with 101 additions and 41 deletions

View File

@@ -40,8 +40,8 @@ const Avatar: FunctionComponent = () => {
return ( return (
<button <button
onClick={() => route("/profile/settings")} onClick={() => route("/profile/settings")}
class={cn("hidden h-32 w-full cursor-pointer overflow-hidden transition-[height] md:block", { class={cn("hidden h-32 w-full cursor-pointer overflow-hidden opacity-100 transition-[height,opacity] md:block", {
"h-0": path === "/profile/settings", "h-0 opacity-0": path === "/profile/settings",
})} })}
> >
<div <div

View File

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

View File

@@ -1,4 +1,5 @@
import { FunctionComponent } from "preact"; import { cn } from "@/utils/class-merge";
import { FunctionComponent, Ref } from "preact";
import { tv } from "tailwind-variants"; import { tv } from "tailwind-variants";
import classes from "./Input.module.scss"; import classes from "./Input.module.scss";
@@ -9,6 +10,10 @@ const input = tv({
center: "text-center", center: "text-center",
left: "text-left", left: "text-left",
}, },
"border-error": {
true: "border-red-500 placeholder:text-red-500",
false: "border-gray-300",
},
}, },
defaultVariants: { defaultVariants: {
"text-align": "left", "text-align": "left",
@@ -19,15 +24,33 @@ interface InputProps {
isPassword?: boolean; isPassword?: boolean;
placeholder?: string; placeholder?: string;
textAlign?: "center" | "left"; textAlign?: "center" | "left";
error?: string;
textRef?: Ref<HTMLInputElement> | null;
} }
const Input: FunctionComponent<InputProps> = ({ isPassword = false, placeholder = "", textAlign }: InputProps) => { const Input: FunctionComponent<InputProps> = ({
isPassword = false,
placeholder = "",
textAlign,
error = "",
textRef = null,
}: InputProps) => {
return ( return (
<input <div class="flex w-full flex-col items-center gap-1">
type={isPassword ? "password" : "text"} <input
class={input({ "text-align": textAlign })} type={isPassword ? "password" : "text"}
placeholder={placeholder} class={input({ "text-align": textAlign, "border-error": error !== "" })}
/> placeholder={placeholder}
ref={textRef}
/>
<p
class={cn("invisible h-10 w-[80%] text-center text-[0.7rem] break-words text-red-500", {
visible: error !== "",
})}
>
{error}
</p>
</div>
); );
}; };

View File

@@ -5,9 +5,9 @@
} }
.login_card { .login_card {
@apply flex min-h-[50vh] w-[95%] min-w-[300px] flex-col justify-around gap-2 rounded-[7rem] 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]; @apply flex h-fit w-[95%] min-w-[300px] flex-col justify-around rounded-[3rem] 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 {
@apply text-center text-2xl font-semibold; @apply mb-8 text-center text-2xl font-semibold;
} }

View File

@@ -5,28 +5,52 @@ 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";
import "preact/debug";
import { useRef, useState } from "preact/hooks";
import classes from "./login.module.scss"; import classes from "./login.module.scss";
const testUser = {
login: "test",
password: "test",
};
const LoginPage: FunctionComponent = () => { const LoginPage: FunctionComponent = () => {
const { isLoggedIn } = useAppContext(); const { isLoggedIn } = useAppContext();
const { route } = useLocation(); const { route } = useLocation();
return ( const loginRef = useRef<HTMLInputElement | null>(null);
const passwordRef = useRef<HTMLInputElement | null>(null);
const [loginError, setLoginError] = useState("");
const [passwordError, setPasswordError] = useState("");
const login = async () => {
if (!loginRef.current || !passwordRef.current) return;
setLoginError("");
setPasswordError("");
if (!loginRef.current.value.length || !passwordRef.current.value.length) {
if (!loginRef.current.value.length) setLoginError("Введите логин");
if (!passwordRef.current.value.length) setPasswordError("Введите пароль");
return;
}
if (loginRef.current.value !== testUser.login || passwordRef.current.value !== testUser.password) {
setLoginError("Неправильный логин или пароль");
return;
}
isLoggedIn.value = true;
localStorage.setItem("loggedIn", "true");
route("/profile/tasks", true);
};
if (isLoggedIn.value) route("/profile/tasks", true);
return !isLoggedIn.value ? (
<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="Логин" textAlign="center" /> <Input placeholder="Логин" textAlign="center" textRef={loginRef} error={loginError} />
<Input isPassword placeholder="Пароль" textAlign="center" /> <Input isPassword placeholder="Пароль" textAlign="center" textRef={passwordRef} error={passwordError} />
<Button <Button color="secondary" onClick={login}>
color="secondary"
onClick={() => {
isLoggedIn.value = true;
localStorage.setItem("loggedIn", "true");
route("/profile/tasks", true);
}}
>
Войти Войти
</Button> </Button>
</div> </div>
</div> </div>
) : (
<p>Redirecting...</p>
); );
}; };

View File

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

View File

@@ -17,25 +17,38 @@ const ProfileTasks: FunctionComponent = () => {
}, []); }, []);
return ( return (
<div class={classes.container}> <div class={classes.container}>
<div class={classes.header}>Сегодня: {getDate}</div> {example_tasks.length > 0 ? (
<div class={classes.tasks_container}> <>
{example_tasks.map((task, index) => ( <div class={classes.header}>Сегодня: {getDate}</div>
<Task name={task} key={index} checked={index % 2 === 0} /> <div class={classes.tasks_container}>
))} {example_tasks.map((task, index) => (
</div> <Task name={task} key={index} checked={index % 2 === 0} />
<div class="group fixed right-[22rem] bottom-4 hidden flex-row items-center justify-start space-x-3 overflow-x-hidden md:flex"> ))}
<div class="aspect-square h-20 cursor-pointer items-center justify-center rounded-full bg-[rgb(251,194,199,0.53)] text-9xl text-white transition-all duration-300 ease-out group-hover:ml-[12rem] hover:bg-[rgb(251,194,199,0.7)]">
<PlusIcon />
</div>
<div class="absolute left-0 my-auto flex flex-row space-x-3 opacity-0 transition-opacity duration-100 group-hover:opacity-100">
<div class="flex aspect-square h-20 cursor-pointer flex-col items-center justify-center rounded-full bg-[rgba(206,232,251,0.7)] text-xl text-gray-600 hover:bg-[rgba(206,232,251,0.9)]">
<MagnifyingGlassIcon class="size-12" />
</div> </div>
<div class="flex aspect-square h-20 cursor-pointer flex-col items-center justify-center rounded-full bg-[rgba(206,232,251,0.7)] text-xl text-gray-600 hover:bg-[rgba(206,232,251,0.9)]"> <div class="group fixed right-[22rem] bottom-4 hidden flex-row items-center justify-start space-x-3 overflow-x-hidden py-2 md:flex">
<FunnelIcon class="size-12" /> <div class="flex aspect-square h-20 cursor-pointer items-center justify-center rounded-full bg-[rgb(251,194,199,0.53)] text-9xl text-white shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-all duration-300 ease-out group-hover:ml-[12rem] hover:bg-[rgb(251,194,199,0.7)]">
<PlusIcon />
</div>
<div class="absolute left-0 my-auto flex flex-row space-x-3 opacity-0 transition-opacity duration-100 group-hover:opacity-100">
<div class="pointer-events-none flex aspect-square h-20 cursor-pointer flex-col items-center justify-center rounded-full bg-[rgba(206,232,251,0.7)] text-xl text-gray-600 shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] group-hover:pointer-events-auto hover:bg-[rgba(206,232,251,0.9)]">
<MagnifyingGlassIcon class="size-12" />
</div>
<div class="pointer-events-none flex aspect-square h-20 cursor-pointer flex-col items-center justify-center rounded-full bg-[rgba(206,232,251,0.7)] text-xl text-gray-600 shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] group-hover:pointer-events-auto hover:bg-[rgba(206,232,251,0.9)]">
<FunnelIcon class="size-12" />
</div>
</div>
</div> </div>
</div> </>
</div> ) : (
<>
<div class="flex w-full flex-1 flex-col items-center justify-center text-2xl">Начни уже сегодня!</div>
<div class="fixed right-[22rem] bottom-4 hidden flex-row items-center justify-start overflow-x-hidden py-2 md:flex">
<div class="flex aspect-square h-20 cursor-pointer items-center justify-center rounded-full bg-[rgb(251,194,199,0.53)] text-9xl text-white shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)] transition-all duration-300 ease-out hover:bg-[rgb(251,194,199,0.7)]">
<PlusIcon />
</div>
</div>
</>
)}
</div> </div>
); );
}; };