dashboard: restrict access to organization users

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-03-26 21:13:11 +01:00
parent 45e9b1d702
commit d0079c8dc3
33 changed files with 856 additions and 225 deletions

View 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>
);
}

View File

@@ -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} />

View File

@@ -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>

View 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>
);
}

View File

@@ -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'}
>

View File

@@ -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 }

View File

@@ -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,

View File

@@ -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}

View File

@@ -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 };