[WEB-5459] feat(codemods): add function declaration transformer with tests (#8137)
- Add jscodeshift-based codemod to convert arrow function components to function declarations - Support React.FC, observer-wrapped, and forwardRef components - Include comprehensive test suite covering edge cases - Add npm script to run transformer across codebase - Target only .tsx files in source directories, excluding node_modules and declaration files * [WEB-5459] chore: updates after running codemod --------- Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
parent
90866fb925
commit
83fdebf64d
1771 changed files with 17003 additions and 13856 deletions
|
|
@ -11,7 +11,7 @@ type TAuthBanner = {
|
|||
handleBannerData?: (bannerData: TAuthErrorInfo | undefined) => void;
|
||||
};
|
||||
|
||||
export const AuthBanner: FC<TAuthBanner> = (props) => {
|
||||
export function AuthBanner(props: TAuthBanner) {
|
||||
const { bannerData, handleBannerData } = props;
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -37,4 +37,4 @@ export const AuthBanner: FC<TAuthBanner> = (props) => {
|
|||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const Titles = {
|
|||
|
||||
const workSpaceService = new WorkspaceService();
|
||||
|
||||
export const AuthHeader: FC<TAuthHeader> = observer((props) => {
|
||||
export const AuthHeader = observer(function AuthHeader(props: TAuthHeader) {
|
||||
const { workspaceSlug, invitationId, invitationEmail, authMode, currentAuthStep } = props;
|
||||
// plane imports
|
||||
const { t } = useTranslation();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ type TAuthRoot = {
|
|||
authMode: EAuthModes;
|
||||
};
|
||||
|
||||
export const AuthRoot: FC<TAuthRoot> = observer((props) => {
|
||||
export const AuthRoot = observer(function AuthRoot(props: TAuthRoot) {
|
||||
//router
|
||||
const searchParams = useSearchParams();
|
||||
// query params
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
"use client";
|
||||
|
||||
export const FormContainer = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
|
||||
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">{children}</div>
|
||||
</div>
|
||||
);
|
||||
export function FormContainer({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
|
||||
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
"use client";
|
||||
|
||||
export const AuthFormHeader = ({ title, description }: { title: string; description: string }) => (
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-2xl font-semibold text-custom-text-100">{title}</span>
|
||||
<span className="text-2xl font-semibold text-custom-text-400">{description}</span>
|
||||
</div>
|
||||
);
|
||||
export function AuthFormHeader({ title, description }: { title: string; description: string }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-2xl font-semibold text-custom-text-100">{title}</span>
|
||||
<span className="text-2xl font-semibold text-custom-text-400">{description}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type TAuthEmailForm = {
|
|||
onSubmit: (data: IEmailCheckData) => Promise<void>;
|
||||
};
|
||||
|
||||
export const AuthEmailForm: FC<TAuthEmailForm> = observer((props) => {
|
||||
export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailForm) {
|
||||
const { onSubmit, defaultEmail } = props;
|
||||
// states
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Popover } from "@headlessui/react";
|
|||
import { useTranslation } from "@plane/i18n";
|
||||
import { CloseIcon } from "@plane/propel/icons";
|
||||
|
||||
export const ForgotPasswordPopover = () => {
|
||||
export function ForgotPasswordPopover() {
|
||||
// popper-js refs
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
|
|
@ -58,4 +58,4 @@ export const ForgotPasswordPopover = () => {
|
|||
</Popover.Panel>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const defaultValues: TForgotPasswordFormValues = {
|
|||
// services
|
||||
const authService = new AuthService();
|
||||
|
||||
export const ForgotPasswordForm = observer(() => {
|
||||
export const ForgotPasswordForm = observer(function ForgotPasswordForm() {
|
||||
// search params
|
||||
const searchParams = useSearchParams();
|
||||
const email = searchParams.get("email");
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ type TAuthFormRoot = {
|
|||
|
||||
const authService = new AuthService();
|
||||
|
||||
export const AuthFormRoot = observer((props: TAuthFormRoot) => {
|
||||
export const AuthFormRoot = observer(function AuthFormRoot(props: TAuthFormRoot) {
|
||||
const { authStep, authMode, email, setEmail, setAuthMode, setAuthStep, setErrorInfo, currentAuthMode } = props;
|
||||
// router
|
||||
const router = useAppRouter();
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const defaultValues: TPasswordFormValues = {
|
|||
|
||||
const authService = new AuthService();
|
||||
|
||||
export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||
export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props) {
|
||||
const { email, isSMTPConfigured, handleAuthStep, handleEmailClear, mode, nextPath } = props;
|
||||
// plane imports
|
||||
const { t } = useTranslation();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const defaultValues: TResetPasswordFormValues = {
|
|||
// services
|
||||
const authService = new AuthService();
|
||||
|
||||
export const ResetPasswordForm = observer(() => {
|
||||
export const ResetPasswordForm = observer(function ResetPasswordForm() {
|
||||
// search params
|
||||
const searchParams = useSearchParams();
|
||||
const uidb64 = searchParams.get("uidb64");
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const defaultValues: TResetPasswordFormValues = {
|
|||
// services
|
||||
const authService = new AuthService();
|
||||
|
||||
export const SetPasswordForm = observer(() => {
|
||||
export const SetPasswordForm = observer(function SetPasswordForm() {
|
||||
// router
|
||||
const router = useAppRouter();
|
||||
// search params
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const defaultValues: TUniqueCodeFormValues = {
|
|||
code: "",
|
||||
};
|
||||
|
||||
export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
|
||||
export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) {
|
||||
const { mode, email, handleEmailClear, generateEmailUniqueCode, isExistingEmail, nextPath } = props;
|
||||
// derived values
|
||||
const defaultResetTimerValue = 5;
|
||||
|
|
@ -205,4 +205,4 @@ export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
|
|||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type Props = {
|
|||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const DeactivateAccountModal: React.FC<Props> = (props) => {
|
||||
export function DeactivateAccountModal(props: Props) {
|
||||
const router = useAppRouter();
|
||||
const { isOpen, onClose } = props;
|
||||
// hooks
|
||||
|
|
@ -125,4 +125,4 @@ export const DeactivateAccountModal: React.FC<Props> = (props) => {
|
|||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,18 +18,22 @@ const MESSAGES = {
|
|||
} as const;
|
||||
|
||||
// Reusable link component to reduce duplication
|
||||
const LegalLink: React.FC<{ href: string; children: React.ReactNode }> = ({ href, children }) => (
|
||||
<Link href={href} className="text-custom-text-200" target="_blank" rel="noopener noreferrer">
|
||||
<span className="text-sm font-medium underline hover:cursor-pointer">{children}</span>
|
||||
</Link>
|
||||
);
|
||||
function LegalLink({ href, children }: { href: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<Link href={href} className="text-custom-text-200" target="_blank" rel="noopener noreferrer">
|
||||
<span className="text-sm font-medium underline hover:cursor-pointer">{children}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export const TermsAndConditions: React.FC<TermsAndConditionsProps> = ({ authType = EAuthModes.SIGN_IN }) => (
|
||||
<div className="flex items-center justify-center">
|
||||
<p className="text-center text-sm text-custom-text-300 whitespace-pre-line">
|
||||
{`${MESSAGES[authType]}, you understand and agree to \n our `}
|
||||
<LegalLink href={LEGAL_LINKS.termsOfService}>Terms of Service</LegalLink> and{" "}
|
||||
<LegalLink href={LEGAL_LINKS.privacyPolicy}>Privacy Policy</LegalLink>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
export function TermsAndConditions({ authType = EAuthModes.SIGN_IN }: TermsAndConditionsProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<p className="text-center text-sm text-custom-text-300 whitespace-pre-line">
|
||||
{`${MESSAGES[authType]}, you understand and agree to \n our `}
|
||||
<LegalLink href={LEGAL_LINKS.termsOfService}>Terms of Service</LegalLink> and{" "}
|
||||
<LegalLink href={LEGAL_LINKS.privacyPolicy}>Privacy Policy</LegalLink>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useProject } from "@/hooks/store/use-project";
|
|||
import DurationDropdown from "./select/duration";
|
||||
import { ProjectSelect } from "./select/project";
|
||||
|
||||
const AnalyticsFilterActions = observer(() => {
|
||||
const AnalyticsFilterActions = observer(function AnalyticsFilterActions() {
|
||||
const { selectedProjects, selectedDuration, updateSelectedProjects, updateSelectedDuration } = useAnalytics();
|
||||
const { joinedProjectIds } = useProject();
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ type Props = {
|
|||
headerClassName?: string;
|
||||
};
|
||||
|
||||
const AnalyticsSectionWrapper: React.FC<Props> = (props) => {
|
||||
function AnalyticsSectionWrapper(props: Props) {
|
||||
const { title, children, className, subtitle, actions, headerClassName } = props;
|
||||
return (
|
||||
<div className={className}>
|
||||
|
|
@ -25,6 +25,6 @@ const AnalyticsSectionWrapper: React.FC<Props> = (props) => {
|
|||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default AnalyticsSectionWrapper;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ type Props = {
|
|||
className?: string;
|
||||
};
|
||||
|
||||
const AnalyticsWrapper: React.FC<Props> = (props) => {
|
||||
function AnalyticsWrapper(props: Props) {
|
||||
const { i18nTitle, children, className } = props;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
|
|
@ -18,6 +18,6 @@ const AnalyticsWrapper: React.FC<Props> = (props) => {
|
|||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default AnalyticsWrapper;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ type Props = {
|
|||
className?: string;
|
||||
};
|
||||
|
||||
const AnalyticsEmptyState = ({ title, description, assetPath, className }: Props) => {
|
||||
function AnalyticsEmptyState({ title, description, assetPath, className }: Props) {
|
||||
// theme hook
|
||||
const { resolvedTheme } = useTheme();
|
||||
const backgroundReolvedPath = resolvedTheme === "light" ? lightBackgroundAsset : darkBackgroundAsset;
|
||||
|
|
@ -40,5 +40,6 @@ const AnalyticsEmptyState = ({ title, description, assetPath, className }: Props
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default AnalyticsEmptyState;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export type InsightCardProps = {
|
|||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
const InsightCard = (props: InsightCardProps) => {
|
||||
function InsightCard(props: InsightCardProps) {
|
||||
const { data, label, isLoading = false } = props;
|
||||
const count = data?.count ?? 0;
|
||||
|
||||
|
|
@ -25,6 +25,6 @@ const InsightCard = (props: InsightCardProps) => {
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default InsightCard;
|
||||
|
|
|
|||
|
|
@ -8,27 +8,29 @@ interface TableSkeletonProps {
|
|||
rows: number;
|
||||
}
|
||||
|
||||
export const TableLoader: React.FC<TableSkeletonProps> = ({ columns, rows }) => (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{columns.map((column, index) => (
|
||||
<TableHead key={column.header?.toString() ?? index}>
|
||||
{typeof column.header === "string" ? column.header : ""}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{Array.from({ length: rows }).map((_, rowIndex) => (
|
||||
<TableRow key={rowIndex}>
|
||||
{columns.map((_, colIndex) => (
|
||||
<TableCell key={colIndex}>
|
||||
<Loader.Item height="20px" width="100%" />
|
||||
</TableCell>
|
||||
export function TableLoader({ columns, rows }: TableSkeletonProps) {
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{columns.map((column, index) => (
|
||||
<TableHead key={column.header?.toString() ?? index}>
|
||||
{typeof column.header === "string" ? column.header : ""}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{Array.from({ length: rows }).map((_, rowIndex) => (
|
||||
<TableRow key={rowIndex}>
|
||||
{columns.map((_, colIndex) => (
|
||||
<TableCell key={colIndex}>
|
||||
<Loader.Item height="20px" width="100%" />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ interface InsightTableProps<T extends Exclude<TAnalyticsTabsBase, "overview">> {
|
|||
onExport?: (rows: Row<AnalyticsTableDataMap[T]>[]) => void;
|
||||
}
|
||||
|
||||
export const InsightTable = <T extends Exclude<TAnalyticsTabsBase, "overview">>(
|
||||
export function InsightTable<T extends Exclude<TAnalyticsTabsBase, "overview">>(
|
||||
props: InsightTableProps<T>
|
||||
): React.ReactElement => {
|
||||
): React.ReactElement {
|
||||
const { data, isLoading, columns, headerText, onExport } = props;
|
||||
const { t } = useTranslation();
|
||||
if (isLoading) {
|
||||
|
|
@ -42,4 +42,4 @@ export const InsightTable = <T extends Exclude<TAnalyticsTabsBase, "overview">>(
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,27 @@
|
|||
import { Loader } from "@plane/ui";
|
||||
|
||||
export const ProjectInsightsLoader = () => (
|
||||
<div className="flex h-[200px] gap-1">
|
||||
<Loader className="h-full w-full">
|
||||
<Loader.Item height="100%" width="100%" />
|
||||
</Loader>
|
||||
<div className="flex h-full w-full flex-col gap-1">
|
||||
<Loader className="h-12 w-full">
|
||||
<Loader.Item height="100%" width="100%" />
|
||||
</Loader>
|
||||
export function ProjectInsightsLoader() {
|
||||
return (
|
||||
<div className="flex h-[200px] gap-1">
|
||||
<Loader className="h-full w-full">
|
||||
<Loader.Item height="100%" width="100%" />
|
||||
</Loader>
|
||||
<div className="flex h-full w-full flex-col gap-1">
|
||||
<Loader className="h-12 w-full">
|
||||
<Loader.Item height="100%" width="100%" />
|
||||
</Loader>
|
||||
<Loader className="h-full w-full">
|
||||
<Loader.Item height="100%" width="100%" />
|
||||
</Loader>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export const ChartLoader = () => (
|
||||
<Loader className="h-[350px] w-full">
|
||||
<Loader.Item height="100%" width="100%" />
|
||||
</Loader>
|
||||
);
|
||||
export function ChartLoader() {
|
||||
return (
|
||||
<Loader className="h-[350px] w-full">
|
||||
<Loader.Item height="100%" width="100%" />
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,16 +13,17 @@ type Props = {
|
|||
};
|
||||
isLoading?: boolean;
|
||||
};
|
||||
const CompletionPercentage = ({ percentage }: { percentage: number }) => {
|
||||
|
||||
function CompletionPercentage({ percentage }: { percentage: number }) {
|
||||
const percentageColor = percentage > 50 ? "bg-green-500/30 text-green-500" : "bg-red-500/30 text-red-500";
|
||||
return (
|
||||
<div className={cn("flex items-center gap-2 rounded p-1 text-xs", percentageColor)}>
|
||||
<span>{percentage}%</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const ActiveProjectItem = (props: Props) => {
|
||||
function ActiveProjectItem(props: Props) {
|
||||
const { project } = props;
|
||||
const { getProjectById } = useProject();
|
||||
const { id, completed_issues, total_issues } = project;
|
||||
|
|
@ -52,6 +53,6 @@ const ActiveProjectItem = (props: Props) => {
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default ActiveProjectItem;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { useProject } from "@/hooks/store/use-project";
|
|||
import AnalyticsSectionWrapper from "../analytics-section-wrapper";
|
||||
import ActiveProjectItem from "./active-project-item";
|
||||
|
||||
const ActiveProjects = observer(() => {
|
||||
const ActiveProjects = observer(function ActiveProjects() {
|
||||
const { t } = useTranslation();
|
||||
const { fetchProjectAnalyticsCount } = useProject();
|
||||
const { workspaceSlug } = useParams();
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@ import { AnalyticsService } from "@/services/analytics.service";
|
|||
import AnalyticsSectionWrapper from "../analytics-section-wrapper";
|
||||
import { ProjectInsightsLoader } from "../loaders";
|
||||
|
||||
const RadarChart = lazy(() =>
|
||||
import("@plane/propel/charts/radar-chart").then((mod) => ({
|
||||
const RadarChart = lazy(function RadarChart() {
|
||||
return import("@plane/propel/charts/radar-chart").then((mod) => ({
|
||||
default: mod.RadarChart,
|
||||
}))
|
||||
);
|
||||
}));
|
||||
});
|
||||
|
||||
const analyticsService = new AnalyticsService();
|
||||
|
||||
const ProjectInsights = observer(() => {
|
||||
const ProjectInsights = observer(function ProjectInsights() {
|
||||
const params = useParams();
|
||||
const { t } = useTranslation();
|
||||
const workspaceSlug = params.workspaceSlug.toString();
|
||||
|
|
|
|||
|
|
@ -4,16 +4,18 @@ import TotalInsights from "../total-insights";
|
|||
import ActiveProjects from "./active-projects";
|
||||
import ProjectInsights from "./project-insights";
|
||||
|
||||
const Overview: React.FC = () => (
|
||||
<AnalyticsWrapper i18nTitle="common.overview">
|
||||
<div className="flex flex-col gap-14">
|
||||
<TotalInsights analyticsType="overview" />
|
||||
<div className="grid grid-cols-1 gap-14 md:grid-cols-5 ">
|
||||
<ProjectInsights />
|
||||
<ActiveProjects />
|
||||
function Overview() {
|
||||
return (
|
||||
<AnalyticsWrapper i18nTitle="common.overview">
|
||||
<div className="flex flex-col gap-14">
|
||||
<TotalInsights analyticsType="overview" />
|
||||
<div className="grid grid-cols-1 gap-14 md:grid-cols-5 ">
|
||||
<ProjectInsights />
|
||||
<ActiveProjects />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AnalyticsWrapper>
|
||||
);
|
||||
</AnalyticsWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export { Overview };
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type Props = {
|
|||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const AnalyticsSelectParams: React.FC<Props> = observer((props) => {
|
||||
export const AnalyticsSelectParams = observer(function AnalyticsSelectParams(props: Props) {
|
||||
const { control, params, classNames, isEpic } = props;
|
||||
const xAxisOptions = useMemo(
|
||||
() => ANALYTICS_X_AXIS_VALUES.filter((option) => option.value !== params.group_by),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type Props = {
|
|||
projectIds: string[] | undefined;
|
||||
};
|
||||
|
||||
export const ProjectSelect: React.FC<Props> = observer((props) => {
|
||||
export const ProjectSelect = observer(function ProjectSelect(props: Props) {
|
||||
const { value, onChange, projectIds } = props;
|
||||
const { getProjectById } = useProject();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ type Props = {
|
|||
label?: string | React.ReactNode;
|
||||
};
|
||||
|
||||
export const SelectXAxis: React.FC<Props> = (props) => {
|
||||
export function SelectXAxis(props: Props) {
|
||||
const { value, onChange, options, hiddenOptions, allowNoValue, label } = props;
|
||||
return (
|
||||
<CustomSelect value={value} label={label} onChange={onChange} maxHeight="lg">
|
||||
|
|
@ -28,4 +28,4 @@ export const SelectXAxis: React.FC<Props> = (props) => {
|
|||
})}
|
||||
</CustomSelect>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type Props = {
|
|||
options: { value: ChartYAxisMetric; label: string }[];
|
||||
};
|
||||
|
||||
export const SelectYAxis: React.FC<Props> = observer(({ value, onChange, hiddenOptions, options }) => {
|
||||
export const SelectYAxis = observer(function SelectYAxis({ value, onChange, hiddenOptions, options }: Props) {
|
||||
// hooks
|
||||
const { projectId } = useParams();
|
||||
const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates();
|
||||
|
|
|
|||
|
|
@ -44,10 +44,13 @@ const getInsightLabel = (
|
|||
return `${prefix}${baseTranslation}${suffix}`;
|
||||
};
|
||||
|
||||
const TotalInsights: React.FC<{
|
||||
const TotalInsights = observer(function TotalInsights({
|
||||
analyticsType,
|
||||
peekView,
|
||||
}: {
|
||||
analyticsType: TAnalyticsTabsBase;
|
||||
peekView?: boolean;
|
||||
}> = observer(({ analyticsType, peekView }) => {
|
||||
}) {
|
||||
const params = useParams();
|
||||
const workspaceSlug = params.workspaceSlug.toString();
|
||||
const { t } = useTranslation();
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const variants: Record<NonNullable<Props["variant"]>, Record<"ontrack" | "offtra
|
|||
},
|
||||
} as const;
|
||||
|
||||
const TrendPiece = (props: Props) => {
|
||||
function TrendPiece(props: Props) {
|
||||
const { percentage, className, trendIconVisible = true, size = "sm", variant = "simple" } = props;
|
||||
const isOnTrack = percentage >= 66;
|
||||
const isOffTrack = percentage >= 33 && percentage < 66;
|
||||
|
|
@ -75,6 +75,6 @@ const TrendPiece = (props: Props) => {
|
|||
{Math.round(Math.abs(percentage))}%
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default TrendPiece;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import AnalyticsSectionWrapper from "../analytics-section-wrapper";
|
|||
import { ChartLoader } from "../loaders";
|
||||
|
||||
const analyticsService = new AnalyticsService();
|
||||
const CreatedVsResolved = observer(() => {
|
||||
const CreatedVsResolved = observer(function CreatedVsResolved() {
|
||||
const {
|
||||
selectedDuration,
|
||||
selectedDurationLabel,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,13 @@ import AnalyticsSectionWrapper from "../analytics-section-wrapper";
|
|||
import { AnalyticsSelectParams } from "../select/analytics-params";
|
||||
import PriorityChart from "./priority-chart";
|
||||
|
||||
const CustomizedInsights = observer(({ peekView, isEpic }: { peekView?: boolean; isEpic?: boolean }) => {
|
||||
const CustomizedInsights = observer(function CustomizedInsights({
|
||||
peekView,
|
||||
isEpic,
|
||||
}: {
|
||||
peekView?: boolean;
|
||||
isEpic?: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { workspaceSlug } = useParams();
|
||||
const { control, watch, setValue } = useForm<IAnalyticsParams>({
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type Props = {
|
|||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const WorkItemsModalMainContent: React.FC<Props> = observer((props) => {
|
||||
export const WorkItemsModalMainContent = observer(function WorkItemsModalMainContent(props: Props) {
|
||||
const { projectDetails, cycleDetails, moduleDetails, fullScreen, isEpic } = props;
|
||||
const { updateSelectedProjects, updateSelectedCycle, updateSelectedModule, updateIsPeekView } = useAnalytics();
|
||||
const [isModalConfigured, setIsModalConfigured] = useState(false);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type Props = {
|
|||
module?: IModule;
|
||||
};
|
||||
|
||||
export const WorkItemsModalHeader: React.FC<Props> = observer((props) => {
|
||||
export const WorkItemsModalHeader = observer(function WorkItemsModalHeader(props: Props) {
|
||||
const { fullScreen, handleClose, setFullScreen, title, cycle, module } = props;
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type Props = {
|
|||
isEpic?: boolean;
|
||||
};
|
||||
|
||||
export const WorkItemsModal: React.FC<Props> = observer((props) => {
|
||||
export const WorkItemsModal = observer(function WorkItemsModal(props: Props) {
|
||||
const { isOpen, onClose, projectDetails, moduleDetails, cycleDetails, isEpic } = props;
|
||||
const { updateIsEpic, isPeekView } = useAnalytics();
|
||||
const [fullScreen, setFullScreen] = useState(false);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ interface Props {
|
|||
}
|
||||
|
||||
const analyticsService = new AnalyticsService();
|
||||
const PriorityChart = observer((props: Props) => {
|
||||
const PriorityChart = observer(function PriorityChart(props: Props) {
|
||||
const { x_axis, y_axis, group_by } = props;
|
||||
const { t } = useTranslation();
|
||||
// store hooks
|
||||
|
|
|
|||
|
|
@ -5,15 +5,17 @@ import CreatedVsResolved from "./created-vs-resolved";
|
|||
import CustomizedInsights from "./customized-insights";
|
||||
import WorkItemsInsightTable from "./workitems-insight-table";
|
||||
|
||||
const WorkItems: React.FC = () => (
|
||||
<AnalyticsWrapper i18nTitle="sidebar.work_items">
|
||||
<div className="flex flex-col gap-14">
|
||||
<TotalInsights analyticsType="work-items" />
|
||||
<CreatedVsResolved />
|
||||
<CustomizedInsights />
|
||||
<WorkItemsInsightTable />
|
||||
</div>
|
||||
</AnalyticsWrapper>
|
||||
);
|
||||
function WorkItems() {
|
||||
return (
|
||||
<AnalyticsWrapper i18nTitle="sidebar.work_items">
|
||||
<div className="flex flex-col gap-14">
|
||||
<TotalInsights analyticsType="work-items" />
|
||||
<CreatedVsResolved />
|
||||
<CustomizedInsights />
|
||||
<WorkItemsInsightTable />
|
||||
</div>
|
||||
</AnalyticsWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export { WorkItems };
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ declare module "@tanstack/react-table" {
|
|||
}
|
||||
}
|
||||
|
||||
const WorkItemsInsightTable = observer(() => {
|
||||
const WorkItemsInsightTable = observer(function WorkItemsInsightTable() {
|
||||
// router
|
||||
const params = useParams();
|
||||
const workspaceSlug = params.workspaceSlug.toString();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ type Props = {
|
|||
|
||||
const apiTokenService = new APITokenService();
|
||||
|
||||
export const DeleteApiTokenModal: FC<Props> = (props) => {
|
||||
export function DeleteApiTokenModal(props: Props) {
|
||||
const { isOpen, onClose, tokenId } = props;
|
||||
// states
|
||||
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
|
||||
|
|
@ -90,4 +90,4 @@ export const DeleteApiTokenModal: FC<Props> = (props) => {
|
|||
content={<>{t("workspace_settings.settings.api_tokens.delete.description")} </>}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ type Props = {
|
|||
onClick: () => void;
|
||||
};
|
||||
|
||||
export const ApiTokenEmptyState: React.FC<Props> = (props) => {
|
||||
export function ApiTokenEmptyState(props: Props) {
|
||||
const { onClick } = props;
|
||||
|
||||
return (
|
||||
|
|
@ -29,4 +29,4 @@ export const ApiTokenEmptyState: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type Props = {
|
|||
// services
|
||||
const apiTokenService = new APITokenService();
|
||||
|
||||
export const CreateApiTokenModal: React.FC<Props> = (props) => {
|
||||
export function CreateApiTokenModal(props: Props) {
|
||||
const { isOpen, onClose } = props;
|
||||
// states
|
||||
const [neverExpires, setNeverExpires] = useState<boolean>(false);
|
||||
|
|
@ -104,4 +104,4 @@ export const CreateApiTokenModal: React.FC<Props> = (props) => {
|
|||
)}
|
||||
</ModalCore>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ const getFormattedDate = (date: Date): Date => {
|
|||
return add(date, { hours, minutes, seconds });
|
||||
};
|
||||
|
||||
export const CreateApiTokenForm: React.FC<Props> = (props) => {
|
||||
export function CreateApiTokenForm(props: Props) {
|
||||
const { handleClose, neverExpires, toggleNeverExpires, onSubmit } = props;
|
||||
// states
|
||||
const [customDate, setCustomDate] = useState<Date | null>(null);
|
||||
|
|
@ -253,4 +253,4 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type Props = {
|
|||
tokenDetails: IApiToken;
|
||||
};
|
||||
|
||||
export const GeneratedTokenDetails: React.FC<Props> = (props) => {
|
||||
export function GeneratedTokenDetails(props: Props) {
|
||||
const { handleClose, tokenDetails } = props;
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -60,4 +60,4 @@ export const GeneratedTokenDetails: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ type Props = {
|
|||
token: IApiToken;
|
||||
};
|
||||
|
||||
export const ApiTokenListItem: React.FC<Props> = (props) => {
|
||||
export function ApiTokenListItem(props: Props) {
|
||||
const { token } = props;
|
||||
// states
|
||||
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
||||
|
|
@ -61,4 +61,4 @@ export const ApiTokenListItem: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const ARCHIVES_TAB_LIST: {
|
|||
},
|
||||
];
|
||||
|
||||
export const ArchiveTabsList: FC = observer(() => {
|
||||
export const ArchiveTabsList = observer(function ArchiveTabsList() {
|
||||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
const pathname = usePathname();
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ type AuthBaseProps = {
|
|||
authType: EAuthModes;
|
||||
};
|
||||
|
||||
export const AuthBase = ({ authType }: AuthBaseProps) => (
|
||||
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
|
||||
<AuthHeader type={authType} />
|
||||
<AuthRoot authMode={authType} />
|
||||
<AuthFooter />
|
||||
</div>
|
||||
);
|
||||
export function AuthBase({ authType }: AuthBaseProps) {
|
||||
return (
|
||||
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
|
||||
<AuthHeader type={authType} />
|
||||
<AuthRoot authMode={authType} />
|
||||
<AuthFooter />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,15 +24,17 @@ const BRAND_LOGOS: {
|
|||
},
|
||||
];
|
||||
|
||||
export const AuthFooter = () => (
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<span className="text-sm text-custom-text-300 whitespace-nowrap">Join 10,000+ teams building with Plane</span>
|
||||
<div className="flex items-center justify-center gap-x-10 gap-y-4 w-full flex-wrap">
|
||||
{BRAND_LOGOS.map((brand) => (
|
||||
<div className="flex items-center justify-center h-7 flex-1" key={brand.id}>
|
||||
{brand.icon}
|
||||
</div>
|
||||
))}
|
||||
export function AuthFooter() {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<span className="text-sm text-custom-text-300 whitespace-nowrap">Join 10,000+ teams building with Plane</span>
|
||||
<div className="flex items-center justify-center gap-x-10 gap-y-4 w-full flex-wrap">
|
||||
{BRAND_LOGOS.map((brand) => (
|
||||
<div className="flex items-center justify-center h-7 flex-1" key={brand.id}>
|
||||
{brand.icon}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ type AuthHeaderProps = {
|
|||
type: EAuthModes;
|
||||
};
|
||||
|
||||
export const AuthHeader = observer(({ type }: AuthHeaderProps) => {
|
||||
export const AuthHeader = observer(function AuthHeader({ type }: AuthHeaderProps) {
|
||||
const { t } = useTranslation();
|
||||
// store
|
||||
const { config } = useInstance();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type Props = {
|
|||
className?: string;
|
||||
};
|
||||
|
||||
export const NotAuthorizedView: React.FC<Props> = observer((props) => {
|
||||
export const NotAuthorizedView = observer(function NotAuthorizedView(props: Props) {
|
||||
const { actionButton, section = "general", isProjectView = false, className } = props;
|
||||
|
||||
// assets
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ type Props = {
|
|||
isPrivateProject?: boolean;
|
||||
};
|
||||
|
||||
export const JoinProject: React.FC<Props> = (props) => {
|
||||
export function JoinProject(props: Props) {
|
||||
const { projectId, isPrivateProject = false } = props;
|
||||
// states
|
||||
const [isJoiningProject, setIsJoiningProject] = useState(false);
|
||||
|
|
@ -65,4 +65,4 @@ export const JoinProject: React.FC<Props> = (props) => {
|
|||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,30 +6,32 @@ import { Button } from "@plane/propel/button";
|
|||
// layouts
|
||||
import DefaultLayout from "@/layouts/default-layout";
|
||||
|
||||
export const NotAWorkspaceMember = () => (
|
||||
<DefaultLayout>
|
||||
<div className="grid h-full place-items-center p-4">
|
||||
<div className="space-y-8 text-center">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold">Not Authorized!</h3>
|
||||
<p className="mx-auto w-1/2 text-sm text-custom-text-200">
|
||||
You{"'"}re not a member of this workspace. Please contact the workspace admin to get an invitation or check
|
||||
your pending invitations.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Link href="/invitations">
|
||||
<span>
|
||||
<Button variant="neutral-primary">Check pending invites</Button>
|
||||
</span>
|
||||
</Link>
|
||||
<Link href="/create-workspace">
|
||||
<span>
|
||||
<Button variant="primary">Create new workspace</Button>
|
||||
</span>
|
||||
</Link>
|
||||
export function NotAWorkspaceMember() {
|
||||
return (
|
||||
<DefaultLayout>
|
||||
<div className="grid h-full place-items-center p-4">
|
||||
<div className="space-y-8 text-center">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold">Not Authorized!</h3>
|
||||
<p className="mx-auto w-1/2 text-sm text-custom-text-200">
|
||||
You{"'"}re not a member of this workspace. Please contact the workspace admin to get an invitation or
|
||||
check your pending invitations.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Link href="/invitations">
|
||||
<span>
|
||||
<Button variant="neutral-primary">Check pending invites</Button>
|
||||
</span>
|
||||
</Link>
|
||||
<Link href="/create-workspace">
|
||||
<span>
|
||||
<Button variant="primary">Create new workspace</Button>
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
);
|
||||
</DefaultLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ type Props = {
|
|||
|
||||
const initialValues: Partial<IProject> = { archive_in: 1 };
|
||||
|
||||
export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
|
||||
export const AutoArchiveAutomation = observer(function AutoArchiveAutomation(props: Props) {
|
||||
const { handleChange } = props;
|
||||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ type Props = {
|
|||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||
};
|
||||
|
||||
export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
|
||||
export const AutoCloseAutomation = observer(function AutoCloseAutomation(props: Props) {
|
||||
const { handleChange } = props;
|
||||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type Props = {
|
|||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||
};
|
||||
|
||||
export const SelectMonthModal: React.FC<Props> = ({ type, initialValues, isOpen, handleClose, handleChange }) => {
|
||||
export function SelectMonthModal({ type, initialValues, isOpen, handleClose, handleChange }: Props) {
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
|
||||
const {
|
||||
|
|
@ -165,4 +165,4 @@ export const SelectMonthModal: React.FC<Props> = ({ type, initialValues, isOpen,
|
|||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import { TimeLineTypeContext } from "@/components/gantt-chart/contexts";
|
|||
import { GanttChartRoot } from "@/components/gantt-chart/root";
|
||||
import { BaseGanttSidebar } from "./sidebar";
|
||||
|
||||
export const BaseGanttLayout = observer(<T extends IBaseLayoutsGanttItem>(props: IBaseLayoutsGanttProps<T>) => {
|
||||
export const BaseGanttLayout = observer(function BaseGanttLayout<T extends IBaseLayoutsGanttItem>(
|
||||
props: IBaseLayoutsGanttProps<T>
|
||||
) {
|
||||
const {
|
||||
items,
|
||||
groupedItemIds,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ type Props<T extends IBaseLayoutsBaseItem> = {
|
|||
renderItem: (item: T) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const BaseGanttSidebar = observer(<T extends IBaseLayoutsBaseItem>(props: Props<T>) => {
|
||||
export const BaseGanttSidebar = observer(function BaseGanttSidebar<T extends IBaseLayoutsBaseItem>(props: Props<T>) {
|
||||
const {
|
||||
blockUpdateHandler,
|
||||
blockIds,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
import type { IGroupHeaderProps } from "@plane/types";
|
||||
|
||||
export const GroupHeader = ({ group, itemCount, onToggleGroup }: IGroupHeaderProps) => (
|
||||
<button
|
||||
onClick={() => onToggleGroup(group.id)}
|
||||
className="flex w-full items-center gap-2 text-sm font-medium text-custom-text-200"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{group.icon}
|
||||
<span>{group.name}</span>
|
||||
</div>
|
||||
<span className="text-xs text-custom-text-300">{itemCount}</span>
|
||||
</button>
|
||||
);
|
||||
export function GroupHeader({ group, itemCount, onToggleGroup }: IGroupHeaderProps) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => onToggleGroup(group.id)}
|
||||
className="flex w-full items-center gap-2 text-sm font-medium text-custom-text-200"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{group.icon}
|
||||
<span>{group.name}</span>
|
||||
</div>
|
||||
<span className="text-xs text-custom-text-300">{itemCount}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import { useGroupDropTarget } from "../hooks/use-group-drop-target";
|
|||
import { GroupHeader } from "./group-header";
|
||||
import { BaseKanbanItem } from "./item";
|
||||
|
||||
export const BaseKanbanGroup = observer(<T extends IBaseLayoutsKanbanItem>(props: IBaseLayoutsKanbanGroupProps<T>) => {
|
||||
export const BaseKanbanGroup = observer(function BaseKanbanGroup<T extends IBaseLayoutsKanbanItem>(
|
||||
props: IBaseLayoutsKanbanGroupProps<T>
|
||||
) {
|
||||
const {
|
||||
group,
|
||||
itemIds,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-d
|
|||
import { observer } from "mobx-react";
|
||||
import type { IBaseLayoutsKanbanItem, IBaseLayoutsKanbanItemProps } from "@plane/types";
|
||||
|
||||
export const BaseKanbanItem = observer(<T extends IBaseLayoutsKanbanItem>(props: IBaseLayoutsKanbanItemProps<T>) => {
|
||||
export const BaseKanbanItem = observer(function BaseKanbanItem<T extends IBaseLayoutsKanbanItem>(
|
||||
props: IBaseLayoutsKanbanItemProps<T>
|
||||
) {
|
||||
const { item, groupId, renderItem, enableDragDrop, canDrag } = props;
|
||||
|
||||
const itemRef = useRef<HTMLDivElement | null>(null);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import { cn } from "@plane/utils";
|
|||
import { useLayoutState } from "../hooks/use-layout-state";
|
||||
import { BaseKanbanGroup } from "./group";
|
||||
|
||||
export const BaseKanbanLayout = observer(<T extends IBaseLayoutsKanbanItem>(props: IBaseLayoutsKanbanProps<T>) => {
|
||||
export const BaseKanbanLayout = observer(function BaseKanbanLayout<T extends IBaseLayoutsKanbanItem>(
|
||||
props: IBaseLayoutsKanbanProps<T>
|
||||
) {
|
||||
const {
|
||||
items,
|
||||
groups,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ type Props = {
|
|||
selectedLayout: TBaseLayoutType;
|
||||
};
|
||||
|
||||
export const LayoutSwitcher: React.FC<Props> = (props) => {
|
||||
export function LayoutSwitcher(props: Props) {
|
||||
const { layouts, onChange, selectedLayout } = props;
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
|
|
@ -47,4 +47,4 @@ export const LayoutSwitcher: React.FC<Props> = (props) => {
|
|||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import type { IGroupHeaderProps } from "@plane/types";
|
||||
|
||||
export const GroupHeader = ({ group, itemCount, onToggleGroup }: IGroupHeaderProps) => (
|
||||
<button
|
||||
onClick={() => onToggleGroup(group.id)}
|
||||
className="flex w-full items-center gap-2 py-2 text-sm font-medium text-custom-text-200"
|
||||
>
|
||||
{group.icon}
|
||||
<span>{group.name}</span>
|
||||
<span className="text-xs text-custom-text-300">{itemCount}</span>
|
||||
</button>
|
||||
);
|
||||
export function GroupHeader({ group, itemCount, onToggleGroup }: IGroupHeaderProps) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => onToggleGroup(group.id)}
|
||||
className="flex w-full items-center gap-2 py-2 text-sm font-medium text-custom-text-200"
|
||||
>
|
||||
{group.icon}
|
||||
<span>{group.name}</span>
|
||||
<span className="text-xs text-custom-text-300">{itemCount}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import { useGroupDropTarget } from "../hooks/use-group-drop-target";
|
|||
import { GroupHeader } from "./group-header";
|
||||
import { BaseListItem } from "./item";
|
||||
|
||||
export const BaseListGroup = observer(<T extends IBaseLayoutsListItem>(props: IBaseLayoutsListGroupProps<T>) => {
|
||||
export const BaseListGroup = observer(function BaseListGroup<T extends IBaseLayoutsListItem>(
|
||||
props: IBaseLayoutsListGroupProps<T>
|
||||
) {
|
||||
const {
|
||||
group,
|
||||
itemIds,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-d
|
|||
import { observer } from "mobx-react";
|
||||
import type { IBaseLayoutsListItem, IBaseLayoutsListItemProps } from "@plane/types";
|
||||
|
||||
export const BaseListItem = observer(<T extends IBaseLayoutsListItem>(props: IBaseLayoutsListItemProps<T>) => {
|
||||
export const BaseListItem = observer(function BaseListItem<T extends IBaseLayoutsListItem>(
|
||||
props: IBaseLayoutsListItemProps<T>
|
||||
) {
|
||||
const { item, groupId, renderItem, enableDragDrop, canDrag, isLast: _isLast, index: _index } = props;
|
||||
const itemRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import { cn } from "@plane/ui";
|
|||
import { useLayoutState } from "../hooks/use-layout-state";
|
||||
import { BaseListGroup } from "./group";
|
||||
|
||||
export const BaseListLayout = observer(<T extends IBaseLayoutsListItem>(props: IBaseLayoutsListProps<T>) => {
|
||||
export const BaseListLayout = observer(function BaseListLayout<T extends IBaseLayoutsListItem>(
|
||||
props: IBaseLayoutsListProps<T>
|
||||
) {
|
||||
const {
|
||||
items,
|
||||
groupedItemIds,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ interface GenericLayoutLoaderProps {
|
|||
customLoaders?: Partial<Record<TBaseLayoutType, React.ComponentType>>;
|
||||
}
|
||||
|
||||
export const GenericLayoutLoader = ({ layout, customLoaders }: GenericLayoutLoaderProps) => {
|
||||
export function GenericLayoutLoader({ layout, customLoaders }: GenericLayoutLoaderProps) {
|
||||
const CustomLoader = customLoaders?.[layout];
|
||||
if (CustomLoader) return <CustomLoader />;
|
||||
|
||||
|
|
@ -21,4 +21,4 @@ export const GenericLayoutLoader = ({ layout, customLoaders }: GenericLayoutLoad
|
|||
console.warn(`Unknown layout: ${layout}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ type Props = {
|
|||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
export const CommentCardDisplay: React.FC<Props> = observer((props) => {
|
||||
export const CommentCardDisplay = observer(function CommentCardDisplay(props: Props) {
|
||||
const {
|
||||
activityOperations,
|
||||
comment,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type Props = {
|
|||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
export const CommentCardEditForm: React.FC<Props> = observer((props) => {
|
||||
export const CommentCardEditForm = observer(function CommentCardEditForm(props: Props) {
|
||||
const {
|
||||
activityOperations,
|
||||
comment,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ type TCommentCard = {
|
|||
projectId?: string;
|
||||
};
|
||||
|
||||
export const CommentCard: FC<TCommentCard> = observer((props) => {
|
||||
export const CommentCard = observer(function CommentCard(props: TCommentCard) {
|
||||
const {
|
||||
workspaceSlug,
|
||||
comment,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ type TCommentCreate = {
|
|||
// services
|
||||
const fileService = new FileService();
|
||||
|
||||
export const CommentCreate: FC<TCommentCreate> = observer((props) => {
|
||||
export const CommentCreate = observer(function CommentCreate(props: TCommentCreate) {
|
||||
const {
|
||||
workspaceSlug,
|
||||
entityId,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export type TProps = {
|
|||
activityOperations: TCommentsOperations;
|
||||
};
|
||||
|
||||
export const CommentReactions: FC<TProps> = observer((props) => {
|
||||
export const CommentReactions = observer(function CommentReactions(props: TProps) {
|
||||
const { comment, activityOperations, disabled = false } = props;
|
||||
// state
|
||||
const [isPickerOpen, setIsPickerOpen] = useState(false);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ type TCommentsWrapper = {
|
|||
showCopyLinkOption?: boolean;
|
||||
};
|
||||
|
||||
export const CommentsWrapper: FC<TCommentsWrapper> = observer((props) => {
|
||||
export const CommentsWrapper = observer(function CommentsWrapper(props: TCommentsWrapper) {
|
||||
const {
|
||||
entityId,
|
||||
activityOperations,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type TCommentCard = {
|
|||
showCopyLinkOption: boolean;
|
||||
};
|
||||
|
||||
export const CommentQuickActions: FC<TCommentCard> = observer((props) => {
|
||||
export const CommentQuickActions = observer(function CommentQuickActions(props: TCommentCard) {
|
||||
const { activityOperations, comment, setEditMode, showAccessSpecifier, showCopyLinkOption } = props;
|
||||
// store hooks
|
||||
const { data: currentUser } = useUser();
|
||||
|
|
@ -33,8 +33,8 @@ export const CommentQuickActions: FC<TCommentCard> = observer((props) => {
|
|||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
const MENU_ITEMS: TContextMenuItem[] = useMemo(
|
||||
() => [
|
||||
const MENU_ITEMS = useMemo(function MENU_ITEMS() {
|
||||
return [
|
||||
{
|
||||
key: "edit",
|
||||
action: setEditMode,
|
||||
|
|
@ -72,9 +72,8 @@ export const CommentQuickActions: FC<TCommentCard> = observer((props) => {
|
|||
icon: Trash2,
|
||||
shouldRender: canDelete,
|
||||
},
|
||||
],
|
||||
[activityOperations, canDelete, canEdit, comment, setEditMode, showAccessSpecifier, showCopyLinkOption]
|
||||
);
|
||||
];
|
||||
});
|
||||
|
||||
return (
|
||||
<CustomMenu ellipsis closeOnSelect>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type Props = {
|
|||
};
|
||||
|
||||
// TODO: Remove label once i18n is done
|
||||
export const AccessField = (props: Props) => {
|
||||
export function AccessField(props: Props) {
|
||||
const { onChange, value, accessSpecifiers, isMobile = false } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
|
@ -50,4 +50,4 @@ export const AccessField = (props: Props) => {
|
|||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type TActivityBlockComponent = {
|
|||
customUserName?: string;
|
||||
};
|
||||
|
||||
export const ActivityBlockComponent: FC<TActivityBlockComponent> = (props) => {
|
||||
export function ActivityBlockComponent(props: TActivityBlockComponent) {
|
||||
const { icon, activity, ends, children, customUserName } = props;
|
||||
// hooks
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
|
@ -53,4 +53,4 @@ export const ActivityBlockComponent: FC<TActivityBlockComponent> = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ type TActivityItem = {
|
|||
ends?: "top" | "bottom" | undefined;
|
||||
};
|
||||
|
||||
export const ActivityItem: FC<TActivityItem> = observer((props) => {
|
||||
export const ActivityItem = observer(function ActivityItem(props: TActivityItem) {
|
||||
const { activity, showProject = true, ends } = props;
|
||||
|
||||
if (!activity) return null;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ type TUser = {
|
|||
customUserName?: string;
|
||||
};
|
||||
|
||||
export const User: FC<TUser> = observer((props) => {
|
||||
export const User = observer(function User(props: TUser) {
|
||||
const { activity, customUserName } = props;
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ type Props = {
|
|||
values: string[];
|
||||
};
|
||||
|
||||
export const AppliedDateFilters: React.FC<Props> = observer((props) => {
|
||||
export const AppliedDateFilters = observer(function AppliedDateFilters(props: Props) {
|
||||
const { editable, handleRemove, values } = props;
|
||||
|
||||
const getDateLabel = (value: string): string => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ type Props = {
|
|||
editable: boolean | undefined;
|
||||
};
|
||||
|
||||
export const AppliedMembersFilters: React.FC<Props> = observer((props) => {
|
||||
export const AppliedMembersFilters = observer(function AppliedMembersFilters(props: Props) {
|
||||
const { handleRemove, values, editable } = props;
|
||||
// store hooks
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -15,19 +15,25 @@ type Props = {
|
|||
isLast?: boolean;
|
||||
};
|
||||
|
||||
const IconWrapper = React.memo(({ icon }: { icon: React.ReactNode }) => (
|
||||
<div className="flex size-4 items-center justify-center overflow-hidden !text-[1rem]">{icon}</div>
|
||||
));
|
||||
const IconWrapper = React.memo(function IconWrapper({ icon }: { icon: React.ReactNode }) {
|
||||
return <div className="flex size-4 items-center justify-center overflow-hidden !text-[1rem]">{icon}</div>;
|
||||
});
|
||||
|
||||
IconWrapper.displayName = "IconWrapper";
|
||||
|
||||
const LabelWrapper = React.memo(({ label }: { label: ReactNode }) => (
|
||||
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
|
||||
));
|
||||
const LabelWrapper = React.memo(function LabelWrapper({ label }: { label: ReactNode }) {
|
||||
return <div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>;
|
||||
});
|
||||
|
||||
LabelWrapper.displayName = "LabelWrapper";
|
||||
|
||||
const BreadcrumbContent = React.memo(({ icon, label }: { icon?: React.ReactNode; label?: ReactNode }) => {
|
||||
const BreadcrumbContent = React.memo(function BreadcrumbContent({
|
||||
icon,
|
||||
label,
|
||||
}: {
|
||||
icon?: React.ReactNode;
|
||||
label?: ReactNode;
|
||||
}) {
|
||||
if (!icon && !label) return null;
|
||||
|
||||
return (
|
||||
|
|
@ -40,13 +46,16 @@ const BreadcrumbContent = React.memo(({ icon, label }: { icon?: React.ReactNode;
|
|||
|
||||
BreadcrumbContent.displayName = "BreadcrumbContent";
|
||||
|
||||
const ItemWrapper = React.memo(({ children, ...props }: React.ComponentProps<typeof Breadcrumbs.ItemWrapper>) => (
|
||||
<Breadcrumbs.ItemWrapper {...props}>{children}</Breadcrumbs.ItemWrapper>
|
||||
));
|
||||
const ItemWrapper = React.memo(function ItemWrapper({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof Breadcrumbs.ItemWrapper>) {
|
||||
return <Breadcrumbs.ItemWrapper {...props}>{children}</Breadcrumbs.ItemWrapper>;
|
||||
});
|
||||
|
||||
ItemWrapper.displayName = "ItemWrapper";
|
||||
|
||||
export const BreadcrumbLink: FC<Props> = observer((props) => {
|
||||
export const BreadcrumbLink = observer(function BreadcrumbLink(props: Props) {
|
||||
const { href, label, icon, disableTooltip = false, isLast = false } = props;
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ type TCountChip = {
|
|||
className?: string;
|
||||
};
|
||||
|
||||
export const CountChip: FC<TCountChip> = (props) => {
|
||||
export function CountChip(props: TCountChip) {
|
||||
const { count, className = "" } = props;
|
||||
|
||||
return (
|
||||
|
|
@ -22,4 +22,4 @@ export const CountChip: FC<TCountChip> = (props) => {
|
|||
{count}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,32 +17,27 @@ type Props = {
|
|||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const EmptyState: React.FC<Props> = ({
|
||||
title,
|
||||
description,
|
||||
image,
|
||||
primaryButton,
|
||||
secondaryButton,
|
||||
disabled = false,
|
||||
}) => (
|
||||
<div className={`flex h-full w-full items-center justify-center`}>
|
||||
<div className="flex w-full flex-col items-center text-center">
|
||||
<img src={image} className="w-52 sm:w-60 object-contain" alt={primaryButton?.text || "button image"} />
|
||||
<h6 className="mb-3 mt-6 text-xl font-semibold sm:mt-8">{title}</h6>
|
||||
{description && <p className="mb-7 px-5 text-custom-text-300 sm:mb-8">{description}</p>}
|
||||
<div className="flex items-center gap-4">
|
||||
{primaryButton && (
|
||||
<Button
|
||||
variant="primary"
|
||||
prependIcon={primaryButton.icon}
|
||||
onClick={primaryButton.onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{primaryButton.text}
|
||||
</Button>
|
||||
)}
|
||||
{secondaryButton}
|
||||
export function EmptyState({ title, description, image, primaryButton, secondaryButton, disabled = false }: Props) {
|
||||
return (
|
||||
<div className={`flex h-full w-full items-center justify-center`}>
|
||||
<div className="flex w-full flex-col items-center text-center">
|
||||
<img src={image} className="w-52 sm:w-60 object-contain" alt={primaryButton?.text || "button image"} />
|
||||
<h6 className="mb-3 mt-6 text-xl font-semibold sm:mt-8">{title}</h6>
|
||||
{description && <p className="mb-7 px-5 text-custom-text-300 sm:mb-8">{description}</p>}
|
||||
<div className="flex items-center gap-4">
|
||||
{primaryButton && (
|
||||
<Button
|
||||
variant="primary"
|
||||
prependIcon={primaryButton.icon}
|
||||
onClick={primaryButton.onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{primaryButton.text}
|
||||
</Button>
|
||||
)}
|
||||
{secondaryButton}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ type Props = {
|
|||
searchQuery: string;
|
||||
};
|
||||
|
||||
export const FilterCreatedDate: React.FC<Props> = observer((props) => {
|
||||
export const FilterCreatedDate = observer(function FilterCreatedDate(props: Props) {
|
||||
const { appliedFilters, handleUpdate, searchQuery } = props;
|
||||
|
||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type Props = {
|
|||
searchQuery: string;
|
||||
};
|
||||
|
||||
export const FilterCreatedBy: React.FC<Props> = observer((props: Props) => {
|
||||
export const FilterCreatedBy = observer(function FilterCreatedBy(props: Props) {
|
||||
const { appliedFilters, handleUpdate, memberIds, searchQuery } = props;
|
||||
// states
|
||||
const [itemsToRender, setItemsToRender] = useState(5);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Lightbulb } from "lucide-react";
|
|||
// images
|
||||
import latestFeatures from "@/app/assets/onboarding/onboarding-pages.webp?url";
|
||||
|
||||
export const LatestFeatureBlock = () => {
|
||||
export function LatestFeatureBlock() {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
return (
|
||||
|
|
@ -36,4 +36,4 @@ export const LatestFeatureBlock = () => {
|
|||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useTheme } from "next-themes";
|
|||
import LogoSpinnerDark from "@/app/assets/images/logo-spinner-dark.gif?url";
|
||||
import LogoSpinnerLight from "@/app/assets/images/logo-spinner-light.gif?url";
|
||||
|
||||
export const LogoSpinner = () => {
|
||||
export function LogoSpinner() {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight;
|
||||
|
|
@ -13,4 +13,4 @@ export const LogoSpinner = () => {
|
|||
<img src={logoSrc} alt="logo" className="h-6 w-auto sm:h-11 object-contain" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,7 @@ type Props = {
|
|||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const NewEmptyState: React.FC<Props> = ({
|
||||
title,
|
||||
description,
|
||||
image,
|
||||
primaryButton,
|
||||
disabled = false,
|
||||
comicBox,
|
||||
}) => {
|
||||
export function NewEmptyState({ title, description, image, primaryButton, disabled = false, comicBox }: Props) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
|
|
@ -109,4 +102,4 @@ export const NewEmptyState: React.FC<Props> = ({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,16 @@ import { ArchiveIcon, Earth, Lock } from "lucide-react";
|
|||
import { EPageAccess } from "@plane/constants";
|
||||
import type { TPage } from "@plane/types";
|
||||
|
||||
export const PageAccessIcon = (page: TPage) => (
|
||||
<div>
|
||||
{page.archived_at ? (
|
||||
<ArchiveIcon className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : page.access === EPageAccess.PUBLIC ? (
|
||||
<Earth className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : (
|
||||
<Lock className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
export function PageAccessIcon(page: TPage) {
|
||||
return (
|
||||
<div>
|
||||
{page.archived_at ? (
|
||||
<ArchiveIcon className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : page.access === EPageAccess.PUBLIC ? (
|
||||
<Earth className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
) : (
|
||||
<Lock className="h-2.5 w-2.5 text-custom-text-300" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ type TProIcon = {
|
|||
className?: string;
|
||||
};
|
||||
|
||||
export const ProIcon: FC<TProIcon> = (props) => {
|
||||
export function ProIcon(props: TProIcon) {
|
||||
const { className } = props;
|
||||
|
||||
return <Crown className={cn("inline-block h-3.5 w-3.5 text-amber-400", className)} />;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,7 @@ type TSwitcherIconProps = {
|
|||
type?: "lucide" | "material";
|
||||
};
|
||||
|
||||
export const SwitcherIcon: FC<TSwitcherIconProps> = ({
|
||||
logo_props,
|
||||
logo_url,
|
||||
LabelIcon,
|
||||
size = 12,
|
||||
type = "lucide",
|
||||
}) => {
|
||||
export function SwitcherIcon({ logo_props, logo_url, LabelIcon, size = 12, type = "lucide" }: TSwitcherIconProps) {
|
||||
if (logo_props?.in_use) {
|
||||
return <Logo logo={logo_props} size={size} type={type} />;
|
||||
}
|
||||
|
|
@ -34,7 +28,7 @@ export const SwitcherIcon: FC<TSwitcherIconProps> = ({
|
|||
);
|
||||
}
|
||||
return <LabelIcon height={size} width={size} />;
|
||||
};
|
||||
}
|
||||
|
||||
type TSwitcherLabelProps = {
|
||||
logo_props?: TLogoProps;
|
||||
|
|
@ -44,7 +38,7 @@ type TSwitcherLabelProps = {
|
|||
type?: "lucide" | "material";
|
||||
};
|
||||
|
||||
export const SwitcherLabel: FC<TSwitcherLabelProps> = (props) => {
|
||||
export function SwitcherLabel(props: TSwitcherLabelProps) {
|
||||
const { logo_props, name, LabelIcon, logo_url, type = "lucide" } = props;
|
||||
return (
|
||||
<div className="flex items-center gap-1 text-custom-text-200">
|
||||
|
|
@ -52,4 +46,4 @@ export const SwitcherLabel: FC<TSwitcherLabelProps> = (props) => {
|
|||
{truncateText(name ?? "", 40)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
|
|
@ -37,7 +36,7 @@ import { useLabel } from "@/hooks/store/use-label";
|
|||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// types
|
||||
|
||||
export const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
|
||||
export function IssueLink({ activity }: { activity: IIssueActivity }) {
|
||||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
|
@ -73,9 +72,9 @@ export const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
|
|||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const UserLink = ({ activity }: { activity: IIssueActivity }) => {
|
||||
function UserLink({ activity }: { activity: IIssueActivity }) {
|
||||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
|
||||
|
|
@ -91,9 +90,9 @@ const UserLink = ({ activity }: { activity: IIssueActivity }) => {
|
|||
{activity.new_value && activity.new_value !== "" ? activity.new_value : activity.old_value}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const LabelPill = observer(({ labelId, workspaceSlug }: { labelId: string; workspaceSlug: string }) => {
|
||||
const LabelPill = observer(function LabelPill({ labelId, workspaceSlug }: { labelId: string; workspaceSlug: string }) {
|
||||
// store hooks
|
||||
const { workspaceLabels, fetchWorkspaceLabels } = useLabel();
|
||||
|
||||
|
|
@ -751,16 +750,16 @@ const activityDetails: {
|
|||
},
|
||||
};
|
||||
|
||||
export const ActivityIcon = ({ activity }: { activity: IIssueActivity }) => (
|
||||
<>{activityDetails[activity.field as keyof typeof activityDetails]?.icon}</>
|
||||
);
|
||||
export function ActivityIcon({ activity }: { activity: IIssueActivity }) {
|
||||
return <>{activityDetails[activity.field as keyof typeof activityDetails]?.icon}</>;
|
||||
}
|
||||
|
||||
type ActivityMessageProps = {
|
||||
activity: IIssueActivity;
|
||||
showIssue?: boolean;
|
||||
};
|
||||
|
||||
export const ActivityMessage = ({ activity, showIssue = false }: ActivityMessageProps) => {
|
||||
export function ActivityMessage({ activity, showIssue = false }: ActivityMessageProps) {
|
||||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
const activityField = activity.field ?? "issue";
|
||||
|
|
@ -774,4 +773,4 @@ export const ActivityMessage = ({ activity, showIssue = false }: ActivityMessage
|
|||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export interface AppHeaderProps {
|
|||
mobileHeader?: ReactNode;
|
||||
}
|
||||
|
||||
export const AppHeader = observer((props: AppHeaderProps) => {
|
||||
export const AppHeader = observer(function AppHeader(props: AppHeaderProps) {
|
||||
const { header, mobileHeader } = props;
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ interface IContentOverflowWrapper {
|
|||
customButton?: ReactNode;
|
||||
}
|
||||
|
||||
export const ContentOverflowWrapper = observer((props: IContentOverflowWrapper) => {
|
||||
export const ContentOverflowWrapper = observer(function ContentOverflowWrapper(props: IContentOverflowWrapper) {
|
||||
const {
|
||||
children,
|
||||
maxHeight = 625,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ export interface ContentWrapperProps {
|
|||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ContentWrapper = ({ className, children }: ContentWrapperProps) => (
|
||||
<div className="h-full w-full overflow-hidden">
|
||||
<div className={cn("relative h-full w-full overflow-x-hidden overflow-y-scroll", className)}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
export function ContentWrapper({ className, children }: ContentWrapperProps) {
|
||||
return (
|
||||
<div className="h-full w-full overflow-hidden">
|
||||
<div className={cn("relative h-full w-full overflow-x-hidden overflow-y-scroll", className)}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ type Props = {
|
|||
version: TDescriptionVersion;
|
||||
};
|
||||
|
||||
export const DescriptionVersionsDropdownItem: React.FC<Props> = observer((props) => {
|
||||
export const DescriptionVersionsDropdownItem = observer(function DescriptionVersionsDropdownItem(props: Props) {
|
||||
const { onClick, version } = props;
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type Props = {
|
|||
versions: TDescriptionVersion[] | undefined;
|
||||
};
|
||||
|
||||
export const DescriptionVersionsDropdown: React.FC<Props> = observer((props) => {
|
||||
export const DescriptionVersionsDropdown = observer(function DescriptionVersionsDropdown(props: Props) {
|
||||
const { disabled, entityInformation, onVersionClick, versions } = props;
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ type Props = {
|
|||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
export const DescriptionVersionsModal: React.FC<Props> = observer((props) => {
|
||||
export const DescriptionVersionsModal = observer(function DescriptionVersionsModal(props: Props) {
|
||||
const {
|
||||
activeVersionDescription,
|
||||
activeVersionDetails,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue