mirror of https://github.com/buster-so/buster.git
fix sidebar settings for interfaces
This commit is contained in:
parent
4fa7e92a38
commit
3ecdf641d3
|
@ -1,8 +1,8 @@
|
||||||
export enum VerificationStatus {
|
export enum VerificationStatus {
|
||||||
NOT_REQUESTED = 'NOT REQUESTED',
|
NOT_REQUESTED = 'notRequested',
|
||||||
REQUESTED = 'REQUESTED',
|
REQUESTED = 'requested',
|
||||||
IN_REVIEW = 'IN REVIEW',
|
IN_REVIEW = 'inReview',
|
||||||
VERIFIED = 'VERIFIED',
|
VERIFIED = 'verified',
|
||||||
BACKLOGGED = 'BACKLOGGED',
|
BACKLOGGED = 'backlogged',
|
||||||
NOT_VERIFIED = 'NOT VERIFIED'
|
NOT_VERIFIED = 'notVerified'
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { useMemoizedFn } from '@/hooks';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
import { Star } from '@/components/ui/icons';
|
import { Star } from '@/components/ui/icons';
|
||||||
|
import { Star as StarFilled } from '@/components/ui/icons/NucleoIconFilled';
|
||||||
import { cva } from 'class-variance-authority';
|
import { cva } from 'class-variance-authority';
|
||||||
import {
|
import {
|
||||||
useAddUserFavorite,
|
useAddUserFavorite,
|
||||||
|
@ -62,7 +63,7 @@ export const FavoriteStar: React.FC<{
|
||||||
return (
|
return (
|
||||||
<AppTooltip title={tooltipText} key={tooltipText}>
|
<AppTooltip title={tooltipText} key={tooltipText}>
|
||||||
<Button
|
<Button
|
||||||
className={cn(className, 'flex')}
|
className={cn('flex transition-none', isFavorited && 'opacity-100!', className)}
|
||||||
onClick={onFavoriteClick}
|
onClick={onFavoriteClick}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
prefix={
|
prefix={
|
||||||
|
@ -73,7 +74,7 @@ export const FavoriteStar: React.FC<{
|
||||||
isFavorited
|
isFavorited
|
||||||
})
|
})
|
||||||
)}>
|
)}>
|
||||||
<Star />
|
{isFavorited ? <StarFilled /> : <Star />}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -26,17 +26,15 @@ export const StatusBadgeIndicator: React.FC<{
|
||||||
const isNotVerified =
|
const isNotVerified =
|
||||||
status === VerificationStatus.NOT_VERIFIED || VerificationStatus.NOT_REQUESTED;
|
status === VerificationStatus.NOT_VERIFIED || VerificationStatus.NOT_REQUESTED;
|
||||||
const sharedClass = cn(`flex items-center justify-center rounded-full`, colorClasses);
|
const sharedClass = cn(`flex items-center justify-center rounded-full`, colorClasses);
|
||||||
const _size = isNotVerified ? size : 16;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppTooltip title={showTooltip ? tooltipText : ''}>
|
<AppTooltip title={showTooltip ? tooltipText : ''}>
|
||||||
<div
|
<div
|
||||||
className={`rounded-full ${className} ${sharedClass} ${isNotVerified ? '' : ''}`}
|
className={`rounded-full ${className} ${sharedClass} ${isNotVerified ? '' : ''}`}
|
||||||
style={{
|
style={{
|
||||||
width: _size,
|
width: size,
|
||||||
height: _size
|
height: size
|
||||||
}}>
|
}}>
|
||||||
<Icon size={_size} />
|
<Icon size={size} />
|
||||||
</div>
|
</div>
|
||||||
</AppTooltip>
|
</AppTooltip>
|
||||||
);
|
);
|
||||||
|
@ -54,6 +52,7 @@ const statusRecordIcon: Record<VerificationStatus, React.FC<any>> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIcon = (status: BusterMetricListItem['status']) => {
|
const getIcon = (status: BusterMetricListItem['status']) => {
|
||||||
|
console.log(status, statusRecordIcon);
|
||||||
return statusRecordIcon[status] || (() => <React.Fragment />);
|
return statusRecordIcon[status] || (() => <React.Fragment />);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { SidebarPrimary } from './SidebarPrimary';
|
import { SidebarPrimary } from './SidebarPrimary';
|
||||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
import { BusterRoutes, createBusterRoute } from '../../../routes';
|
||||||
import { ShareAssetType } from '@/api/asset_interfaces/share/shareInterfaces';
|
import { ShareAssetType } from '../../../api/asset_interfaces/share';
|
||||||
|
import React from 'react';
|
||||||
const meta: Meta<typeof SidebarPrimary> = {
|
const meta: Meta<typeof SidebarPrimary> = {
|
||||||
title: 'Features/Sidebars/SidebarPrimary',
|
title: 'Features/Sidebars/SidebarPrimary',
|
||||||
component: SidebarPrimary,
|
component: SidebarPrimary,
|
||||||
|
|
|
@ -18,9 +18,14 @@ import { SupportModal } from '../modal/SupportModal';
|
||||||
import { InvitePeopleModal } from '../modal/InvitePeopleModal';
|
import { InvitePeopleModal } from '../modal/InvitePeopleModal';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { SidebarUserFooter } from './SidebarUserFooter/SidebarUserFooter';
|
import { SidebarUserFooter } from './SidebarUserFooter/SidebarUserFooter';
|
||||||
import { useGetUserFavorites, useUpdateUserFavorites } from '@/api/buster_rest';
|
import {
|
||||||
|
useDeleteUserFavorite,
|
||||||
|
useGetUserFavorites,
|
||||||
|
useUpdateUserFavorites
|
||||||
|
} from '@/api/buster_rest';
|
||||||
|
|
||||||
const topItems: ISidebarList = {
|
const topItems: ISidebarList = {
|
||||||
|
id: 'top-items',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Home',
|
label: 'Home',
|
||||||
|
@ -39,6 +44,7 @@ const topItems: ISidebarList = {
|
||||||
|
|
||||||
const yourStuff: ISidebarGroup = {
|
const yourStuff: ISidebarGroup = {
|
||||||
label: 'Your stuff',
|
label: 'Your stuff',
|
||||||
|
id: 'your-stuff',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Metrics',
|
label: 'Metrics',
|
||||||
|
@ -63,6 +69,7 @@ const yourStuff: ISidebarGroup = {
|
||||||
|
|
||||||
const adminTools: ISidebarGroup = {
|
const adminTools: ISidebarGroup = {
|
||||||
label: 'Admin tools',
|
label: 'Admin tools',
|
||||||
|
id: 'admin-tools',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Logs',
|
label: 'Logs',
|
||||||
|
@ -90,6 +97,7 @@ const tryGroup = (
|
||||||
onClickLeaveFeedback: () => void
|
onClickLeaveFeedback: () => void
|
||||||
): ISidebarGroup => ({
|
): ISidebarGroup => ({
|
||||||
label: 'Try',
|
label: 'Try',
|
||||||
|
id: 'try',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Invite people',
|
label: 'Invite people',
|
||||||
|
@ -115,6 +123,7 @@ export const SidebarPrimary = React.memo(() => {
|
||||||
const onToggleInviteModal = useAppLayoutContextSelector((s) => s.onToggleInviteModal);
|
const onToggleInviteModal = useAppLayoutContextSelector((s) => s.onToggleInviteModal);
|
||||||
const [openSupportModal, setOpenSupportModal] = useState(false);
|
const [openSupportModal, setOpenSupportModal] = useState(false);
|
||||||
const { mutateAsync: updateUserFavorites } = useUpdateUserFavorites();
|
const { mutateAsync: updateUserFavorites } = useUpdateUserFavorites();
|
||||||
|
const { mutateAsync: deleteUserFavorite } = useDeleteUserFavorite();
|
||||||
|
|
||||||
const onFavoritesReorder = useMemoizedFn((itemIds: string[]) => {
|
const onFavoritesReorder = useMemoizedFn((itemIds: string[]) => {
|
||||||
updateUserFavorites(itemIds);
|
updateUserFavorites(itemIds);
|
||||||
|
@ -130,7 +139,7 @@ export const SidebarPrimary = React.memo(() => {
|
||||||
items.push(yourStuff);
|
items.push(yourStuff);
|
||||||
|
|
||||||
if (favorites && favorites.length > 0) {
|
if (favorites && favorites.length > 0) {
|
||||||
items.push(favoritesDropdown(favorites, onFavoritesReorder));
|
items.push(favoritesDropdown(favorites, { deleteUserFavorite, onFavoritesReorder }));
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(tryGroup(onToggleInviteModal, () => setOpenSupportModal(true)));
|
items.push(tryGroup(onToggleInviteModal, () => setOpenSupportModal(true)));
|
||||||
|
@ -213,12 +222,19 @@ GlobalModals.displayName = 'GlobalModals';
|
||||||
|
|
||||||
const favoritesDropdown = (
|
const favoritesDropdown = (
|
||||||
favorites: BusterUserFavorite[],
|
favorites: BusterUserFavorite[],
|
||||||
onItemsReorder: (itemIds: string[]) => void
|
{
|
||||||
|
onFavoritesReorder,
|
||||||
|
deleteUserFavorite
|
||||||
|
}: {
|
||||||
|
onFavoritesReorder: (itemIds: string[]) => void;
|
||||||
|
deleteUserFavorite: (itemId: string) => void;
|
||||||
|
}
|
||||||
): ISidebarGroup => {
|
): ISidebarGroup => {
|
||||||
return {
|
return {
|
||||||
label: 'Favorites',
|
label: 'Favorites',
|
||||||
|
id: 'favorites',
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
onItemsReorder,
|
onItemsReorder: onFavoritesReorder,
|
||||||
items: favorites.map((favorite) => {
|
items: favorites.map((favorite) => {
|
||||||
const Icon = assetTypeToIcon(favorite.asset_type);
|
const Icon = assetTypeToIcon(favorite.asset_type);
|
||||||
const route = assetTypeToRoute(favorite.asset_type, favorite.id);
|
const route = assetTypeToRoute(favorite.asset_type, favorite.id);
|
||||||
|
@ -226,7 +242,8 @@ const favoritesDropdown = (
|
||||||
label: favorite.name,
|
label: favorite.name,
|
||||||
icon: <Icon />,
|
icon: <Icon />,
|
||||||
route,
|
route,
|
||||||
id: route
|
id: route,
|
||||||
|
onRemove: () => deleteUserFavorite(favorite.id)
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { SidebarSettings } from './SidebarSettings';
|
import { SidebarSettings } from './SidebarSettings';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
const meta: Meta<typeof SidebarSettings> = {
|
const meta: Meta<typeof SidebarSettings> = {
|
||||||
title: 'Features/Sidebars/SidebarSettings',
|
title: 'Features/Sidebars/SidebarSettings',
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { SidebarUserFooter } from './SidebarUserFooter/SidebarUserFooter';
|
||||||
const accountItems: ISidebarGroup = {
|
const accountItems: ISidebarGroup = {
|
||||||
label: 'Account',
|
label: 'Account',
|
||||||
variant: 'icon',
|
variant: 'icon',
|
||||||
|
id: 'account',
|
||||||
icon: <CircleUser />,
|
icon: <CircleUser />,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
@ -25,6 +26,7 @@ const accountItems: ISidebarGroup = {
|
||||||
const workspaceItems: ISidebarGroup = {
|
const workspaceItems: ISidebarGroup = {
|
||||||
label: 'Workspace',
|
label: 'Workspace',
|
||||||
variant: 'icon',
|
variant: 'icon',
|
||||||
|
id: 'workspace',
|
||||||
icon: <ApartmentBuilding />,
|
icon: <ApartmentBuilding />,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
@ -38,6 +40,7 @@ const workspaceItems: ISidebarGroup = {
|
||||||
const permissionAndSecurityItems: ISidebarGroup = {
|
const permissionAndSecurityItems: ISidebarGroup = {
|
||||||
label: 'Permission & Security',
|
label: 'Permission & Security',
|
||||||
variant: 'icon',
|
variant: 'icon',
|
||||||
|
id: 'permission-and-security',
|
||||||
icon: <LockCircle />,
|
icon: <LockCircle />,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,21 +2,16 @@ import React from 'react';
|
||||||
import { ISidebarGroup, ISidebarList, SidebarProps } from './interfaces';
|
import { ISidebarGroup, ISidebarList, SidebarProps } from './interfaces';
|
||||||
import { SidebarCollapsible } from './SidebarCollapsible';
|
import { SidebarCollapsible } from './SidebarCollapsible';
|
||||||
import { SidebarItem } from './SidebarItem';
|
import { SidebarItem } from './SidebarItem';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
|
||||||
|
|
||||||
export const Sidebar: React.FC<SidebarProps> = React.memo(
|
export const Sidebar: React.FC<SidebarProps> = React.memo(
|
||||||
({ header, content, footer, activeItem }) => {
|
({ header, content, footer, activeItem }) => {
|
||||||
const onItemsReorder = useMemoizedFn((ids: string[], contentIndex: number) => {
|
|
||||||
console.log('onItemsReorder', ids);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col overflow-hidden px-3.5 pt-4.5">
|
<div className="flex h-full flex-col overflow-hidden px-3.5 pt-4.5">
|
||||||
<div className="flex flex-col space-y-4.5 overflow-hidden">
|
<div className="flex flex-col space-y-4.5 overflow-hidden">
|
||||||
<div className="mb-5"> {header}</div>
|
<div className="mb-5"> {header}</div>
|
||||||
<div className="flex flex-grow flex-col space-y-4.5 overflow-y-auto pb-3">
|
<div className="flex flex-grow flex-col space-y-4.5 overflow-y-auto pb-3">
|
||||||
{content.map((item, index) => (
|
{content.map((item) => (
|
||||||
<ContentSelector key={index} content={item} activeItem={activeItem} />
|
<ContentSelector key={item.id} content={item} activeItem={activeItem} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Collapsible,
|
Collapsible,
|
||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
|
@ -118,7 +118,11 @@ export const SidebarCollapsible: React.FC<
|
||||||
const [draggingId, setDraggingId] = React.useState<string | null>(null);
|
const [draggingId, setDraggingId] = React.useState<string | null>(null);
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor),
|
useSensor(PointerSensor, {
|
||||||
|
activationConstraint: {
|
||||||
|
distance: 2
|
||||||
|
}
|
||||||
|
}),
|
||||||
useSensor(KeyboardSensor, {
|
useSensor(KeyboardSensor, {
|
||||||
coordinateGetter: sortableKeyboardCoordinates
|
coordinateGetter: sortableKeyboardCoordinates
|
||||||
})
|
})
|
||||||
|
@ -145,6 +149,10 @@ export const SidebarCollapsible: React.FC<
|
||||||
|
|
||||||
const draggingItem = draggingId ? sortedItems.find((item) => item.id === draggingId) : null;
|
const draggingItem = draggingId ? sortedItems.find((item) => item.id === draggingId) : null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSortedItems(items);
|
||||||
|
}, [items]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="space-y-0.5">
|
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="space-y-0.5">
|
||||||
{variant === 'collapsible' && (
|
{variant === 'collapsible' && (
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { SidebarItem } from './SidebarItem';
|
import { SidebarItem } from './SidebarItem';
|
||||||
import { HouseModern } from '@/components/ui/icons';
|
import { HouseModern } from '../icons/NucleoIconOutlined';
|
||||||
import { BusterRoutes } from '@/routes';
|
import { BusterRoutes } from '../../../routes';
|
||||||
|
import React from 'react';
|
||||||
const meta: Meta<typeof SidebarItem> = {
|
const meta: Meta<typeof SidebarItem> = {
|
||||||
title: 'UI/Sidebar/SidebarItem',
|
title: 'UI/Sidebar/SidebarItem',
|
||||||
component: SidebarItem,
|
component: SidebarItem,
|
||||||
|
|
|
@ -83,7 +83,7 @@ export const SidebarItem: React.FC<
|
||||||
href={route || ''}
|
href={route || ''}
|
||||||
className={cn(itemVariants({ active, disabled, variant }), className)}
|
className={cn(itemVariants({ active, disabled, variant }), className)}
|
||||||
onClick={onClick}>
|
onClick={onClick}>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 overflow-hidden">
|
||||||
<span
|
<span
|
||||||
className={cn('text-icon-size! text-icon-color', {
|
className={cn('text-icon-size! text-icon-color', {
|
||||||
'text-text-disabled': disabled,
|
'text-text-disabled': disabled,
|
||||||
|
@ -99,7 +99,11 @@ export const SidebarItem: React.FC<
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size={'small'}
|
size={'small'}
|
||||||
prefix={<Xmark />}
|
prefix={<Xmark />}
|
||||||
onClick={onRemove}></Button>
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
onRemove();
|
||||||
|
}}></Button>
|
||||||
)}
|
)}
|
||||||
</ItemNode>
|
</ItemNode>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,6 +14,7 @@ export interface ISidebarItem {
|
||||||
export interface ISidebarGroup {
|
export interface ISidebarGroup {
|
||||||
label: string;
|
label: string;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
|
id: string;
|
||||||
items: ISidebarItem[];
|
items: ISidebarItem[];
|
||||||
variant?: 'collapsible' | 'icon'; //default is collapsible
|
variant?: 'collapsible' | 'icon'; //default is collapsible
|
||||||
defaultOpen?: boolean; //will default to true
|
defaultOpen?: boolean; //will default to true
|
||||||
|
@ -23,6 +24,7 @@ export interface ISidebarGroup {
|
||||||
|
|
||||||
export interface ISidebarList {
|
export interface ISidebarList {
|
||||||
items: ISidebarItem[];
|
items: ISidebarItem[];
|
||||||
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SidebarContent = ISidebarGroup | ISidebarList;
|
type SidebarContent = ISidebarGroup | ISidebarList;
|
||||||
|
|
|
@ -13,7 +13,7 @@ const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||||
const TooltipContent = React.forwardRef<
|
const TooltipContent = React.forwardRef<
|
||||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||||
>(({ className, sideOffset = 6, ...props }, ref) => (
|
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||||
<TooltipPrimitive.Content
|
<TooltipPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
|
|
Loading…
Reference in New Issue