fix: Adjust Appearance of drawer
This commit is contained in:
20
frontend/components/UserAvatar.tsx
Normal file
20
frontend/components/UserAvatar.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { User } from 'lucide-react';
|
||||||
|
import Avatar from 'boring-avatars';
|
||||||
|
import { TokenContextType } from '@/types/user';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
context: TokenContextType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PublicityChip({ context }: Props) {
|
||||||
|
return context.isSignedIn() ? (
|
||||||
|
<Avatar
|
||||||
|
size={16}
|
||||||
|
name={context.token.user.username}
|
||||||
|
variant="beam"
|
||||||
|
colors={['#0A0310', '#49007E', '#FF005B', '#FF7D10', '#FFB238']}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<User size={16} />
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
const roles = [{ uid: 'administrator' }, { uid: 'user' }];
|
const roles = [{ uid: 'administrator' }, { uid: 'user' }];
|
||||||
|
|
||||||
const memberRoles = [{ uid: 'manager' }, { uid: 'developer' }, { uid: 'reporter' }];
|
const memberRoles = [{ uid: 'manager' }, { uid: 'developer' }, { uid: 'reporter' }];
|
||||||
|
|
||||||
const categoricalPalette = ['#fba91e', '#6ea56c', '#3ac6e1', '#feda2f', '#f15f47', '#244470', '#9c80bb', '#f595a6'];
|
const categoricalPalette = ['#fba91e', '#6ea56c', '#3ac6e1', '#feda2f', '#f15f47', '#244470', '#9c80bb', '#f595a6'];
|
||||||
|
|
||||||
|
const locales = [
|
||||||
|
{ code: 'en', name: 'English' },
|
||||||
|
{ code: 'ja', name: '日本語' },
|
||||||
|
];
|
||||||
|
|
||||||
const priorities = [
|
const priorities = [
|
||||||
{ uid: 'critical', color: '#bb3e03', chartColor: '#bb3e03' },
|
{ uid: 'critical', color: '#bb3e03', chartColor: '#bb3e03' },
|
||||||
{ uid: 'high', color: '#ca6702', chartColor: '#ca6702' },
|
{ uid: 'high', color: '#ca6702', chartColor: '#ca6702' },
|
||||||
@@ -56,4 +62,14 @@ const testRunCaseStatus = [
|
|||||||
{ uid: 'skipped', color: 'primary', chartColor: '#805aab' },
|
{ uid: 'skipped', color: 'primary', chartColor: '#805aab' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export { roles, memberRoles, priorities, testTypes, automationStatus, templates, testRunStatus, testRunCaseStatus };
|
export {
|
||||||
|
roles,
|
||||||
|
memberRoles,
|
||||||
|
locales,
|
||||||
|
priorities,
|
||||||
|
testTypes,
|
||||||
|
automationStatus,
|
||||||
|
templates,
|
||||||
|
testRunStatus,
|
||||||
|
testRunCaseStatus,
|
||||||
|
};
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
"account": "Account",
|
"account": "Account",
|
||||||
"signup": "Sign up",
|
"signup": "Sign up",
|
||||||
"signin": "Sign in",
|
"signin": "Sign in",
|
||||||
"signout": "Sign out"
|
"signout": "Sign out",
|
||||||
|
"links": "Links",
|
||||||
|
"languages": "Languages"
|
||||||
},
|
},
|
||||||
"Toast": {
|
"Toast": {
|
||||||
"need_signed_in": "You need signed in",
|
"need_signed_in": "You need signed in",
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
"account": "アカウント",
|
"account": "アカウント",
|
||||||
"signup": "新規登録",
|
"signup": "新規登録",
|
||||||
"signin": "サインイン",
|
"signin": "サインイン",
|
||||||
"signout": "サインアウト"
|
"signout": "サインアウト",
|
||||||
|
"links": "リンク",
|
||||||
|
"languages": "言語"
|
||||||
},
|
},
|
||||||
"Toast": {
|
"Toast": {
|
||||||
"need_signed_in": "サインインが必要です",
|
"need_signed_in": "サインインが必要です",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Button, DropdownTrigger, Dropdown, DropdownMenu, DropdownItem } from '@nextui-org/react';
|
import { Button, DropdownTrigger, Dropdown, DropdownMenu, DropdownItem } from '@nextui-org/react';
|
||||||
import { User, ChevronDown, PenTool, ArrowRightFromLine, ArrowRightToLine } from 'lucide-react';
|
import { ChevronDown, PenTool, ArrowRightFromLine, ArrowRightToLine } from 'lucide-react';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { TokenContext } from '@/utils/TokenProvider';
|
import { TokenContext } from '@/utils/TokenProvider';
|
||||||
import { useRouter } from '@/src/navigation';
|
import { useRouter } from '@/src/navigation';
|
||||||
import { AccountDropDownMessages } from '@/types/user';
|
import { AccountDropDownMessages } from '@/types/user';
|
||||||
import Avatar from 'boring-avatars';
|
import UserAvatar from '@/components/UserAvatar';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
messages: AccountDropDownMessages;
|
messages: AccountDropDownMessages;
|
||||||
@@ -23,22 +23,11 @@ export default function DropdownAccount({ messages, locale, onItemPress }: Props
|
|||||||
router.push(`/`, { locale: locale });
|
router.push(`/`, { locale: locale });
|
||||||
};
|
};
|
||||||
|
|
||||||
let userAvatar = context.isSignedIn() ? (
|
|
||||||
<Avatar
|
|
||||||
size={16}
|
|
||||||
name={context.token.user.username}
|
|
||||||
variant="beam"
|
|
||||||
colors={['#0A0310', '#49007E', '#FF005B', '#FF7D10', '#FFB238']}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<User size={16} />
|
|
||||||
);
|
|
||||||
|
|
||||||
const signinItems = [
|
const signinItems = [
|
||||||
{
|
{
|
||||||
uid: 'account',
|
uid: 'account',
|
||||||
title: messages.account,
|
title: messages.account,
|
||||||
icon: userAvatar,
|
icon: <UserAvatar context={context} />,
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
router.push('/account', { locale: locale });
|
router.push('/account', { locale: locale });
|
||||||
onItemPress();
|
onItemPress();
|
||||||
@@ -79,7 +68,12 @@ export default function DropdownAccount({ messages, locale, onItemPress }: Props
|
|||||||
return (
|
return (
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownTrigger>
|
<DropdownTrigger>
|
||||||
<Button size="sm" variant="light" startContent={userAvatar} endContent={<ChevronDown size={16} />}>
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="light"
|
||||||
|
startContent={<UserAvatar context={context} />}
|
||||||
|
endContent={<ChevronDown size={16} />}
|
||||||
|
>
|
||||||
{context.isSignedIn() ? context.token.user.username : messages.signIn}
|
{context.isSignedIn() ? context.token.user.username : messages.signIn}
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
|
|||||||
@@ -1,40 +1,24 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Button, DropdownTrigger, Dropdown, DropdownMenu, DropdownItem } from '@nextui-org/react';
|
import { Button, DropdownTrigger, Dropdown, DropdownMenu, DropdownItem } from '@nextui-org/react';
|
||||||
import { Globe, ChevronDown } from 'lucide-react';
|
import { Globe, ChevronDown } from 'lucide-react';
|
||||||
import { usePathname } from 'next/navigation';
|
import { locales } from '@/config/selection';
|
||||||
import { useRouter } from '@/src/navigation';
|
|
||||||
|
|
||||||
const locales = [
|
type Props = {
|
||||||
{ code: 'en', name: 'English' },
|
locale: string;
|
||||||
{ code: 'ja', name: '日本語' },
|
onChangeLocale: (code: string) => void;
|
||||||
];
|
};
|
||||||
|
|
||||||
export default function DropdownLanguage(params: { locale: string }) {
|
|
||||||
const router = useRouter();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
async function changeLocale(nextLocale: string) {
|
|
||||||
let newPathname;
|
|
||||||
if (pathname.length < 4) {
|
|
||||||
// when root path
|
|
||||||
router.push('/', { locale: nextLocale });
|
|
||||||
} else {
|
|
||||||
// when not root path, trim first "/en" from pathname = "/en/projects"
|
|
||||||
newPathname = pathname.slice(params.locale.length + 1);
|
|
||||||
router.push(newPathname, { locale: nextLocale });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export default function DropdownLanguage({ locale, onChangeLocale }: Props) {
|
||||||
return (
|
return (
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownTrigger>
|
<DropdownTrigger>
|
||||||
<Button size="sm" variant="light" startContent={<Globe size={16} />} endContent={<ChevronDown size={16} />}>
|
<Button size="sm" variant="light" startContent={<Globe size={16} />} endContent={<ChevronDown size={16} />}>
|
||||||
{locales.find((locale) => locale.code === params.locale)?.name || params.locale}
|
{locales.find((entry) => entry.code === locale)?.name || locale}
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
<DropdownMenu aria-label="lacales">
|
<DropdownMenu aria-label="lacales">
|
||||||
{locales.map((entry) => (
|
{locales.map((entry) => (
|
||||||
<DropdownItem key={entry.code} onClick={() => changeLocale(entry.code)}>
|
<DropdownItem key={entry.code} onClick={() => onChangeLocale(entry.code)}>
|
||||||
{entry.name}
|
{entry.name}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export default function Header(params: { locale: string }) {
|
|||||||
signUp: t('signup'),
|
signUp: t('signup'),
|
||||||
signIn: t('signin'),
|
signIn: t('signin'),
|
||||||
signOut: t('signout'),
|
signOut: t('signout'),
|
||||||
|
links: t('links'),
|
||||||
|
languages: t('languages'),
|
||||||
};
|
};
|
||||||
|
|
||||||
return <HeaderNavbarMenu messages={messages} locale={params.locale} />;
|
return <HeaderNavbarMenu messages={messages} locale={params.locale} />;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { useState, useContext } from 'react';
|
import { useState, useContext } from 'react';
|
||||||
import { TokenContext } from '@/utils/TokenProvider';
|
import { TokenContext } from '@/utils/TokenProvider';
|
||||||
import { Link } from '@/src/navigation';
|
import { Link, useRouter } from '@/src/navigation';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import {
|
import {
|
||||||
Navbar,
|
Navbar,
|
||||||
@@ -10,15 +11,18 @@ import {
|
|||||||
NavbarMenuToggle,
|
NavbarMenuToggle,
|
||||||
NavbarBrand,
|
NavbarBrand,
|
||||||
NavbarItem,
|
NavbarItem,
|
||||||
NavbarMenuItem,
|
|
||||||
Link as NextUiLink,
|
Link as NextUiLink,
|
||||||
Chip,
|
Chip,
|
||||||
|
ListboxItem,
|
||||||
|
Listbox,
|
||||||
} from '@nextui-org/react';
|
} from '@nextui-org/react';
|
||||||
import { MoveUpRight } from 'lucide-react';
|
import { ArrowRightFromLine, ArrowRightToLine, File, Globe, MoveUpRight, PenTool } from 'lucide-react';
|
||||||
import { ThemeSwitch } from '@/components/theme-switch';
|
import { ThemeSwitch } from '@/components/theme-switch';
|
||||||
import { GithubIcon } from '@/components/icons';
|
import { GithubIcon } from '@/components/icons';
|
||||||
|
import { locales } from '@/config/selection';
|
||||||
import DropdownAccount from './DropdownAccount';
|
import DropdownAccount from './DropdownAccount';
|
||||||
import DropdownLanguage from './DropdownLanguage';
|
import DropdownLanguage from './DropdownLanguage';
|
||||||
|
import UserAvatar from '@/components/UserAvatar';
|
||||||
|
|
||||||
type NabbarMenuMessages = {
|
type NabbarMenuMessages = {
|
||||||
projects: string;
|
projects: string;
|
||||||
@@ -28,6 +32,8 @@ type NabbarMenuMessages = {
|
|||||||
signUp: string;
|
signUp: string;
|
||||||
signIn: string;
|
signIn: string;
|
||||||
signOut: string;
|
signOut: string;
|
||||||
|
links: string;
|
||||||
|
languages: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -54,6 +60,20 @@ export default function HeaderNavbarMenu({ messages, locale }: Props) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
async function changeLocale(nextLocale: string) {
|
||||||
|
let newPathname;
|
||||||
|
if (pathname.length < 4) {
|
||||||
|
// when root path
|
||||||
|
router.push('/', { locale: nextLocale });
|
||||||
|
} else {
|
||||||
|
// when not root path, trim first "/en" from pathname = "/en/projects"
|
||||||
|
newPathname = pathname.slice(locale.length + 1);
|
||||||
|
router.push(newPathname, { locale: nextLocale });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navbar isMenuOpen={isMenuOpen} maxWidth="full" position="sticky" className="bg-inherit">
|
<Navbar isMenuOpen={isMenuOpen} maxWidth="full" position="sticky" className="bg-inherit">
|
||||||
<NavbarContent className="basis-1/5 sm:basis-full" justify="start">
|
<NavbarContent className="basis-1/5 sm:basis-full" justify="start">
|
||||||
@@ -115,44 +135,120 @@ export default function HeaderNavbarMenu({ messages, locale }: Props) {
|
|||||||
<ThemeSwitch />
|
<ThemeSwitch />
|
||||||
<div className="hidden md:block">
|
<div className="hidden md:block">
|
||||||
<DropdownAccount messages={messages} locale={locale} onItemPress={() => {}} />
|
<DropdownAccount messages={messages} locale={locale} onItemPress={() => {}} />
|
||||||
<DropdownLanguage locale={locale} />
|
<DropdownLanguage locale={locale} onChangeLocale={changeLocale} />
|
||||||
</div>
|
</div>
|
||||||
<NavbarMenuToggle className="md:hidden" onClick={() => setIsMenuOpen(!isMenuOpen)} />
|
<NavbarMenuToggle className="md:hidden" onClick={() => setIsMenuOpen(!isMenuOpen)} />
|
||||||
</NavbarContent>
|
</NavbarContent>
|
||||||
|
|
||||||
<NavbarMenu>
|
<NavbarMenu>
|
||||||
<div className="mx-4 mt-2 flex flex-col gap-2">
|
<div className="mx-4 mt-2 flex flex-col gap-2">
|
||||||
<NavbarMenuItem>
|
<p className="font-bold">{messages.links}</p>
|
||||||
<DropdownAccount messages={messages} locale={locale} onItemPress={() => setIsMenuOpen(false)} />
|
<Listbox
|
||||||
</NavbarMenuItem>
|
aria-label="Links"
|
||||||
<NavbarMenuItem>
|
itemClasses={{
|
||||||
<DropdownLanguage locale={locale} />
|
base: 'h-10 text-large',
|
||||||
</NavbarMenuItem>
|
}}
|
||||||
|
>
|
||||||
{commonLinks.map((link) =>
|
{commonLinks.map((link) =>
|
||||||
link.isExternal ? (
|
link.isExternal ? (
|
||||||
<NavbarMenuItem key={link.uid}>
|
<ListboxItem
|
||||||
<NextUiLink
|
key={link.uid}
|
||||||
href={link.href}
|
title={link.label}
|
||||||
onPress={() => setIsMenuOpen(false)}
|
startContent={<MoveUpRight size={12} />}
|
||||||
showAnchorIcon
|
onPress={() => {
|
||||||
anchorIcon={<MoveUpRight size={12} className="ms-1" />}
|
router.push(link.href, { locale: locale });
|
||||||
>
|
setIsMenuOpen(false);
|
||||||
{messages.docs}
|
}}
|
||||||
</NextUiLink>
|
/>
|
||||||
</NavbarMenuItem>
|
|
||||||
) : (
|
) : (
|
||||||
<NavbarMenuItem key={link.uid}>
|
<ListboxItem
|
||||||
<Link
|
key={link.uid}
|
||||||
className="data-[active=true]:text-primary data-[active=true]:font-medium"
|
title={link.label}
|
||||||
href={link.href}
|
startContent={<File size={12} />}
|
||||||
locale={locale}
|
onPress={() => {
|
||||||
onClick={() => setIsMenuOpen(false)}
|
router.push(link.href, { locale: locale });
|
||||||
>
|
setIsMenuOpen(false);
|
||||||
{link.label}
|
}}
|
||||||
</Link>
|
/>
|
||||||
</NavbarMenuItem>
|
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
</Listbox>
|
||||||
|
|
||||||
|
<p className="font-bold">{messages.account}</p>
|
||||||
|
{context.isSignedIn() ? (
|
||||||
|
<Listbox
|
||||||
|
aria-label="Account links"
|
||||||
|
itemClasses={{
|
||||||
|
base: 'h-10 text-large',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListboxItem
|
||||||
|
key="account"
|
||||||
|
title={messages.account}
|
||||||
|
startContent={<UserAvatar context={context} />}
|
||||||
|
onPress={() => {
|
||||||
|
router.push('/account', { locale: locale });
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ListboxItem
|
||||||
|
key="signout"
|
||||||
|
title={messages.signOut}
|
||||||
|
startContent={<ArrowRightFromLine size={16} />}
|
||||||
|
onPress={() => {
|
||||||
|
context.setToken(null);
|
||||||
|
context.removeTokenFromLocalStorage();
|
||||||
|
router.push(`/`, { locale: locale });
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Listbox>
|
||||||
|
) : (
|
||||||
|
<Listbox
|
||||||
|
aria-label="Account links"
|
||||||
|
itemClasses={{
|
||||||
|
base: 'h-10 text-large',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListboxItem
|
||||||
|
key="signin"
|
||||||
|
startContent={<ArrowRightToLine size={16} />}
|
||||||
|
title={messages.signIn}
|
||||||
|
onClick={() => {
|
||||||
|
router.push('/account/signin', { locale: locale });
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ListboxItem
|
||||||
|
key="signup"
|
||||||
|
title={messages.signUp}
|
||||||
|
startContent={<PenTool size={16} />}
|
||||||
|
onClick={() => {
|
||||||
|
router.push('/account/signup', { locale: locale });
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Listbox>
|
||||||
|
)}
|
||||||
|
<p className="font-bold">{messages.languages}</p>
|
||||||
|
<Listbox
|
||||||
|
aria-label="Language links"
|
||||||
|
itemClasses={{
|
||||||
|
base: 'h-10 text-large',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{locales.map((entry) => (
|
||||||
|
<ListboxItem
|
||||||
|
key={entry.code}
|
||||||
|
startContent={<Globe size={16} />}
|
||||||
|
title={entry.name}
|
||||||
|
onClick={() => {
|
||||||
|
changeLocale(entry.code);
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Listbox>
|
||||||
</div>
|
</div>
|
||||||
</NavbarMenu>
|
</NavbarMenu>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|||||||
Reference in New Issue
Block a user