Skip to content

Commit

Permalink
chore(dashboard): bring the hrm alive (#6703)
Browse files Browse the repository at this point in the history
  • Loading branch information
LetItRock authored Oct 16, 2024
1 parent bfb8b95 commit f2bfd0a
Show file tree
Hide file tree
Showing 41 changed files with 324 additions and 297 deletions.
2 changes: 1 addition & 1 deletion apps/dashboard/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default tseslint.config(
rules: {
...reactHooks.configs.recommended.rules,
'@typescript-eslint/no-explicit-any': 'warn',
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
'react-refresh/only-export-components': ['error', { allowConstantExport: true }],
'@typescript-eslint/no-unused-vars': ['off'],
'@typescript-eslint/naming-convention': [
'error',
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/error-page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRouteError } from 'react-router-dom';

export default function ErrorPage() {
const error = useRouteError() as any;
const error = useRouteError() as { statusText?: string; message: string };
console.error(error);

return (
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/icons/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { StepTypeEnum } from '@novu/shared';
import {
RiCellphoneFill,
RiChatThreadFill,
Expand All @@ -11,6 +10,7 @@ import { Mail3Fill } from './mail-3-fill';
import { Notification5Fill } from './notification-5-fill';
import { Sms } from './sms';
import { IconType } from 'react-icons/lib';
import { StepTypeEnum } from '@/utils/enums';

export const STEP_TYPE_TO_ICON: Record<StepTypeEnum, IconType> = {
[StepTypeEnum.CHAT]: RiChatThreadFill,
Expand Down
60 changes: 3 additions & 57 deletions apps/dashboard/src/components/primitives/badge.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,14 @@
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { type VariantProps } from 'class-variance-authority';
import { cn } from '@/utils/ui';

const badgeVariants = cva(
'inline-flex items-center h-5 border px-2 py-1 transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
neutral: 'border-neutral-500 bg-neutral-500',
feature: 'border-feature bg-feature',
information: 'border-information bg-information',
highlighted: 'border-highlighted bg-highlighted',
stable: 'border-stable bg-stable',
verified: 'border-verified bg-verified',
destructive: 'border-destructive bg-destructive',
'destructive-light': 'border-transparent bg-destructive/10',
success: 'border-success bg-success',
'success-light': 'border-transparent bg-success/10',
warning: 'border-warning bg-warning',
'warning-light': 'border-transparent bg-warning/10',
alert: 'border-alert bg-alert',
soft: 'border-neutral-alpha-200 bg-neutral-alpha-200',
},
kind: {
default: 'rounded-md',
pill: 'rounded-full',
'pill-stroke': 'rounded-full bg-transparent',
},
},
defaultVariants: {
variant: 'neutral',
kind: 'default',
},
}
);
import { badgeContentVariants, badgeVariants } from './variants';

export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}

function Badge({ className, variant, kind, ...props }: BadgeProps) {
return <div className={cn(badgeVariants({ variant, kind }), className)} {...props} />;
}

const badgeContentVariants = cva('text-xs font-medium', {
variants: {
variant: {
foreground: 'text-foreground',
'neutral-foreground': 'text-neutral-foreground',
neutral: 'text-neutral-500',
feature: 'text-feature',
information: 'text-information',
highlighted: 'text-highlighted',
stable: 'text-stable',
verified: 'text-verified',
destructive: 'text-destructive',
success: 'text-success',
warning: 'text-warning',
alert: 'text-alert',
},
},
defaultVariants: {
variant: 'foreground',
},
});

export interface BadgeContentProps
extends React.HTMLAttributes<HTMLSpanElement>,
VariantProps<typeof badgeContentVariants> {}
Expand All @@ -71,4 +17,4 @@ function BadgeContent({ className, variant, ...props }: BadgeContentProps) {
return <span className={cn(badgeContentVariants({ variant }), className)} {...props} />;
}

export { Badge, BadgeContent, badgeVariants, badgeContentVariants };
export { Badge, BadgeContent };
37 changes: 3 additions & 34 deletions apps/dashboard/src/components/primitives/button.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { type VariantProps } from 'class-variance-authority';
import { cn } from '@/utils/ui';

const buttonVariants = cva(
`relative isolate inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50`,
{
variants: {
variant: {
default:
'bg-gradient-to-b from-neutral-alpha-900 to-neutral-900 text-neutral-foreground shadow-[inset_0_-4px_2px_-2px_hsl(var(--neutral-900)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--neutral-900)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
primary:
'bg-gradient-to-b from-primary/90 to-primary text-primary-foreground shadow-[inset_0_-4px_2px_-2px_hsl(var(--primary)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--primary)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
destructive:
'bg-gradient-to-b from-destructive/90 to-destructive text-destructive-foreground shadow-[inset_0_-4px_2px_-2px_hsl(var(--destructive)),inset_0_0_0_1px_rgba(255,255,255,0.16),0_0_0_1px_hsl(var(--destructive)),0px_1px_2px_0px_#0E121B3D] after:content-[""] after:absolute after:w-full after:h-full after:bg-gradient-to-b after:from-background/10 after:opacity-0 hover:after:opacity-100 after:rounded-lg after:transition-opacity after:duration-300',
outline: 'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',
ghost: 'hover:bg-accent',
link: 'underline-offset-4 hover:underline',
light:
'bg-destructive/10 hover:bg-background hover:border hover:border-destructive text-destructive focus-visible:ring-destructive/10 focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:bg-background focus-visible:border focus-visible:border-destructive',
},
size: {
default: 'h-9 p-2.5',
xs: 'h-6 px-1.5 rounded-md text-xs',
sm: 'h-8 px-3 rounded-md text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'size-8',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
import { buttonVariants } from './variants';

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
Expand All @@ -49,4 +18,4 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
);
Button.displayName = 'Button';

export { Button, buttonVariants };
export { Button };
40 changes: 40 additions & 0 deletions apps/dashboard/src/components/primitives/form/form-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { FieldPath, FieldValues, useFormContext } from 'react-hook-form';

type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};

export const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);

type FormItemContextValue = {
id: string;
};

export const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);

export const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();

const fieldState = getFieldState(fieldContext.name, formState);

if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>');
}

const { id } = itemContext;

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import * as React from 'react';
import * as LabelPrimitive from '@radix-ui/react-label';
import { Slot } from '@radix-ui/react-slot';
import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider, useFormContext } from 'react-hook-form';
import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider } from 'react-hook-form';

import { cn } from '@/utils/ui';
import { Label } from '@/components/primitives/label';
import { cva } from 'class-variance-authority';
import { RiInformationFill } from 'react-icons/ri';
import { FormFieldContext, FormItemContext, useFormField } from './form-context';

const Form = FormProvider;

type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
name: TName;
};

const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);

const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
Expand All @@ -32,35 +24,6 @@ const FormField = <
);
};

const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState, formState } = useFormContext();

const fieldState = getFieldState(fieldContext.name, formState);

if (!fieldContext) {
throw new Error('useFormField should be used within <FormField>');
}

const { id } = itemContext;

return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
};
};

type FormItemContextValue = {
id: string;
};

const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);

const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const id = React.useId();
Expand Down Expand Up @@ -145,4 +108,4 @@ const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<
);
FormMessage.displayName = 'FormMessage';

export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField };
export { Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField };
1 change: 1 addition & 0 deletions apps/dashboard/src/components/primitives/form/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './form';
3 changes: 2 additions & 1 deletion apps/dashboard/src/components/primitives/pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
} from '@radix-ui/react-icons';

import { cn } from '@/utils/ui';
import { ButtonProps, buttonVariants } from '@/components/primitives/button';
import { ButtonProps } from '@/components/primitives/button';
import { buttonVariants } from '@/components/primitives/variants';
import { Link, LinkProps } from 'react-router-dom';

const Pagination = ({ className, ...props }: React.ComponentProps<'nav'>) => (
Expand Down
32 changes: 3 additions & 29 deletions apps/dashboard/src/components/primitives/step.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,13 @@
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { type VariantProps } from 'class-variance-authority';

import { cn } from '@/utils/ui';

const stepVariants = cva(
'inline-flex items-center shadow-xs rounded-full border text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 bg-neutral-50',
{
variants: {
variant: {
// use solid bg here because we usually stack these on top of each other
neutral: 'border-neutral-100 text-neutral-400',
feature: 'border-feature/30 text-feature/30',
information: 'border-information/30 text-information/30',
highlighted: 'border-highlighted/30 text-highlighted/30',
stable: 'border-stable/30 text-stable/30',
verified: 'border-verified/30 text-verified/30',
destructive: 'border-destructive/30 text-destructive/30',
warning: 'border-warning/30 text-warning/30',
alert: 'border-alert/30 text-alert/30',
},
size: {
default: 'p-1 [&>svg]:size-3.5',
},
},
defaultVariants: {
variant: 'neutral',
size: 'default',
},
}
);
import { stepVariants } from './variants';

export interface StepProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof stepVariants> {}

function Step({ className, variant, ...props }: StepProps) {
return <div className={cn(stepVariants({ variant }), className)} {...props} />;
}

export { Step, stepVariants };
export { Step };
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/primitives/tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ function Tag({ className, variant, ...props }: TagProps) {
return <div className={cn(tagVariants({ variant }), className)} {...props} />;
}

export { Tag, tagVariants };
export { Tag };
Loading

0 comments on commit f2bfd0a

Please sign in to comment.