mirror of
https://github.com/Radiquum/AniX.git
synced 2025-04-04 23:34:38 +00:00
feat: mobile menu page instead of dropdown for authorized user
This commit is contained in:
parent
3ab5954197
commit
75eb4a3170
5 changed files with 327 additions and 180 deletions
|
@ -2,16 +2,10 @@
|
|||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useUserStore } from "#/store/auth";
|
||||
import { usePreferencesStore } from "#/store/preferences";
|
||||
import {
|
||||
Dropdown,
|
||||
Modal,
|
||||
Button,
|
||||
useThemeMode,
|
||||
ToggleSwitch,
|
||||
} from "flowbite-react";
|
||||
import { Dropdown } from "flowbite-react";
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { SettingsModal } from "#/components/SettingsModal/SettingsModal";
|
||||
|
||||
export const Navbar = () => {
|
||||
const pathname = usePathname();
|
||||
|
@ -110,93 +104,114 @@ export const Navbar = () => {
|
|||
})}
|
||||
</nav>
|
||||
{userStore.isAuth ? (
|
||||
<div className="flex flex-col items-center justify-end text-sm lg:gap-1 lg:justify-center lg:flex-row lg:text-base">
|
||||
<Image
|
||||
src={userStore.user.avatar}
|
||||
alt=""
|
||||
className="w-6 h-6 rounded-full"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Dropdown
|
||||
label={userStore.user.login}
|
||||
inline={true}
|
||||
dismissOnClick={true}
|
||||
theme={{
|
||||
arrowIcon:
|
||||
"ml-1 w-4 h-4 [transform:rotateX(180deg)] sm:transform-none",
|
||||
floating: {
|
||||
target: "text-sm sm:text-base",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Dropdown.Item className="text-sm md:text-base">
|
||||
<Link
|
||||
href={`/profile/${userStore.user.id}`}
|
||||
className="flex items-center gap-1"
|
||||
<>
|
||||
<div className="flex-col items-center justify-end hidden text-sm md:flex lg:gap-1 lg:justify-center lg:flex-row lg:text-base">
|
||||
<Image
|
||||
src={userStore.user.avatar}
|
||||
alt=""
|
||||
className="w-6 h-6 rounded-full"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<Dropdown
|
||||
label={userStore.user.login}
|
||||
inline={true}
|
||||
dismissOnClick={true}
|
||||
theme={{
|
||||
arrowIcon:
|
||||
"ml-1 w-4 h-4 [transform:rotateX(180deg)] sm:transform-none",
|
||||
floating: {
|
||||
target: "text-sm sm:text-base",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Dropdown.Item className="text-sm md:text-base">
|
||||
<Link
|
||||
href={`/profile/${userStore.user.id}`}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<span
|
||||
className={`iconify ${
|
||||
pathname == `/profile/${userStore.user.id}`
|
||||
? "font-bold mdi--user"
|
||||
: "mdi--user-outline"
|
||||
} w-6 h-6`}
|
||||
></span>
|
||||
<span>Профиль</span>
|
||||
</Link>
|
||||
</Dropdown.Item>
|
||||
{navLinks.map((link) => {
|
||||
return (
|
||||
<Dropdown.Item
|
||||
key={link.id + "_mobile"}
|
||||
className={`${
|
||||
link.mobileMenu ? "block sm:hidden" : "hidden"
|
||||
} text-sm md:text-base`}
|
||||
>
|
||||
<Link
|
||||
href={link.href}
|
||||
className={`flex items-center gap-1`}
|
||||
>
|
||||
<span
|
||||
className={`iconify ${
|
||||
pathname == link.href
|
||||
? link.iconActive
|
||||
: link.icon
|
||||
} w-6 h-6`}
|
||||
></span>
|
||||
<span
|
||||
className={`${
|
||||
pathname == link.href ? "font-bold" : ""
|
||||
}`}
|
||||
>
|
||||
{link.title}
|
||||
</span>
|
||||
</Link>
|
||||
</Dropdown.Item>
|
||||
);
|
||||
})}
|
||||
<Dropdown.Item
|
||||
onClick={() => {
|
||||
setIsSettingModalOpen(true);
|
||||
}}
|
||||
className="flex items-center gap-1 text-sm md:text-base"
|
||||
>
|
||||
<span
|
||||
className={`iconify ${
|
||||
pathname == `/profile/${userStore.user.id}`
|
||||
? "font-bold mdi--user"
|
||||
: "mdi--user-outline"
|
||||
} w-6 h-6`}
|
||||
className={`iconify material-symbols--settings-outline-rounded w-6 h-6`}
|
||||
></span>
|
||||
<span>Профиль</span>
|
||||
</Link>
|
||||
</Dropdown.Item>
|
||||
{navLinks.map((link) => {
|
||||
return (
|
||||
<Dropdown.Item
|
||||
key={link.id + "_mobile"}
|
||||
className={`${
|
||||
link.mobileMenu ? "block sm:hidden" : "hidden"
|
||||
} text-sm md:text-base`}
|
||||
>
|
||||
<Link
|
||||
href={link.href}
|
||||
className={`flex items-center gap-1`}
|
||||
>
|
||||
<span
|
||||
className={`iconify ${
|
||||
pathname == link.href ? link.iconActive : link.icon
|
||||
} w-6 h-6`}
|
||||
></span>
|
||||
<span
|
||||
className={`${
|
||||
pathname == link.href ? "font-bold" : ""
|
||||
}`}
|
||||
>
|
||||
{link.title}
|
||||
</span>
|
||||
</Link>
|
||||
</Dropdown.Item>
|
||||
);
|
||||
})}
|
||||
<Dropdown.Item
|
||||
onClick={() => {
|
||||
setIsSettingModalOpen(true);
|
||||
}}
|
||||
className="flex items-center gap-1 text-sm md:text-base"
|
||||
<span>Настройки</span>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
onClick={() => {
|
||||
userStore.logout();
|
||||
}}
|
||||
className="flex items-center gap-1 text-sm md:text-base"
|
||||
>
|
||||
<span
|
||||
className={`iconify material-symbols--logout-rounded w-6 h-6`}
|
||||
></span>
|
||||
<span>Выйти</span>
|
||||
</Dropdown.Item>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div className="block md:hidden">
|
||||
<Link
|
||||
href={"/menu"}
|
||||
className={`flex flex-col items-center justify-end text-sm md:hidden lg:gap-1 lg:justify-center lg:flex-row lg:text-base ${
|
||||
pathname == "/menu" ? "font-bold" : ""
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`iconify material-symbols--settings-outline-rounded w-6 h-6`}
|
||||
></span>
|
||||
<span>Настройки</span>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
onClick={() => {
|
||||
userStore.logout();
|
||||
}}
|
||||
className="flex items-center gap-1 text-sm md:text-base"
|
||||
>
|
||||
<span
|
||||
className={`iconify material-symbols--logout-rounded w-6 h-6`}
|
||||
></span>
|
||||
<span>Выйти</span>
|
||||
</Dropdown.Item>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<Image
|
||||
src={userStore.user.avatar}
|
||||
alt=""
|
||||
className="w-6 h-6 rounded-full"
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<p>{userStore.user.login}</p>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Dropdown
|
||||
label=""
|
||||
|
@ -258,91 +273,3 @@ export const Navbar = () => {
|
|||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SettingsModal = (props: { isOpen: boolean; setIsOpen: any }) => {
|
||||
const preferenceStore = usePreferencesStore();
|
||||
|
||||
const { computedMode, setMode } = useThemeMode();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dismissible
|
||||
show={props.isOpen}
|
||||
onClose={() => props.setIsOpen(false)}
|
||||
>
|
||||
<Modal.Header>Настройки</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-bold dark:text-white">Тема</p>
|
||||
<Button.Group>
|
||||
<Button
|
||||
color={computedMode == "light" ? "blue" : "gray"}
|
||||
onClick={() => setMode("light")}
|
||||
>
|
||||
Светлая
|
||||
</Button>
|
||||
<Button
|
||||
color={computedMode == "dark" ? "blue" : "gray"}
|
||||
onClick={() => setMode("dark")}
|
||||
>
|
||||
Темная
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-bold dark:text-white">
|
||||
Показывать список изменений
|
||||
</p>
|
||||
<ToggleSwitch
|
||||
color="blue"
|
||||
theme={{
|
||||
toggle: {
|
||||
checked: {
|
||||
color: {
|
||||
blue: "border-blue-700 bg-blue-700",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
onChange={() =>
|
||||
preferenceStore.setFlags({
|
||||
showChangelog: !preferenceStore.flags.showChangelog,
|
||||
})
|
||||
}
|
||||
checked={preferenceStore.flags.showChangelog}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-bold dark:text-white">
|
||||
Отправка аналитики
|
||||
</p>
|
||||
<p className="text-gray-500 dark:text-gray-400">
|
||||
Требуется перезагрузка для применения
|
||||
</p>
|
||||
</div>
|
||||
<ToggleSwitch
|
||||
color="blue"
|
||||
theme={{
|
||||
toggle: {
|
||||
checked: {
|
||||
color: {
|
||||
blue: "border-blue-700 bg-blue-700",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
onChange={() =>
|
||||
preferenceStore.setFlags({
|
||||
enableAnalytics: !preferenceStore.flags.enableAnalytics,
|
||||
})
|
||||
}
|
||||
checked={preferenceStore.flags.enableAnalytics}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
95
app/components/SettingsModal/SettingsModal.tsx
Normal file
95
app/components/SettingsModal/SettingsModal.tsx
Normal file
|
@ -0,0 +1,95 @@
|
|||
"use client";
|
||||
|
||||
import { usePreferencesStore } from "#/store/preferences";
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
useThemeMode,
|
||||
ToggleSwitch,
|
||||
} from "flowbite-react";
|
||||
|
||||
export const SettingsModal = (props: { isOpen: boolean; setIsOpen: any }) => {
|
||||
const preferenceStore = usePreferencesStore();
|
||||
|
||||
const { computedMode, setMode } = useThemeMode();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dismissible
|
||||
show={props.isOpen}
|
||||
onClose={() => props.setIsOpen(false)}
|
||||
>
|
||||
<Modal.Header>Настройки</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-bold dark:text-white">Тема</p>
|
||||
<Button.Group>
|
||||
<Button
|
||||
color={computedMode == "light" ? "blue" : "gray"}
|
||||
onClick={() => setMode("light")}
|
||||
>
|
||||
Светлая
|
||||
</Button>
|
||||
<Button
|
||||
color={computedMode == "dark" ? "blue" : "gray"}
|
||||
onClick={() => setMode("dark")}
|
||||
>
|
||||
Темная
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="font-bold dark:text-white">
|
||||
Показывать список изменений
|
||||
</p>
|
||||
<ToggleSwitch
|
||||
color="blue"
|
||||
theme={{
|
||||
toggle: {
|
||||
checked: {
|
||||
color: {
|
||||
blue: "border-blue-700 bg-blue-700",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
onChange={() =>
|
||||
preferenceStore.setFlags({
|
||||
showChangelog: !preferenceStore.flags.showChangelog,
|
||||
})
|
||||
}
|
||||
checked={preferenceStore.flags.showChangelog}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-bold dark:text-white">Отправка аналитики</p>
|
||||
<p className="text-gray-500 dark:text-gray-400">
|
||||
Требуется перезагрузка для применения
|
||||
</p>
|
||||
</div>
|
||||
<ToggleSwitch
|
||||
color="blue"
|
||||
theme={{
|
||||
toggle: {
|
||||
checked: {
|
||||
color: {
|
||||
blue: "border-blue-700 bg-blue-700",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
onChange={() =>
|
||||
preferenceStore.setFlags({
|
||||
enableAnalytics: !preferenceStore.flags.enableAnalytics,
|
||||
})
|
||||
}
|
||||
checked={preferenceStore.flags.enableAnalytics}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
};
|
9
app/menu/page.tsx
Normal file
9
app/menu/page.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
export const metadata = {
|
||||
title: "Меню",
|
||||
};
|
||||
|
||||
import { MenuPage } from "#/pages/MobileMenuPage";
|
||||
|
||||
export default function Index() {
|
||||
return <MenuPage />;
|
||||
}
|
115
app/pages/MobileMenuPage.tsx
Normal file
115
app/pages/MobileMenuPage.tsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
"use client";
|
||||
import { Card } from "flowbite-react";
|
||||
import { useUserStore } from "#/store/auth";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { SettingsModal } from "#/components/SettingsModal/SettingsModal";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const MenuPage = () => {
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
const [isSettingModalOpen, setIsSettingModalOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userStore.user) {
|
||||
router.push("/");
|
||||
}
|
||||
}, [userStore.user]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{userStore.user && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Link href={`/profile/${userStore.user.id}`} className="flex-1">
|
||||
<Card>
|
||||
<div className="flex items-center gap-4">
|
||||
<img
|
||||
src={userStore.user.avatar}
|
||||
alt=""
|
||||
className="w-16 h-16 rounded-full sm:w-28 sm:h-28"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xl sm:text-2xl">
|
||||
{userStore.user.login}
|
||||
</p>
|
||||
<p className="text-sm text-gray-400 whitespace-pre sm:text-base dark:text-gray-300">
|
||||
{userStore.user.status}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
<div className="flex flex-1 h-full gap-2 sm:flex-col">
|
||||
<button
|
||||
className="flex-1"
|
||||
onClick={() => {
|
||||
userStore.logout();
|
||||
}}
|
||||
>
|
||||
<Card>
|
||||
<div className="flex items-center justify-center gap-2 sm:justify-start">
|
||||
<span
|
||||
className={`iconify material-symbols--logout-rounded w-6 h-6 text-red-500`}
|
||||
></span>
|
||||
<p className="text-red-500">Выйти</p>
|
||||
</div>
|
||||
</Card>
|
||||
</button>
|
||||
<button
|
||||
className="flex-1"
|
||||
onClick={() => {
|
||||
setIsSettingModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<Card className="flex-1">
|
||||
<div className="flex items-center justify-center gap-2 sm:justify-start">
|
||||
<span
|
||||
className={`iconify material-symbols--settings-outline-rounded w-6 h-6`}
|
||||
></span>
|
||||
<p>Настройки</p>
|
||||
</div>
|
||||
</Card>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/favorites" className="flex-1">
|
||||
<Card>
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={`iconify material-symbols--favorite-outline w-6 h-6`}
|
||||
></span>
|
||||
<p>Избранное</p>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
<Link href="/collections" className="flex-1">
|
||||
<Card>
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={`iconify material-symbols--collections-bookmark-outline w-6 h-6`}
|
||||
></span>
|
||||
<p>Коллекции</p>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
<Link href="/history" className="flex-1">
|
||||
<Card>
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={`iconify material-symbols--history w-6 h-6`}
|
||||
></span>
|
||||
<p>История</p>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
<SettingsModal
|
||||
isOpen={isSettingModalOpen}
|
||||
setIsOpen={setIsSettingModalOpen}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -3,10 +3,11 @@
|
|||
## Добавлено
|
||||
|
||||
- Редактирование профиля
|
||||
- Страница меню, для авторизованного пользователя, вместо выпадающего списка на мобильных устройствах
|
||||
|
||||
## Изменено
|
||||
|
||||
- Изменён вид каруселей
|
||||
- Изменён вид каруселей релизов для ПК
|
||||
|
||||
## Исправлено
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue