Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ui/dashboard/src/@locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"user-profile": "User profile",
"logout": "Logout",
"back-to-main": "Back to Main",
"collapse": "Collapse sidebar",
"expand": "Expand sidebar",
"no-projects": "No projects found",
"no-organizations": "No organizations found",
"insights": "Insights"
Expand Down
2 changes: 2 additions & 0 deletions ui/dashboard/src/@locales/ja/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"user-profile": "ユーザープロフィール",
"logout": "ログアウト",
"back-to-main": "メインに戻る",
"collapse": "サイドバーを折りたたむ",
"expand": "サイドバーを展開",
"no-projects": "プロジェクトが見つかりません",
"no-organizations": "組織が見つかりません",
"insights": "インサイト"
Expand Down
18 changes: 16 additions & 2 deletions ui/dashboard/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
setCurrentEnvIdStorage
} from 'storage/environment';
import { getIsLoginFirstTimeStorage } from 'storage/login';
import { getNavigationCollapsedStorage } from 'storage/navigation';
import {
getCurrentProjectEnvironmentStorage,
setCurrentProjectEnvironmentStorage
Expand All @@ -62,6 +63,7 @@ import { ConsoleAccount, EnvironmentRole } from '@types';
import { isNotEmpty } from 'utils/data-type';
import { checkEnvironmentEmptyId } from 'utils/function';
import { stringifyParams, useSearchParams } from 'utils/search-params';
import { cn } from 'utils/style';
import AccessDeniedPage from 'pages/access-denied';
import APIKeysPage from 'pages/api-keys';
import AuditLogsPage from 'pages/audit-logs';
Expand Down Expand Up @@ -141,6 +143,9 @@ function App() {
export const Root = memo(() => {
const authToken = getTokenStorage();
const [pageKey, setPageKey] = useState<string>(uuid());
const [isNavCollapsed, setIsNavCollapsed] = useState(
getNavigationCollapsedStorage
);
const { isInitialLoading, isLogin, consoleAccount, myOrganizations } =
useAuth();

Expand All @@ -160,8 +165,17 @@ export const Root = memo(() => {
return (
<WalkthroughProvider>
<div className="flex flex-row w-full h-full">
<Navigation onClickNavLink={handleChangePageKey} />
<div className="w-full ml-[248px] shadow-lg overflow-y-auto">
<Navigation
onClickNavLink={handleChangePageKey}
isCollapsed={isNavCollapsed}
onToggleCollapsed={setIsNavCollapsed}
/>
<div
className={cn(
'w-full shadow-lg overflow-y-auto transition-all duration-300 ease-in-out',
isNavCollapsed ? 'ml-[60px]' : 'ml-[248px]'
)}
>
<Routes>
{consoleAccount.isSystemAdmin && (
<Route
Expand Down
4 changes: 4 additions & 0 deletions ui/dashboard/src/assets/logos/logo-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions ui/dashboard/src/components/dropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const DropdownMenuTrigger = forwardRef<
<DropdownMenuPrimitive.Trigger
type="button"
ref={ref}
aria-label={ariaLabel}
className={cn(
triggerVariants({
variant
Expand Down Expand Up @@ -363,6 +364,7 @@ interface DropdownProps {
menuContentSide?: 'top' | 'bottom' | 'left' | 'right';
alignContent?: 'center' | 'end' | 'start';
trigger?: ReactNode;
ariaLabel?: string;
additionalElement?: (item: DropdownOption) => ReactNode;
onChange?: (value: DropdownValue | DropdownValue[]) => void;
onChangeAdditional?: (value: DropdownValue | DropdownValue[]) => void; // for additional options
Expand Down Expand Up @@ -396,6 +398,7 @@ const Dropdown = ({
className = 'w-full',
alignContent = 'start',
trigger,
ariaLabel,

additionalElement,
sideOffsetContent,
Expand Down Expand Up @@ -468,6 +471,7 @@ const Dropdown = ({
}
label={labelCustom ? labelCustom : triggerLabel}
trigger={trigger}
ariaLabel={ariaLabel}
disabled={disabled}
loading={loading}
variant={variant}
Expand Down
171 changes: 137 additions & 34 deletions ui/dashboard/src/components/navigation/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect } from 'react';
import { Link, useLocation, useNavigate } from 'react-router';
import logoIcon from 'assets/logos/logo-icon.svg';
import logo from 'assets/logos/logo-white.svg';
import { useAuth, getCurrentEnvironment } from 'auth';
import * as ROUTING from 'constants/routing';
Expand All @@ -8,22 +9,40 @@ import { useToggleOpen } from 'hooks';
import { useTranslation } from 'i18n';
import compact from 'lodash/compact';
import flatMapDeep from 'lodash/flatMapDeep';
import { setNavigationCollapsedStorage } from 'storage/navigation';
import { cn } from 'utils/style';
import * as IconSystem from '@icons';
import Divider from 'components/divider';
import Icon from 'components/icon';
import { Tooltip } from 'components/tooltip';
import SectionMenu from './menu-section';
import MyProjects from './my-projects';
import NotificationBell from './notification-bell';
import SwitchOrganization from './switch-organization';
import UserMenu from './user-menu';

const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
type NavigationProps = {
onClickNavLink: () => void;
isCollapsed: boolean;
onToggleCollapsed: (value: boolean) => void;
};

const Navigation = ({
onClickNavLink,
isCollapsed,
onToggleCollapsed
}: NavigationProps) => {
const { t } = useTranslation(['common']);
const { pathname } = useLocation();
const navigate = useNavigate();
const { consoleAccount } = useAuth();

const toggleCollapsed = () => {
const next = !isCollapsed;
setNavigationCollapsedStorage(next);
onToggleCollapsed(next);
};

const currentEnvironment = getCurrentEnvironment(consoleAccount!);
const envUrlCode = currentEnvironment.urlCode;

Expand Down Expand Up @@ -161,11 +180,59 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
useToggleOpen(false);

return (
<div className="fixed h-screen w-[248px] bg-primary-500 z-50 py-8 px-6">
<div
className={cn(
'fixed h-screen bg-primary-500 z-50 py-8 transition-all duration-300 ease-in-out',
isCollapsed ? 'w-[60px] px-2' : 'w-[248px] px-6'
)}
>
<div className="flex flex-col size-full relative overflow-hidden">
<Link to={ROUTING.PAGE_PATH_ROOT} onClick={onCloseSetting}>
<img src={logo} alt="Bucketer" />
</Link>
<div
className={cn('group relative flex items-center', {
'w-full justify-center': isCollapsed
})}
>
<Link
to={ROUTING.PAGE_PATH_ROOT}
onClick={onCloseSetting}
className={cn(
'overflow-hidden',
isCollapsed && 'flex-center w-full',
isCollapsed && 'group-hover:pointer-events-none'
)}
>
{isCollapsed ? (
<img
src={logoIcon}
alt="Bucketeer"
className="w-8 h-8 shrink-0"
/>
) : (
<img src={logo} alt="Bucketeer" />
)}
</Link>
<button
type="button"
onClick={toggleCollapsed}
className={cn(
'flex-center rounded-md text-primary-50 shrink-0',
'opacity-0 group-hover:opacity-100 focus-visible:opacity-100 transition-opacity',
isCollapsed
? 'absolute inset-0 m-auto size-8 bg-primary-400 hover:bg-primary-300 pointer-events-none group-hover:pointer-events-auto focus-visible:pointer-events-auto'
: 'size-6 ml-auto hover:bg-primary-400'
)}
aria-label={t(
isCollapsed ? `navigation.expand` : `navigation.collapse`
)}
>
<Icon
icon={IconSystem.IconChevronRight}
color="primary-50"
size="xs"
className={cn({ 'rotate-180': !isCollapsed })}
/>
</button>
</div>

<div className="flex flex-col flex-1 items-center pt-6">
<div
Expand All @@ -174,23 +241,39 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
{ 'right-0': isOpenSetting }
)}
>
<button
onClick={() => {
onCloseSetting();
navigate(`/${envUrlCode}${ROUTING.PAGE_PATH_FEATURES}`);
}}
className="flex items-center gap-x-2 text-primary-50"
>
<Icon icon={IconSystem.IconBackspace} />
<span>{t(`navigation.back-to-main`)}</span>
</button>
<Tooltip
hidden={!isCollapsed}
content={t(`navigation.back-to-main`)}
side="right"
trigger={
<button
onClick={() => {
onCloseSetting();
navigate(`/${envUrlCode}${ROUTING.PAGE_PATH_FEATURES}`);
}}
aria-label={
isCollapsed ? t(`navigation.back-to-main`) : undefined
}
className={cn(
'flex items-center gap-x-2 text-primary-50 rounded-lg',
isCollapsed
? 'justify-center w-full py-2 hover:bg-primary-400'
: 'px-3'
)}
>
<Icon icon={IconSystem.IconBackspace} />
{!isCollapsed && <span>{t(`navigation.back-to-main`)}</span>}
Comment on lines +264 to +265

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed 948c98d

</button>
}
/>
<Divider className="my-5 bg-primary-50 opacity-10" />
{settingMenuSections.map((item, index) => (
<SectionMenu
key={index}
className="first:mt-0 mt-4"
title={item.title}
items={item.menus}
isCollapsed={isCollapsed}
/>
))}
</div>
Expand All @@ -200,10 +283,12 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
{ 'left-0': !isOpenSetting }
)}
>
<div className="px-3 opacity-80 uppercase typo-head-bold-tiny text-primary-50 mb-3">
{t(`environment`)}
</div>
<MyProjects />
{!isCollapsed && (
<div className="px-3 opacity-80 uppercase typo-head-bold-tiny text-primary-50 mb-3">
{t(`environment`)}
</div>
)}
<MyProjects isCollapsed={isCollapsed} />
<Divider className="my-5 bg-primary-50 opacity-10" />
{mainMenuSections.map((item, index) => (
<SectionMenu
Expand All @@ -212,37 +297,55 @@ const Navigation = ({ onClickNavLink }: { onClickNavLink: () => void }) => {
title={item.title}
items={item.menus}
onClickNavLink={onClickNavLink}
isCollapsed={isCollapsed}
/>
))}
</div>
</div>

<Divider className="mb-3 bg-primary-50 opacity-10" />

<div className="flex items-center justify-between">
<div
className={cn('flex items-center justify-between', {
'flex-col gap-y-3': isCollapsed
})}
>
<UserMenu onOpenSwitchOrg={onOpenSwitchOrg} />
<div className="flex items-center justify-center gap-2">
<div
className={cn('flex items-center justify-center gap-2', {
'flex-col': isCollapsed
})}
>
<NotificationBell envUrlCode={envUrlCode} />
<button
type="button"
onClick={() => {
onOpenSetting();
if (consoleAccount?.isSystemAdmin) {
navigate(ROUTING.PAGE_PATH_ORGANIZATIONS);
} else {
navigate(`/${envUrlCode}${ROUTING.PAGE_PATH_SETTINGS}`);
}
}}
>
<Icon icon={IconSystem.IconSetting} color="primary-50" />
</button>
<Tooltip
hidden={!isCollapsed}
content={t(`settings`)}
side="right"
trigger={
<button
type="button"
aria-label={isCollapsed ? t(`settings`) : undefined}
onClick={() => {
onOpenSetting();
if (consoleAccount?.isSystemAdmin) {
navigate(ROUTING.PAGE_PATH_ORGANIZATIONS);
} else {
navigate(`/${envUrlCode}${ROUTING.PAGE_PATH_SETTINGS}`);
}
}}
>
<Icon icon={IconSystem.IconSetting} color="primary-50" />
</button>
}
/>
</div>
</div>
</div>
<SwitchOrganization
isOpen={isOpenSwitchOrg}
onCloseSwitchOrg={onCloseSwitchOrg}
onCloseSetting={onCloseSetting}
isCollapsed={isCollapsed}
/>
</div>
);
Expand Down
Loading