chore: tweak setting page

This commit is contained in:
Steven 2024-01-11 22:25:05 +08:00
parent a1dda913c3
commit 1994c20c54
12 changed files with 113 additions and 175 deletions

View File

@ -56,15 +56,15 @@ const AccessTokenSection = () => {
return (
<>
<div className="mt-8 w-full flex flex-col justify-start items-start space-y-4">
<div className="mt-4 w-full flex flex-col justify-start items-start space-y-4">
<div className="w-full">
<div className="sm:flex sm:items-center sm:justify-between">
<div className="sm:flex-auto space-y-1">
<p className="flex flex-row justify-start items-center font-medium text-gray-700 dark:text-gray-300">
<p className="flex flex-row justify-start items-center font-medium text-gray-700 dark:text-gray-400">
Access Tokens
<LearnMore className="ml-2" url="https://usememos.com/docs/security/access-tokens" />
</p>
<p className="text-sm text-gray-700 dark:text-gray-400">A list of all access tokens for your account.</p>
<p className="text-sm text-gray-700 dark:text-gray-500">A list of all access tokens for your account.</p>
</div>
<div className="mt-4 sm:mt-0">
<Button
@ -81,19 +81,19 @@ const AccessTokenSection = () => {
<div className="mt-2 flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full py-2 align-middle">
<table className="min-w-full divide-y divide-gray-300 dark:divide-gray-400">
<table className="min-w-full divide-y divide-gray-300 dark:divide-zinc-600">
<thead>
<tr>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
<th scope="col" className="px-3 py-2 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
Token
</th>
<th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
<th scope="col" className="py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
Description
</th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
<th scope="col" className="px-3 py-2 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
Created At
</th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
<th scope="col" className="px-3 py-2 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">
Expires At
</th>
<th scope="col" className="relative py-3.5 pl-3 pr-4">
@ -101,10 +101,10 @@ const AccessTokenSection = () => {
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
<tbody className="divide-y divide-gray-200 dark:divide-zinc-700">
{userAccessTokens.map((userAccessToken) => (
<tr key={userAccessToken.accessToken}>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-900 dark:text-gray-400 flex flex-row justify-start items-center gap-x-1">
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-900 dark:text-gray-400 flex flex-row justify-start items-center gap-x-1">
<span className="font-mono">{getFormatedAccessToken(userAccessToken.accessToken)}</span>
<IconButton
color="neutral"
@ -115,16 +115,16 @@ const AccessTokenSection = () => {
<Icon.Clipboard className="w-4 h-auto text-gray-400" />
</IconButton>
</td>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-400">
<td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-400">
{userAccessToken.description}
</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500 dark:text-gray-400">
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-400">
{userAccessToken.issuedAt?.toLocaleString()}
</td>
<td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500 dark:text-gray-400">
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-400">
{userAccessToken.expiresAt?.toLocaleString() ?? "Never"}
</td>
<td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm">
<td className="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm">
<IconButton
color="danger"
variant="plain"

View File

@ -121,8 +121,8 @@ const MemberSection = () => {
};
return (
<div className="section-container member-section-container">
<p className="title-text">{t("setting.member-section.create-a-member")}</p>
<div className="w-full flex flex-col gap-2 pt-2 pb-4">
<p className="font-medium text-gray-700 dark:text-gray-500">{t("setting.member-section.create-a-member")}</p>
<div className="w-full flex flex-col justify-start items-start gap-2">
<div className="flex flex-col justify-start items-start gap-1">
<span className="text-sm">{t("common.username")}</span>
@ -140,10 +140,10 @@ const MemberSection = () => {
<div className="title-text">{t("setting.member-list")}</div>
</div>
<div className="w-full overflow-x-auto">
<div className="inline-block min-w-full align-middle border rounded-lg dark:border-gray-500">
<table className="min-w-full divide-y divide-gray-300 dark:divide-gray-500">
<div className="inline-block min-w-full align-middle border rounded-lg dark:border-zinc-600">
<table className="min-w-full divide-y divide-gray-300 dark:divide-zinc-600">
<thead>
<tr className="text-sm font-semibold text-left text-gray-900 dark:text-gray-300">
<tr className="text-sm font-semibold text-left text-gray-900 dark:text-gray-400">
<th scope="col" className="py-2 pl-4 pr-3">
ID
</th>
@ -159,16 +159,16 @@ const MemberSection = () => {
<th scope="col" className="relative py-2 pl-3 pr-4"></th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-500">
<tbody className="divide-y divide-gray-200 dark:divide-zinc-600">
{userList.map((user) => (
<tr key={user.id}>
<td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-300">{user.id}</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">
<td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-400">{user.id}</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-400">
{user.username}
<span className="ml-1 italic">{user.rowStatus === RowStatus.ARCHIVED && "(Archived)"}</span>
</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">{user.nickname}</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-300">{user.email}</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-400">{user.nickname}</td>
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-500 dark:text-gray-400">{user.email}</td>
<td className="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm font-medium flex justify-end">
{currentUser?.id === user.id ? (
<span>{t("common.yourself")}</span>

View File

@ -11,28 +11,26 @@ const MyAccountSection = () => {
const user = useCurrentUser();
return (
<>
<div className="section-container account-section-container">
<p className="font-medium my-2 text-gray-700 dark:text-gray-400">{t("setting.account-section.title")}</p>
<div className="flex flex-row justify-start items-center">
<UserAvatar className="mr-2 w-14 h-14" avatarUrl={user.avatarUrl} />
<div className="flex flex-col justify-center items-start">
<span className="text-2xl font-medium">{user.nickname}</span>
<span className="-mt-2 text-base text-gray-500 dark:text-gray-400">({user.username})</span>
</div>
<div className="w-full gap-2 pt-2 pb-4">
<p className="font-medium text-gray-700 dark:text-gray-500">{t("setting.account-section.title")}</p>
<div className="mt-1 flex flex-row justify-start items-center">
<UserAvatar className="mr-2" avatarUrl={user.avatarUrl} />
<div className="flex flex-col justify-center items-start">
<span className="text-2xl font-medium">{user.nickname}</span>
<span className="-mt-2 text-base text-gray-500 dark:text-gray-400">({user.username})</span>
</div>
<div className="w-full flex flex-row justify-start items-center mt-4 space-x-2">
<Button variant="outlined" onClick={showUpdateAccountDialog}>
{t("common.edit")}
</Button>
<Button variant="outlined" onClick={showChangePasswordDialog}>
{t("setting.account-section.change-password")}
</Button>
</div>
<AccessTokenSection />
</div>
</>
<div className="w-full flex flex-row justify-start items-center mt-2 space-x-2">
<Button variant="outlined" onClick={showUpdateAccountDialog}>
{t("common.edit")}
</Button>
<Button variant="outlined" onClick={showChangePasswordDialog}>
{t("setting.account-section.change-password")}
</Button>
</div>
<AccessTokenSection />
</div>
);
};

View File

@ -13,7 +13,6 @@ import Icon from "../Icon";
import LocaleSelect from "../LocaleSelect";
import VisibilityIcon from "../VisibilityIcon";
import WebhookSection from "./WebhookSection";
import "@/less/settings/preferences-section.less";
const PreferencesSection = () => {
const t = useTranslate();
@ -72,19 +71,19 @@ const PreferencesSection = () => {
};
return (
<div className="section-container preferences-section-container">
<p className="title-text">{t("common.basic")}</p>
<div className="form-label selector">
<span className="text-sm">{t("common.language")}</span>
<div className="w-full flex flex-col gap-2 pt-2 pb-4">
<p className="font-medium text-gray-700 dark:text-gray-500">{t("common.basic")}</p>
<div className="w-full flex flex-row justify-between items-center">
<span>{t("common.language")}</span>
<LocaleSelect value={locale} onChange={handleLocaleSelectChange} />
</div>
<div className="form-label selector">
<span className="text-sm">{t("setting.preference-section.theme")}</span>
<div className="w-full flex flex-row justify-between items-center">
<span>{t("setting.preference-section.theme")}</span>
<AppearanceSelect value={appearance} onChange={handleAppearanceSelectChange} />
</div>
<p className="title-text">{t("setting.preference")}</p>
<div className="form-label selector">
<span className="text-sm break-keep text-ellipsis overflow-hidden">{t("setting.preference-section.default-memo-visibility")}</span>
<p className="font-medium text-gray-700 dark:text-gray-500">{t("setting.preference")}</p>
<div className="w-full flex flex-row justify-between items-center">
<span className="truncate">{t("setting.preference-section.default-memo-visibility")}</span>
<Select
className="!min-w-fit"
value={setting.memoVisibility}
@ -110,7 +109,7 @@ const PreferencesSection = () => {
<div className="w-full flex flex-col justify-start items-start">
<div className="mb-2 w-full flex flex-row justify-between items-center">
<div className="w-auto flex items-center">
<span className="text-sm mr-1">{t("setting.preference-section.telegram-user-id")}</span>
<span className="mr-1">{t("setting.preference-section.telegram-user-id")}</span>
</div>
<Button variant="outlined" color="neutral" onClick={handleSaveTelegramUserId}>
{t("common.save")}

View File

@ -57,10 +57,10 @@ const SSOSection = () => {
};
return (
<div className="section-container">
<div className="mb-2 w-full flex flex-row justify-between items-center gap-1">
<div className="w-full flex flex-col gap-2 pt-2 pb-4">
<div className="w-full flex flex-row justify-between items-center gap-1">
<div className="flex flex-row items-center gap-1">
<span className="font-mono text-sm text-gray-400">{t("setting.sso-section.sso-list")}</span>
<span className="font-mono text-gray-400">{t("setting.sso-section.sso-list")}</span>
<LearnMore url="https://usememos.com/docs/advanced-settings/keycloak" />
</div>
<Button onClick={() => showCreateIdentityProviderDialog(undefined, fetchIdentityProviderList)}>{t("common.create")}</Button>
@ -100,8 +100,13 @@ const SSOSection = () => {
</div>
</div>
))}
{identityProviderList.length === 0 && (
<div className="w-full mt-2 text-sm dark:border-zinc-700 opacity-60 flex flex-row items-center justify-between">
<p className="">No SSO found.</p>
</div>
)}
<div className="w-full mt-8">
<div className="w-full mt-4">
<p className="text-sm">{t("common.learn-more")}:</p>
<List component="ul" marker="disc" size="sm">
<ListItem>

View File

@ -60,9 +60,9 @@ const StorageSection = () => {
};
return (
<div className="section-container">
<div className="mt-4 mb-2 w-full flex flex-row justify-start items-center">
<span className="font-mono text-sm text-gray-400 mr-2">{t("setting.storage-section.current-storage")}</span>
<div className="w-full flex flex-col gap-2 pt-2 pb-4">
<div className="w-full flex flex-row justify-start items-center">
<span className="font-mono text-sm text-gray-400 mr-2 dark:text-gray-500">{t("setting.storage-section.current-storage")}</span>
</div>
<RadioGroup
className="w-full"
@ -82,7 +82,7 @@ const StorageSection = () => {
<Radio key={storage.id} value={storage.id} label={storage.name} />
))}
</RadioGroup>
<Divider className="!my-4" />
<Divider className="!my-2" />
<div className="mb-2 w-full flex flex-row justify-between items-center gap-1">
<div className="flex items-center gap-1">
<span className="font-mono text-sm text-gray-400">{t("setting.storage-section.storage-services")}</span>
@ -90,7 +90,7 @@ const StorageSection = () => {
</div>
<Button onClick={() => showCreateStorageServiceDialog(undefined, fetchStorageList)}>{t("common.create")}</Button>
</div>
<div className="mt-2 w-full flex flex-col">
<div className="w-full flex flex-col">
{storageList.map((storage) => (
<div
key={storage.id}
@ -123,7 +123,7 @@ const StorageSection = () => {
</div>
))}
{storageList.length === 0 && (
<div className="pb-2 w-full text-sm dark:border-zinc-700 opacity-60 flex flex-row items-center justify-between">
<div className="w-full text-sm dark:border-zinc-700 opacity-60 flex flex-row items-center justify-between">
<p className="">No storage service found.</p>
</div>
)}

View File

@ -10,7 +10,6 @@ import { showCommonDialog } from "../Dialog/CommonDialog";
import showDisablePasswordLoginDialog from "../DisablePasswordLoginDialog";
import Icon from "../Icon";
import showUpdateCustomizedProfileDialog from "../UpdateCustomizedProfileDialog";
import "@/less/settings/system-section.less";
interface State {
dbSize: number;
@ -244,38 +243,38 @@ const SystemSection = () => {
};
return (
<div className="section-container system-section-container">
<p className="title-text">{t("common.basic")}</p>
<div className="form-label">
<div className="w-full flex flex-col gap-2 pt-2 pb-4">
<p className="font-medium text-gray-700 dark:text-gray-500">{t("common.basic")}</p>
<div className="w-full flex flex-row justify-between items-center">
<div className="normal-text">
{t("setting.system-section.server-name")}: <span className="font-mono font-bold">{systemStatus.customizedProfile.name}</span>
</div>
<Button onClick={handleUpdateCustomizedProfileButtonClick}>{t("common.edit")}</Button>
</div>
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<span className="text-sm">
{t("setting.system-section.database-file-size")}: <span className="font-mono font-bold">{formatBytes(state.dbSize)}</span>
</span>
<Button onClick={handleVacuumBtnClick}>{t("common.vacuum")}</Button>
</div>
<p className="title-text">{t("common.settings")}</p>
<div className="form-label">
<p className="font-medium text-gray-700 dark:text-gray-500">{t("common.settings")}</p>
<div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.allow-user-signup")}</span>
<Switch checked={state.allowSignUp} onChange={(event) => handleAllowSignUpChanged(event.target.checked)} />
</div>
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.disable-password-login")}</span>
<Switch checked={state.disablePasswordLogin} onChange={(event) => handleDisablePasswordLoginChanged(event.target.checked)} />
</div>
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.disable-public-memos")}</span>
<Switch checked={state.disablePublicMemos} onChange={(event) => handleDisablePublicMemosChanged(event.target.checked)} />
</div>
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.display-with-updated-time")}</span>
<Switch checked={state.memoDisplayWithUpdatedTs} onChange={(event) => handleMemoDisplayWithUpdatedTs(event.target.checked)} />
</div>
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row items-center">
<span className="text-sm mr-1">{t("setting.system-section.max-upload-size")}</span>
<Tooltip title={t("setting.system-section.max-upload-size-hint")} placement="top">
@ -293,7 +292,7 @@ const SystemSection = () => {
/>
</div>
<Divider className="!mt-3 !my-4" />
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row items-center">
<div className="w-auto flex items-center">
<span className="text-sm mr-1">Instance URL</span>
@ -324,7 +323,7 @@ const SystemSection = () => {
</Link>
</div>
<Divider className="!mt-3 !my-4" />
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row items-center">
<div className="w-auto flex items-center">
<span className="text-sm mr-1">{t("setting.system-section.telegram-bot-token")}</span>
@ -355,7 +354,7 @@ const SystemSection = () => {
</Link>
</div>
<Divider className="!mt-3 !my-4" />
<div className="form-label">
<div className="w-full flex flex-row justify-between items-center">
<span className="normal-text">{t("setting.system-section.additional-style")}</span>
<Button variant="outlined" color="neutral" onClick={handleSaveAdditionalStyle}>
{t("common.save")}
@ -373,7 +372,7 @@ const SystemSection = () => {
value={state.additionalStyle}
onChange={(event) => handleAdditionalStyleChanged(event.target.value)}
/>
<div className="form-label mt-2">
<div className="w-full flex flex-row justify-between items-center mt-2">
<span className="normal-text">{t("setting.system-section.additional-script")}</span>
<Button variant="outlined" color="neutral" onClick={handleSaveAdditionalScript}>
{t("common.save")}

View File

@ -49,7 +49,7 @@ const WebhookSection = () => {
<div className="w-full flex flex-col justify-start items-start">
<div className="w-full flex justify-between items-center">
<div className="flex-auto space-y-1">
<p className="flex flex-row justify-start items-center font-medium text-gray-700 dark:text-gray-300">Webhooks</p>
<p className="flex flex-row justify-start items-center font-medium text-gray-700 dark:text-gray-400">Webhooks</p>
</div>
<div>
<Button
@ -65,8 +65,8 @@ const WebhookSection = () => {
</div>
<div className="w-full mt-2 flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full border rounded-lg align-middle dark:border-gray-500">
<table className="min-w-full divide-y divide-gray-300 dark:divide-gray-500">
<div className="inline-block min-w-full border rounded-lg align-middle dark:border-zinc-600">
<table className="min-w-full divide-y divide-gray-300 dark:divide-zinc-600">
<thead>
<tr>
<th scope="col" className="px-3 py-2 text-left text-sm font-semibold text-gray-900 dark:text-gray-400">

View File

@ -1,47 +0,0 @@
.setting-page-wrapper {
@apply flex flex-row justify-start items-start relative w-full h-full shadow p-4 rounded-lg bg-white dark:bg-zinc-800 dark:text-gray-300 sm:gap-x-4;
> .section-selector-container {
@apply hidden sm:flex flex-col justify-start items-start sm:w-40 h-auto sm:h-full shrink-0 pb-2 border-r dark:border-r-zinc-600;
> .section-title {
@apply text-sm mt-2 sm:mt-4 first:mt-2 mb-1 font-mono text-gray-400;
}
> .section-items-container {
@apply w-full h-auto flex flex-row sm:flex-col justify-start items-start;
> .section-item {
@apply flex flex-row justify-start items-center text-base select-none mr-3 sm:mr-0 mt-0 sm:mt-2 text-gray-700 dark:text-gray-400 cursor-pointer hover:opacity-80;
&.selected {
@apply font-bold dark:text-gray-300 hover:opacity-100;
}
> .icon-text {
@apply text-base mr-2;
}
}
}
}
> .section-content-container {
@apply w-full sm:w-auto pl-2 pb-4 grow flex flex-col justify-start items-start h-full;
> .section-container {
@apply flex flex-col justify-start items-start w-full;
.title-text {
@apply text-sm mt-4 first:mt-2 mb-3 font-mono text-gray-500 dark:text-gray-400;
}
> .form-label {
@apply flex flex-row items-center w-full mb-2;
> .normal-text {
@apply shrink-0 select-text;
}
}
}
}
}

View File

@ -1,13 +0,0 @@
.preferences-section-container {
> .title-text {
@apply mt-4 first:mt-1;
}
> .form-label.selector {
@apply mb-2 flex flex-row justify-between items-center;
> .normal-text {
@apply mr-2 text-sm;
}
}
}

View File

@ -1,17 +0,0 @@
.system-section-container {
> .title-text {
@apply mt-4 first:mt-1;
}
> .text-value {
@apply mr-2 text-sm;
}
> .form-label {
@apply mb-2 flex flex-row justify-between items-center;
> .normal-text {
@apply mr-2 text-sm;
}
}
}

View File

@ -9,9 +9,9 @@ import SSOSection from "@/components/Settings/SSOSection";
import StorageSection from "@/components/Settings/StorageSection";
import SystemSection from "@/components/Settings/SystemSection";
import useCurrentUser from "@/hooks/useCurrentUser";
import { useGlobalStore } from "@/store/module";
import { User_Role } from "@/types/proto/api/v2/user_service";
import { useTranslate } from "@/utils/i18n";
import "@/less/setting.less";
type SettingSection = "my-account" | "preference" | "member" | "system" | "storage" | "sso";
@ -22,6 +22,7 @@ interface State {
const Setting = () => {
const t = useTranslate();
const user = useCurrentUser();
const globalStore = useGlobalStore();
const [state, setState] = useState<State>({
selectedSection: "my-account",
});
@ -45,58 +46,71 @@ const Setting = () => {
<section className="@container w-full max-w-5xl min-h-full flex flex-col justify-start items-start sm:pt-3 md:pt-6 pb-8">
<MobileHeader />
<div className="w-full px-4 sm:px-6">
<div className="setting-page-wrapper">
<div className="section-selector-container">
<span className="section-title">{t("common.basic")}</span>
<div className="section-items-container">
<div className="w-full shadow flex flex-row justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-800 text-gray-600 dark:text-gray-400">
<div className="hidden sm:flex flex-col justify-start items-start w-40 h-auto shrink-0 py-2">
<span className="text-sm mt-0.5 pl-3 font-mono text-gray-400 dark:text-gray-500">{t("common.basic")}</span>
<div className="w-full flex flex-col justify-start items-start mt-1">
<span
className={`w-auto px-3 leading-8 flex flex-row justify-start items-center cursor-pointer rounded-lg hover:opacity-80 ${
state.selectedSection === "my-account" ? "bg-zinc-100 shadow dark:bg-zinc-900" : ""
}`}
onClick={() => handleSectionSelectorItemClick("my-account")}
className={`section-item ${state.selectedSection === "my-account" ? "selected" : ""}`}
>
<Icon.User className="w-4 h-auto mr-2 opacity-80" /> {t("setting.my-account")}
</span>
<span
className={`w-auto px-3 leading-8 flex flex-row justify-start items-center cursor-pointer rounded-lg hover:opacity-80 ${
state.selectedSection === "preference" ? "bg-zinc-100 shadow dark:bg-zinc-900" : ""
}`}
onClick={() => handleSectionSelectorItemClick("preference")}
className={`section-item ${state.selectedSection === "preference" ? "selected" : ""}`}
>
<Icon.Cog className="w-4 h-auto mr-2 opacity-80" /> {t("setting.preference")}
</span>
</div>
{isHost ? (
<>
<span className="section-title">{t("common.admin")}</span>
<div className="section-items-container">
<span className="text-sm mt-4 pl-3 font-mono text-gray-400 dark:text-gray-500">{t("common.admin")}</span>
<div className="w-full flex flex-col justify-start items-start mt-1">
<span
onClick={() => handleSectionSelectorItemClick("member")}
className={`section-item ${state.selectedSection === "member" ? "selected" : ""}`}
className={`w-auto px-3 leading-8 flex flex-row justify-start items-center cursor-pointer rounded-lg hover:opacity-80 ${
state.selectedSection === "member" ? "bg-zinc-100 shadow dark:bg-zinc-900" : ""
}`}
>
<Icon.Users className="w-4 h-auto mr-2 opacity-80" /> {t("setting.member")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("system")}
className={`section-item ${state.selectedSection === "system" ? "selected" : ""}`}
className={`w-auto px-3 leading-8 flex flex-row justify-start items-center cursor-pointer rounded-lg hover:opacity-80 ${
state.selectedSection === "system" ? "bg-zinc-100 shadow dark:bg-zinc-900" : ""
}`}
>
<Icon.Settings2 className="w-4 h-auto mr-2 opacity-80" /> {t("setting.system")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("storage")}
className={`section-item ${state.selectedSection === "storage" ? "selected" : ""}`}
className={`w-auto px-3 leading-8 flex flex-row justify-start items-center cursor-pointer rounded-lg hover:opacity-80 ${
state.selectedSection === "storage" ? "bg-zinc-100 shadow dark:bg-zinc-900" : ""
}`}
>
<Icon.Database className="w-4 h-auto mr-2 opacity-80" /> {t("setting.storage")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("sso")}
className={`section-item ${state.selectedSection === "sso" ? "selected" : ""}`}
className={`w-auto px-3 leading-8 flex flex-row justify-start items-center cursor-pointer rounded-lg hover:opacity-80 ${
state.selectedSection === "sso" ? "bg-zinc-100 shadow dark:bg-zinc-900" : ""
}`}
>
<Icon.Key className="w-4 h-auto mr-2 opacity-80" /> {t("setting.sso")}
</span>
<span className="px-3 mt-2 opacity-70 text-sm">Version: v{globalStore.state.systemStatus.profile.version}</span>
</div>
</>
) : null}
</div>
<div className="section-content-container sm:max-w-[calc(100%-12rem)]">
<div className="w-full grow sm:pl-4 overflow-x-auto">
<Select
className="block mb-2 sm:!hidden"
className="block my-2 sm:!hidden"
value={state.selectedSection}
onChange={(_, value) => handleSectionSelectorItemClick(value as SettingSection)}
>