dashboard: restrict access to organization users
This commit is contained in:
46
apps/dashboard/src/components/dot.tsx
Normal file
46
apps/dashboard/src/components/dot.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { cn } from '@/utils/cn';
|
||||
|
||||
interface DotProps {
|
||||
className?: string;
|
||||
size?: number;
|
||||
animated?: boolean;
|
||||
}
|
||||
|
||||
function filterCn(filter: string[], className: string | undefined) {
|
||||
const split: string[] = className?.split(' ') || [];
|
||||
return split
|
||||
.filter((item) => !filter.some((filterItem) => item.startsWith(filterItem)))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export function Dot({ className, size = 8, animated }: DotProps) {
|
||||
const style = {
|
||||
width: size,
|
||||
height: size,
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'relative',
|
||||
filterCn(['bg-', 'animate-', 'group-hover/row'], className)
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'absolute !m-0 rounded-full',
|
||||
animated !== false && 'animate-ping',
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
'absolute !m-0 rounded-full',
|
||||
filterCn(['animate-', 'group-hover/row'], className)
|
||||
)}
|
||||
style={style}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { cn } from '@/utils/cn';
|
||||
import { BoxSelectIcon } from 'lucide-react';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
@@ -5,15 +6,17 @@ interface FullPageEmptyStateProps {
|
||||
icon?: LucideIcon;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function FullPageEmptyState({
|
||||
icon: Icon = BoxSelectIcon,
|
||||
title,
|
||||
children,
|
||||
className,
|
||||
}: FullPageEmptyStateProps) {
|
||||
return (
|
||||
<div className="p-4 flex items-center justify-center">
|
||||
<div className={cn('p-4 flex items-center justify-center', className)}>
|
||||
<div className="p-8 w-full max-w-xl flex flex-col items-center justify-center">
|
||||
<div className="w-24 h-24 bg-white shadow-sm rounded-full flex justify-center items-center mb-6">
|
||||
<Icon size={60} strokeWidth={1} />
|
||||
|
||||
@@ -120,11 +120,7 @@ export function FilterItem({ filter, event }: FilterProps) {
|
||||
items={valuesCombobox}
|
||||
value={filter.value}
|
||||
className="flex-1"
|
||||
onChange={(setFn) => {
|
||||
changeFilterValue(
|
||||
typeof setFn === 'function' ? setFn(filter.value) : setFn
|
||||
);
|
||||
}}
|
||||
onChange={changeFilterValue}
|
||||
placeholder="Select..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
26
apps/dashboard/src/components/tooltip-complete.tsx
Normal file
26
apps/dashboard/src/components/tooltip-complete.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||
|
||||
interface TooltipCompleteProps {
|
||||
children: React.ReactNode | string;
|
||||
content: React.ReactNode | string;
|
||||
disabled?: boolean;
|
||||
side?: 'top' | 'right' | 'bottom' | 'left';
|
||||
}
|
||||
|
||||
export function TooltipComplete({
|
||||
children,
|
||||
disabled,
|
||||
content,
|
||||
side,
|
||||
}: TooltipCompleteProps) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild={typeof children !== 'string'}>
|
||||
{children}
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side={side} disabled={disabled}>
|
||||
{content}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ type IItem = Record<'value' | 'label', IValue>;
|
||||
|
||||
interface ComboboxAdvancedProps {
|
||||
value: IValue[];
|
||||
onChange: React.Dispatch<React.SetStateAction<IValue[]>>;
|
||||
onChange: (value: IValue[]) => void;
|
||||
items: IItem[];
|
||||
placeholder: string;
|
||||
className?: string;
|
||||
@@ -57,12 +57,11 @@ export function ComboboxAdvanced({
|
||||
}}
|
||||
onSelect={() => {
|
||||
setInputValue('');
|
||||
onChange((prev) => {
|
||||
if (prev.includes(item.value)) {
|
||||
return prev.filter((s) => s !== item.value);
|
||||
}
|
||||
return [...prev, item.value];
|
||||
});
|
||||
onChange(
|
||||
value.includes(item.value)
|
||||
? value.filter((s) => s !== item.value)
|
||||
: [...value, item.value]
|
||||
);
|
||||
}}
|
||||
className={'cursor-pointer flex items-center gap-2'}
|
||||
>
|
||||
|
||||
@@ -1,47 +1,42 @@
|
||||
'use client';
|
||||
import * as React from "react"
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||
import { Circle } from "lucide-react"
|
||||
|
||||
import * as React from 'react';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { cn } from "@/utils/cn"
|
||||
|
||||
export type RadioGroupProps = React.InputHTMLAttributes<HTMLDivElement>;
|
||||
export type RadioGroupItemProps =
|
||||
React.InputHTMLAttributes<HTMLButtonElement> & {
|
||||
active?: boolean;
|
||||
};
|
||||
const RadioGroup = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Root
|
||||
className={cn("grid gap-2", className)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
})
|
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
|
||||
|
||||
const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-10 divide-x rounded-md border border-input bg-background text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
const RadioGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
})
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
|
||||
|
||||
const RadioGroupItem = React.forwardRef<HTMLButtonElement, RadioGroupItemProps>(
|
||||
({ className, active, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex-1 px-3 whitespace-nowrap leading-none hover:bg-slate-100 transition-colors font-medium',
|
||||
className,
|
||||
active && 'bg-slate-100'
|
||||
)}
|
||||
type="button"
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
RadioGroup.displayName = 'RadioGroup';
|
||||
RadioGroupItem.displayName = 'RadioGroupItem';
|
||||
|
||||
export { RadioGroup, RadioGroupItem };
|
||||
export { RadioGroup, RadioGroupItem }
|
||||
|
||||
@@ -133,6 +133,14 @@ const SheetDescription = React.forwardRef<
|
||||
));
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||
|
||||
export function closeSheet() {
|
||||
if (typeof document === 'undefined') return;
|
||||
const element = document.querySelector('#close-sheet');
|
||||
if (element instanceof HTMLElement) {
|
||||
element.click();
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Sheet,
|
||||
SheetPortal,
|
||||
|
||||
@@ -6,18 +6,14 @@ import { cn } from '@/utils/cn';
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement> & {
|
||||
wrapper?: boolean;
|
||||
overflow?: boolean;
|
||||
}
|
||||
>(({ className, wrapper, overflow = true, ...props }, ref) => (
|
||||
>(({ className, overflow = true, ...props }, ref) => (
|
||||
<div className={cn('card', className)}>
|
||||
<div className={cn('relative w-full', overflow && 'overflow-auto')}>
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'w-full caption-bottom text-sm [&.mini]:text-xs',
|
||||
className
|
||||
)}
|
||||
className={cn('w-full caption-bottom text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@@ -79,7 +75,7 @@ const TableHead = React.forwardRef<
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'p-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 shadow-[0_0_0_0.5px] shadow-border [.mini_&]:p-2',
|
||||
'px-4 h-10 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 shadow-[0_0_0_0.5px] shadow-border',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -94,7 +90,7 @@ const TableCell = React.forwardRef<
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'p-4 align-middle [&:has([role=checkbox])]:pr-0 shadow-[0_0_0_0.5px] shadow-border [.mini_&]:p-2 whitespace-nowrap',
|
||||
'px-4 h-12 align-middle [&:has([role=checkbox])]:pr-0 shadow-[0_0_0_0.5px] shadow-border whitespace-nowrap',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -12,18 +12,22 @@ const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 overflow-hidden rounded-md border bg-black px-3 py-1.5 text-sm text-popover shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & {
|
||||
disabled?: boolean;
|
||||
}
|
||||
>(({ className, sideOffset = 4, disabled, ...props }, ref) =>
|
||||
disabled ? null : (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 overflow-hidden rounded-md border bg-black px-3 py-1.5 text-sm text-popover shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
||||
|
||||
Reference in New Issue
Block a user