mirror of
https://github.com/sqlchat/sqlchat.git
synced 2025-09-28 02:24:49 +08:00
feat: Use separate /setting route page instead of modal
Better extensibility as we may overlay additional modal in setting
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -29,5 +29,6 @@
|
|||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.formatOnSaveMode": "file"
|
"editor.formatOnSaveMode": "file"
|
||||||
}
|
},
|
||||||
|
"i18n-ally.keystyle": "nested"
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
|
"@headlessui/react": "^1.7.14",
|
||||||
|
"@heroicons/react": "^2.0.17",
|
||||||
"@mui/material": "^5.11.14",
|
"@mui/material": "^5.11.14",
|
||||||
"@mui/styled-engine-sc": "^5.11.11",
|
"@mui/styled-engine-sc": "^5.11.11",
|
||||||
"@prisma/client": "4.13.0",
|
"@prisma/client": "4.13.0",
|
||||||
|
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
@ -7,6 +7,12 @@ dependencies:
|
|||||||
'@emotion/styled':
|
'@emotion/styled':
|
||||||
specifier: ^11.10.6
|
specifier: ^11.10.6
|
||||||
version: 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.28)(react@18.2.0)
|
version: 11.10.6(@emotion/react@11.10.6)(@types/react@18.0.28)(react@18.2.0)
|
||||||
|
'@headlessui/react':
|
||||||
|
specifier: ^1.7.14
|
||||||
|
version: 1.7.14(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@heroicons/react':
|
||||||
|
specifier: ^2.0.17
|
||||||
|
version: 2.0.17(react@18.2.0)
|
||||||
'@mui/material':
|
'@mui/material':
|
||||||
specifier: ^5.11.14
|
specifier: ^5.11.14
|
||||||
version: 5.11.14(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
|
version: 5.11.14(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@ -645,6 +651,26 @@ packages:
|
|||||||
- '@types/react'
|
- '@types/react'
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@headlessui/react@1.7.14(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-znzdq9PG8rkwcu9oQ2FwIy0ZFtP9Z7ycS+BAqJ3R5EIqC/0bJGvhT7193rFf+45i9nnPsYvCQVW4V/bB9Xc+gA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16 || ^17 || ^18
|
||||||
|
react-dom: ^16 || ^17 || ^18
|
||||||
|
dependencies:
|
||||||
|
client-only: 0.0.1
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@heroicons/react@2.0.17(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-90GMZktkA53YbNzHp6asVEDevUQCMtxWH+2UK2S8OpnLEu7qckTJPhNxNQG52xIR1WFTwFqtH6bt7a60ZNcLLA==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>= 16'
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@humanwhocodes/config-array@0.9.5:
|
/@humanwhocodes/config-array@0.9.5:
|
||||||
resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==}
|
resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
|
@ -2,25 +2,20 @@ import { Drawer } from "@mui/material";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useConnectionStore, useLayoutStore, ResponsiveWidth } from "@/store";
|
import { useConnectionStore, useLayoutStore, ResponsiveWidth } from "@/store";
|
||||||
|
import Link from "next/link";
|
||||||
import Select from "./kit/Select";
|
import Select from "./kit/Select";
|
||||||
import Tooltip from "./kit/Tooltip";
|
import Tooltip from "./kit/Tooltip";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import DarkModeSwitch from "./DarkModeSwitch";
|
import DarkModeSwitch from "./DarkModeSwitch";
|
||||||
import SettingModal from "./SettingModal";
|
|
||||||
import ConversationList from "./Sidebar/ConversationList";
|
import ConversationList from "./Sidebar/ConversationList";
|
||||||
import ConnectionList from "./Sidebar/ConnectionList";
|
import ConnectionList from "./Sidebar/ConnectionList";
|
||||||
|
|
||||||
interface State {
|
interface State {}
|
||||||
showSettingModal: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConnectionSidebar = () => {
|
const ConnectionSidebar = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
const [state, setState] = useState<State>({
|
|
||||||
showSettingModal: false,
|
|
||||||
});
|
|
||||||
const [isRequestingDatabase, setIsRequestingDatabase] =
|
const [isRequestingDatabase, setIsRequestingDatabase] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const currentConnectionCtx = connectionStore.currentConnectionCtx;
|
const currentConnectionCtx = connectionStore.currentConnectionCtx;
|
||||||
@ -60,13 +55,6 @@ const ConnectionSidebar = () => {
|
|||||||
}
|
}
|
||||||
}, [currentConnectionCtx?.connection]);
|
}, [currentConnectionCtx?.connection]);
|
||||||
|
|
||||||
const toggleSettingModal = (show = true) => {
|
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
showSettingModal: show,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDatabaseNameSelect = async (databaseName: string) => {
|
const handleDatabaseNameSelect = async (databaseName: string) => {
|
||||||
if (!currentConnectionCtx?.connection) {
|
if (!currentConnectionCtx?.connection) {
|
||||||
return;
|
return;
|
||||||
@ -101,13 +89,13 @@ const ConnectionSidebar = () => {
|
|||||||
<div className="w-full flex flex-col justify-end items-center">
|
<div className="w-full flex flex-col justify-end items-center">
|
||||||
<DarkModeSwitch />
|
<DarkModeSwitch />
|
||||||
<Tooltip title={t("common.setting")} side="right">
|
<Tooltip title={t("common.setting")} side="right">
|
||||||
<button
|
<Link
|
||||||
className=" w-10 h-10 p-1 rounded-full flex flex-row justify-center items-center hover:bg-gray-100 dark:hover:bg-zinc-700"
|
className=" w-10 h-10 p-1 rounded-full flex flex-row justify-center items-center hover:bg-gray-100 dark:hover:bg-zinc-700"
|
||||||
data-tip={t("common.setting")}
|
data-tip={t("common.setting")}
|
||||||
onClick={() => toggleSettingModal(true)}
|
href="/setting"
|
||||||
>
|
>
|
||||||
<Icon.IoMdSettings className="text-gray-600 dark:text-gray-300 w-6 h-auto" />
|
<Icon.IoMdSettings className="text-gray-600 dark:text-gray-300 w-6 h-auto" />
|
||||||
</button>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -179,10 +167,6 @@ const ConnectionSidebar = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
{state.showSettingModal && (
|
|
||||||
<SettingModal close={() => toggleSettingModal(false)} />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocalStorage } from "react-use";
|
import { useLocalStorage } from "react-use";
|
||||||
|
import Link from "next/link";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import SettingModal from "./SettingModal";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -15,7 +14,6 @@ const QuotaOverflowBanner = (props: Props) => {
|
|||||||
"hide-quota-overflow-banner",
|
"hide-quota-overflow-banner",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
const [showSettingModal, setShowSettingModal] = useState(false);
|
|
||||||
const show = !hideBanner;
|
const show = !hideBanner;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -27,12 +25,9 @@ const QuotaOverflowBanner = (props: Props) => {
|
|||||||
>
|
>
|
||||||
<div className="text-sm leading-6 pr-4 cursor-pointer">
|
<div className="text-sm leading-6 pr-4 cursor-pointer">
|
||||||
{t("banner.quota-overflow")}{" "}
|
{t("banner.quota-overflow")}{" "}
|
||||||
<button
|
<Link className="ml-1 underline hover:opacity-80" href="/setting">
|
||||||
className="ml-1 underline hover:opacity-80"
|
|
||||||
onClick={() => setShowSettingModal(true)}
|
|
||||||
>
|
|
||||||
{t("banner.use-my-key")}
|
{t("banner.use-my-key")}
|
||||||
</button>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="absolute right-2 sm:right-4 opacity-60 hover:opacity-100"
|
className="absolute right-2 sm:right-4 opacity-60 hover:opacity-100"
|
||||||
@ -41,10 +36,6 @@ const QuotaOverflowBanner = (props: Props) => {
|
|||||||
<Icon.BiX className="w-6 h-auto" />
|
<Icon.BiX className="w-6 h-auto" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showSettingModal && (
|
|
||||||
<SettingModal close={() => setShowSettingModal(false)} />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import Modal from "./kit/Modal";
|
|
||||||
import Icon from "./Icon";
|
|
||||||
import WeChatQRCodeView from "./WeChatQRCodeView";
|
|
||||||
import ClearDataButton from "./ClearDataButton";
|
|
||||||
import LocaleSelector from "./LocaleSelector";
|
|
||||||
import ThemeSelector from "./ThemeSelector";
|
|
||||||
import OpenAIApiConfigView from "./OpenAIApiConfigView";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
close: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SettingModal = (props: Props) => {
|
|
||||||
const { close } = props;
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal title={t("setting.self")} onClose={close}>
|
|
||||||
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
|
|
||||||
<div className="w-full flex flex-row justify-start items-start flex-wrap gap-2">
|
|
||||||
<a
|
|
||||||
href="https://discord.gg/z6kakemDjm"
|
|
||||||
className="w-auto px-4 py-2 rounded-full bg-indigo-600 text-white text-sm font-medium flex flex-row justify-center items-center hover:underline hover:shadow"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Icon.BsDiscord className="w-4 h-auto mr-1" />
|
|
||||||
{t("social.join-discord-channel")}
|
|
||||||
</a>
|
|
||||||
<WeChatQRCodeView />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 className="pl-4 text-sm text-gray-500">
|
|
||||||
{t("setting.basic.self")}
|
|
||||||
</h3>
|
|
||||||
<div className="w-full border border-gray-200 dark:border-zinc-700 p-4 rounded-lg space-y-2">
|
|
||||||
<div className="w-full flex flex-row justify-between items-center gap-2">
|
|
||||||
<span>{t("setting.basic.language")}</span>
|
|
||||||
<LocaleSelector />
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex flex-row justify-between items-center gap-2">
|
|
||||||
<span>{t("setting.theme.self")}</span>
|
|
||||||
<ThemeSelector />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<OpenAIApiConfigView />
|
|
||||||
|
|
||||||
<h3 className="pl-4 text-sm text-gray-500">{t("setting.data.self")}</h3>
|
|
||||||
<div className="w-full border border-red-200 dark:border-zinc-700 p-4 rounded-lg">
|
|
||||||
<div className="w-full flex flex-row justify-between items-center gap-2">
|
|
||||||
<span>{t("setting.data.clear-all-data")}</span>
|
|
||||||
<ClearDataButton />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SettingModal;
|
|
52
src/components/SettingView.tsx
Normal file
52
src/components/SettingView.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Icon from "./Icon";
|
||||||
|
import WeChatQRCodeView from "./WeChatQRCodeView";
|
||||||
|
import ClearDataButton from "./ClearDataButton";
|
||||||
|
import LocaleSelector from "./LocaleSelector";
|
||||||
|
import ThemeSelector from "./ThemeSelector";
|
||||||
|
import OpenAIApiConfigView from "./OpenAIApiConfigView";
|
||||||
|
|
||||||
|
const SettingView = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full flex flex-col justify-start items-start space-y-3 pt-4">
|
||||||
|
<div className="w-full flex flex-row justify-start items-start flex-wrap gap-2">
|
||||||
|
<a
|
||||||
|
href="https://discord.gg/z6kakemDjm"
|
||||||
|
className="w-auto px-4 py-2 rounded-full bg-indigo-600 text-white text-sm font-medium flex flex-row justify-center items-center hover:underline hover:shadow"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Icon.BsDiscord className="w-4 h-auto mr-1" />
|
||||||
|
{t("social.join-discord-channel")}
|
||||||
|
</a>
|
||||||
|
<WeChatQRCodeView />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="pl-4 text-sm text-gray-500">{t("setting.basic.self")}</h3>
|
||||||
|
<div className="w-full border border-gray-200 dark:border-zinc-700 p-4 rounded-lg space-y-2">
|
||||||
|
<div className="w-full flex flex-row justify-between items-center gap-2">
|
||||||
|
<span>{t("setting.basic.language")}</span>
|
||||||
|
<LocaleSelector />
|
||||||
|
</div>
|
||||||
|
<div className="w-full flex flex-row justify-between items-center gap-2">
|
||||||
|
<span>{t("setting.theme.self")}</span>
|
||||||
|
<ThemeSelector />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<OpenAIApiConfigView />
|
||||||
|
|
||||||
|
<h3 className="pl-4 text-sm text-gray-500">{t("setting.data.self")}</h3>
|
||||||
|
<div className="w-full border border-red-200 dark:border-zinc-700 p-4 rounded-lg">
|
||||||
|
<div className="w-full flex flex-row justify-between items-center gap-2">
|
||||||
|
<span>{t("setting.data.clear-all-data")}</span>
|
||||||
|
<ClearDataButton />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingView;
|
@ -10,7 +10,6 @@ import CreateConnectionModal from "../CreateConnectionModal";
|
|||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
showCreateConnectionModal: boolean;
|
showCreateConnectionModal: boolean;
|
||||||
showSettingModal: boolean;
|
|
||||||
showUpdateConversationModal: boolean;
|
showUpdateConversationModal: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +18,6 @@ const ConnectionList = () => {
|
|||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
showCreateConnectionModal: false,
|
showCreateConnectionModal: false,
|
||||||
showSettingModal: false,
|
|
||||||
showUpdateConversationModal: false,
|
showUpdateConversationModal: false,
|
||||||
});
|
});
|
||||||
const [editConnectionModalContext, setEditConnectionModalContext] =
|
const [editConnectionModalContext, setEditConnectionModalContext] =
|
||||||
|
195
src/pages/setting/index.tsx
Normal file
195
src/pages/setting/index.tsx
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import { NextPage } from "next";
|
||||||
|
import { Fragment, useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
|
import {
|
||||||
|
ArrowUturnLeftIcon,
|
||||||
|
Bars3Icon,
|
||||||
|
Cog6ToothIcon,
|
||||||
|
XMarkIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
|
import SettingView from "../../components/SettingView";
|
||||||
|
|
||||||
|
function classNames(...classes: string[]) {
|
||||||
|
return classes.filter(Boolean).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
const SettingPage: NextPage = () => {
|
||||||
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
|
||||||
|
const navigation = [
|
||||||
|
{ name: "Back", href: "/", icon: ArrowUturnLeftIcon, current: false },
|
||||||
|
{ name: "General", href: "/setting", icon: Cog6ToothIcon, current: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/*
|
||||||
|
This example requires updating your template:
|
||||||
|
|
||||||
|
```
|
||||||
|
<html class="h-full bg-white">
|
||||||
|
<body class="h-full">
|
||||||
|
```
|
||||||
|
*/}
|
||||||
|
<div>
|
||||||
|
<Transition.Root show={sidebarOpen} as={Fragment}>
|
||||||
|
<Dialog
|
||||||
|
as="div"
|
||||||
|
className="relative z-50 lg:hidden"
|
||||||
|
onClose={setSidebarOpen}
|
||||||
|
>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="transition-opacity ease-linear duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="transition-opacity ease-linear duration-300"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<div className="fixed inset-0 bg-gray-900/80" />
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
<div className="fixed inset-0 flex">
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="transition ease-in-out duration-300 transform"
|
||||||
|
enterFrom="-translate-x-full"
|
||||||
|
enterTo="translate-x-0"
|
||||||
|
leave="transition ease-in-out duration-300 transform"
|
||||||
|
leaveFrom="translate-x-0"
|
||||||
|
leaveTo="-translate-x-full"
|
||||||
|
>
|
||||||
|
<Dialog.Panel className="relative mr-16 flex w-full max-w-xs flex-1">
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-in-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in-out duration-300"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<div className="absolute left-full top-0 flex w-16 justify-center pt-5">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="-m-2.5 p-2.5"
|
||||||
|
onClick={() => setSidebarOpen(false)}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Close sidebar</span>
|
||||||
|
<XMarkIcon
|
||||||
|
className="h-6 w-6 text-white"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Transition.Child>
|
||||||
|
{/* Sidebar component, swap this element with another sidebar if you like */}
|
||||||
|
<div className="flex grow flex-col gap-y-5 overflow-y-auto bg-white px-6 pb-2">
|
||||||
|
<Link className="flex pt-4 shrink-0 items-center" href="/">
|
||||||
|
<img className="w-auto" src="/chat-logo.webp" />
|
||||||
|
</Link>
|
||||||
|
<nav className="flex flex-1 flex-col">
|
||||||
|
<ul role="list" className="flex flex-1 flex-col gap-y-7">
|
||||||
|
<li>
|
||||||
|
<ul role="list" className="-mx-2 space-y-1">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<li key={item.name}>
|
||||||
|
<Link
|
||||||
|
href={item.href}
|
||||||
|
className={classNames(
|
||||||
|
item.current
|
||||||
|
? "bg-gray-50 text-indigo-600"
|
||||||
|
: "text-gray-700 hover:text-indigo-600 hover:bg-gray-50",
|
||||||
|
"group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon
|
||||||
|
className={classNames(
|
||||||
|
item.current
|
||||||
|
? "text-indigo-600"
|
||||||
|
: "text-gray-400 group-hover:text-indigo-600",
|
||||||
|
"h-6 w-6 shrink-0"
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</Dialog.Panel>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition.Root>
|
||||||
|
|
||||||
|
{/* Static sidebar for desktop */}
|
||||||
|
<div className="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col">
|
||||||
|
{/* Sidebar component, swap this element with another sidebar if you like */}
|
||||||
|
<div className="flex grow flex-col gap-y-5 overflow-y-auto border-r border-gray-200 bg-white px-6">
|
||||||
|
<Link className="flex pt-4 shrink-0 items-center" href="/">
|
||||||
|
<img className="" src="/chat-logo.webp" />
|
||||||
|
</Link>
|
||||||
|
<nav className="flex flex-1 flex-col">
|
||||||
|
<ul role="list" className="flex flex-1 flex-col gap-y-7">
|
||||||
|
<li>
|
||||||
|
<ul role="list" className="-mx-2 space-y-1">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<li key={item.name}>
|
||||||
|
<Link
|
||||||
|
href={item.href}
|
||||||
|
className={classNames(
|
||||||
|
item.current
|
||||||
|
? "bg-gray-50 text-indigo-600"
|
||||||
|
: "text-gray-700 hover:text-indigo-600 hover:bg-gray-50",
|
||||||
|
"group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon
|
||||||
|
className={classNames(
|
||||||
|
item.current
|
||||||
|
? "text-indigo-600"
|
||||||
|
: "text-gray-400 group-hover:text-indigo-600",
|
||||||
|
"h-6 w-6 shrink-0"
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="sticky top-0 z-40 flex items-center gap-x-6 bg-white px-4 py-4 shadow-sm sm:px-6 lg:hidden">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="-m-2.5 p-2.5 text-gray-700 lg:hidden"
|
||||||
|
onClick={() => setSidebarOpen(true)}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Open sidebar</span>
|
||||||
|
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main className="py-10 lg:pl-72">
|
||||||
|
<div className="px-4 sm:px-6 lg:px-8">
|
||||||
|
<SettingView />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingPage;
|
Reference in New Issue
Block a user