feat: dashboard widgets (#3362)
* fix: created dashboard, widgets and dashboard widget model * fix: new user home dashboard * chore: recent projects list * chore: recent collaborators * chore: priority order change * chore: payload changes * chore: collaborator's active issue count * chore: all dashboard widgets added with services and typs * chore: centered metric for pie chart * chore: widget filters * chore: created issue filter * fix: created and assigned issues payload change * chore: created issue payload change * fix: date filter change * chore: implement filters * fix: added expansion fields * fix: changed issue structure with relation * chore: new issues response * fix: project member fix * chore: updated issue_relation structure * chore: code cleanup * chore: update issues response and added empty states * fix: button text wrap * chore: update empty state messages * fix: filters * chore: update dark mode empty states * build-error: Type check in the issue relation service * fix: issues redirection * fix: project empty state * chore: project member active check * chore: project member check in state and priority * chore: remove console logs and replace harcoded values with constants * fix: code refactoring * fix: key name changed * refactor: mapping through similar components using an array * fix: build errors --------- Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> Co-authored-by: gurusainath <gurusainath007@gmail.com>
This commit is contained in:
parent
f347c1cd69
commit
c9337d4a41
122 changed files with 6790 additions and 849 deletions
4
packages/ui/helpers.ts
Normal file
4
packages/ui/helpers.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
|
||||
|
|
@ -17,6 +17,17 @@
|
|||
"lint": "eslint src/",
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blueprintjs/core": "^4.16.3",
|
||||
"@blueprintjs/popover2": "^1.13.3",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"clsx": "^2.0.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-popper": "^2.3.0",
|
||||
"tailwind-merge": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.5.2",
|
||||
"@types/react": "^18.2.42",
|
||||
|
|
@ -29,14 +40,5 @@
|
|||
"tsconfig": "*",
|
||||
"tsup": "^5.10.1",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blueprintjs/core": "^4.16.3",
|
||||
"@blueprintjs/popover2": "^1.13.3",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-popper": "^2.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ export const Avatar: React.FC<Props> = (props) => {
|
|||
}
|
||||
: {}
|
||||
}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{src ? (
|
||||
<img src={src} className={`h-full w-full ${getBorderRadius(shape)} ${className}`} alt={name} />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import * as React from "react";
|
||||
|
||||
import { getIconStyling, getButtonStyling, TButtonVariant, TButtonSizes } from "./helper";
|
||||
import { cn } from "../../helpers";
|
||||
|
||||
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: TButtonVariant;
|
||||
|
|
@ -31,7 +32,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) =>
|
|||
const buttonIconStyle = getIconStyling(size);
|
||||
|
||||
return (
|
||||
<button ref={ref} type={type} className={`${buttonStyle} ${className}`} disabled={disabled || loading} {...rest}>
|
||||
<button ref={ref} type={type} className={cn(buttonStyle, className)} disabled={disabled || loading} {...rest}>
|
||||
{prependIcon && <div className={buttonIconStyle}>{React.cloneElement(prependIcon, { strokeWidth: 2 })}</div>}
|
||||
{children}
|
||||
{appendIcon && <div className={buttonIconStyle}>{React.cloneElement(appendIcon, { strokeWidth: 2 })}</div>}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ export interface IButtonStyling {
|
|||
}
|
||||
|
||||
enum buttonSizeStyling {
|
||||
sm = `px-3 py-1.5 font-medium text-xs rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center inline`,
|
||||
md = `px-4 py-1.5 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center inline`,
|
||||
lg = `px-5 py-2 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center inline`,
|
||||
xl = `px-5 py-3.5 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center inline`,
|
||||
sm = `px-3 py-1.5 font-medium text-xs rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
md = `px-4 py-1.5 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
lg = `px-5 py-2 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
xl = `px-5 py-3.5 font-medium text-sm rounded flex items-center gap-1.5 whitespace-nowrap transition-all justify-center`,
|
||||
}
|
||||
|
||||
enum buttonIconStyling {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export * from "./button";
|
||||
export * from "./helper";
|
||||
export * from "./toggle-switch";
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { Menu } from "@headlessui/react";
|
|||
import { ICustomMenuDropdownProps, ICustomMenuItemProps } from "./helper";
|
||||
// icons
|
||||
import { ChevronDown, MoreHorizontal } from "lucide-react";
|
||||
import { cn } from "../../helpers";
|
||||
|
||||
const CustomMenu = (props: ICustomMenuDropdownProps) => {
|
||||
const {
|
||||
|
|
@ -62,7 +63,7 @@ const CustomMenu = (props: ICustomMenuDropdownProps) => {
|
|||
static
|
||||
>
|
||||
<div
|
||||
className={`my-1 overflow-y-scroll whitespace-nowrap rounded-md border border-custom-border-300 bg-custom-background-90 p-1 text-xs shadow-custom-shadow-rg focus:outline-none ${
|
||||
className={`my-1 overflow-y-scroll rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 text-xs shadow-custom-shadow-rg focus:outline-none ${
|
||||
maxHeight === "lg"
|
||||
? "max-h-60"
|
||||
: maxHeight === "md"
|
||||
|
|
@ -72,7 +73,7 @@ const CustomMenu = (props: ICustomMenuDropdownProps) => {
|
|||
: maxHeight === "sm"
|
||||
? "max-h-28"
|
||||
: ""
|
||||
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
||||
} ${width === "auto" ? "min-w-[12rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
|
|
@ -167,9 +168,13 @@ const MenuItem: React.FC<ICustomMenuItemProps> = (props) => {
|
|||
{({ active, close }) => (
|
||||
<button
|
||||
type="button"
|
||||
className={`w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80 ${
|
||||
active ? "bg-custom-background-80" : ""
|
||||
} ${className}`}
|
||||
className={cn(
|
||||
"w-full select-none truncate rounded px-1 py-1.5 text-left text-custom-text-200",
|
||||
{
|
||||
"bg-custom-background-80": active,
|
||||
},
|
||||
className
|
||||
)}
|
||||
onClick={(e) => {
|
||||
close();
|
||||
onClick && onClick(e);
|
||||
|
|
|
|||
|
|
@ -1,35 +1,79 @@
|
|||
import * as React from "react";
|
||||
import { AlertCircle, Ban, SignalHigh, SignalLow, SignalMedium } from "lucide-react";
|
||||
import { cn } from "../../helpers";
|
||||
|
||||
type TIssuePriorities = "urgent" | "high" | "medium" | "low" | "none";
|
||||
|
||||
interface IPriorityIcon {
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
priority: TIssuePriorities;
|
||||
size?: number;
|
||||
withContainer?: boolean;
|
||||
}
|
||||
|
||||
export const PriorityIcon: React.FC<IPriorityIcon> = (props) => {
|
||||
const { priority, className = "", size = 14 } = props;
|
||||
const { priority, className = "", containerClassName = "", size = 14, withContainer = false } = props;
|
||||
|
||||
// Convert to lowercase for string comparison
|
||||
const lowercasePriority = priority?.toLowerCase();
|
||||
|
||||
//get priority icon
|
||||
const getPriorityIcon = (): React.ReactNode => {
|
||||
switch (lowercasePriority) {
|
||||
case "urgent":
|
||||
return <AlertCircle size={size} className={`text-red-500 ${className}`} />;
|
||||
case "high":
|
||||
return <SignalHigh size={size} strokeWidth={3} className={`text-orange-500 ${className}`} />;
|
||||
case "medium":
|
||||
return <SignalMedium size={size} strokeWidth={3} className={`text-yellow-500 ${className}`} />;
|
||||
case "low":
|
||||
return <SignalLow size={size} strokeWidth={3} className={`text-custom-primary-100 ${className}`} />;
|
||||
default:
|
||||
return <Ban size={size} className={`text-custom-text-200 ${className}`} />;
|
||||
}
|
||||
const priorityClasses = {
|
||||
urgent: "bg-red-500 text-red-500 border-red-500",
|
||||
high: "bg-orange-500/20 text-orange-500 border-orange-500",
|
||||
medium: "bg-yellow-500/20 text-yellow-500 border-yellow-500",
|
||||
low: "bg-custom-primary-100/20 text-custom-primary-100 border-custom-primary-100",
|
||||
none: "bg-custom-background-80 text-custom-text-200 border-custom-border-300",
|
||||
};
|
||||
|
||||
return <>{getPriorityIcon()}</>;
|
||||
// get priority icon
|
||||
const icons = {
|
||||
urgent: AlertCircle,
|
||||
high: SignalHigh,
|
||||
medium: SignalMedium,
|
||||
low: SignalLow,
|
||||
none: Ban,
|
||||
};
|
||||
const Icon = icons[priority];
|
||||
|
||||
if (!Icon) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{withContainer ? (
|
||||
<div
|
||||
className={cn(
|
||||
"grid place-items-center border rounded p-0.5 flex-shrink-0",
|
||||
priorityClasses[priority],
|
||||
containerClassName
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
size={size}
|
||||
className={cn(
|
||||
{
|
||||
"text-white": priority === "urgent",
|
||||
// centre align the icons
|
||||
"translate-x-[0.0625rem]": priority === "high",
|
||||
"translate-x-0.5": priority === "medium",
|
||||
"translate-x-1": priority === "low",
|
||||
},
|
||||
className
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Icon
|
||||
size={size}
|
||||
className={cn(
|
||||
{
|
||||
"text-red-500": priority === "urgent",
|
||||
"text-orange-500": priority === "high",
|
||||
"text-yellow-500": priority === "medium",
|
||||
"text-custom-primary-100": priority === "low",
|
||||
"text-custom-text-200": priority === "none",
|
||||
},
|
||||
className
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue