Skip to content
15 changes: 15 additions & 0 deletions packages/constants/src/emoji.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,18 @@ export const ISSUE_REACTION_EMOJI_CODES = [
"9992",
"128064",
];

export const RANDOM_EMOJI_CODES = [
"8986",
"9200",
"128204",
"127773",
"127891",
"128076",
"128077",
"128187",
"128188",
"128512",
"128522",
"128578",
];
1 change: 1 addition & 0 deletions packages/constants/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export * from "./event-tracker";
export * from "./spreadsheet";
export * from "./dashboard";
export * from "./page";
export * from "./emoji";
21 changes: 19 additions & 2 deletions packages/constants/src/project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// icons
import { TProjectAppliedDisplayFilterKeys, TProjectOrderByOptions } from "@plane/types";
// plane imports
import { IProject, TProjectAppliedDisplayFilterKeys, TProjectOrderByOptions } from "@plane/types";
// local imports
import { RANDOM_EMOJI_CODES } from "./emoji";

export type TNetworkChoiceIconKey = "Lock" | "Globe2";

Expand Down Expand Up @@ -132,3 +134,18 @@ export const PROJECT_ERROR_MESSAGES = {
i18n_message: "workspace_projects.error.issue_delete",
},
};

export const DEFAULT_PROJECT_FORM_VALUES: Partial<IProject> = {
cover_image_url: PROJECT_UNSPLASH_COVERS[Math.floor(Math.random() * PROJECT_UNSPLASH_COVERS.length)],
description: "",
logo_props: {
in_use: "emoji",
emoji: {
value: RANDOM_EMOJI_CODES[Math.floor(Math.random() * RANDOM_EMOJI_CODES.length)],
},
},
identifier: "",
name: "",
network: 2,
project_lead: null,
};
18 changes: 8 additions & 10 deletions packages/constants/src/state.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
export type TStateGroups =
| "backlog"
| "unstarted"
| "started"
| "completed"
| "cancelled";
export type TStateGroups = "backlog" | "unstarted" | "started" | "completed" | "cancelled";

export type TDraggableData = {
groupKey: TStateGroups;
Expand All @@ -14,40 +9,43 @@ export const STATE_GROUPS: {
[key in TStateGroups]: {
key: TStateGroups;
label: string;
defaultStateName: string;
color: string;
};
} = {
backlog: {
key: "backlog",
label: "Backlog",
defaultStateName: "Backlog",
color: "#d9d9d9",
},
unstarted: {
key: "unstarted",
label: "Unstarted",
defaultStateName: "Todo",
color: "#3f76ff",
},
started: {
key: "started",
label: "Started",
defaultStateName: "In Progress",
color: "#f59e0b",
},
completed: {
key: "completed",
label: "Completed",
defaultStateName: "Done",
color: "#16a34a",
},
cancelled: {
key: "cancelled",
label: "Canceled",
defaultStateName: "Cancelled",
color: "#dc2626",
},
};

export const ARCHIVABLE_STATE_GROUPS = [
STATE_GROUPS.completed.key,
STATE_GROUPS.cancelled.key,
];
export const ARCHIVABLE_STATE_GROUPS = [STATE_GROUPS.completed.key, STATE_GROUPS.cancelled.key];
export const COMPLETED_STATE_GROUPS = [STATE_GROUPS.completed.key];
export const PENDING_STATE_GROUPS = [
STATE_GROUPS.backlog.key,
Expand Down
4 changes: 3 additions & 1 deletion packages/types/src/inbox.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// plane constants
import { TInboxIssue, TInboxIssueStatus } from "@plane/constants";
// plane types
import { TPaginationInfo } from "./common";
import { TIssuePriorities } from "./issues";
import { TIssue } from "./issues/base";

// filters
export type TInboxIssueFilterMemberKeys = "assignees" | "created_by";
Expand Down
8 changes: 8 additions & 0 deletions packages/types/src/state.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ export interface IStateLite {
export interface IStateResponse {
[key: string]: IState[];
}

export type TStateOperationsCallbacks = {
createState: (data: Partial<IState>) => Promise<IState>;
updateState: (stateId: string, data: Partial<IState>) => Promise<IState | undefined>;
deleteState: (stateId: string) => Promise<void>;
moveStatePosition: (stateId: string, data: Partial<IState>) => Promise<void>;
markStateAsDefault: (stateId: string) => Promise<void>;
};
4 changes: 2 additions & 2 deletions packages/ui/src/collapsible/collapsible-button.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { FC } from "react";
import { DropdownIcon } from "../icons";
import { cn } from "../../helpers";
import { DropdownIcon } from "../icons";

type Props = {
isOpen: boolean;
title: string;
title: React.ReactNode;
hideChevron?: boolean;
indicatorElement?: React.ReactNode;
actionItemElement?: React.ReactNode;
Expand Down
16 changes: 8 additions & 8 deletions packages/ui/src/dropdowns/helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ interface CustomSearchSelectProps {
onClose?: () => void;
noResultsMessage?: string;
options:
| {
value: any;
query: string;
content: React.ReactNode;
disabled?: boolean;
tooltip?: string | React.ReactNode;
}[]
| undefined;
| {
value: any;
query: string;
content: React.ReactNode;
disabled?: boolean;
tooltip?: string | React.ReactNode;
}[]
| undefined;
}

interface SingleValueProps {
Expand Down
19 changes: 19 additions & 0 deletions packages/utils/src/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
import { CompleteOrEmpty } from "@plane/types";

// Support email can be configured by the application
export const getSupportEmail = (defaultEmail: string = ""): string => defaultEmail;
Expand Down Expand Up @@ -39,3 +40,21 @@ export const partitionValidIds = (ids: string[], validIds: string[]): { valid: s

return { valid, invalid };
};

/**
* Checks if an object is complete (has properties) rather than empty.
* This helps TypeScript narrow the type from CompleteOrEmpty<T> to T.
*
* @param obj The object to check, typed as CompleteOrEmpty<T>
* @returns A boolean indicating if the object is complete (true) or empty (false)
*/
export const isComplete = <T>(obj: CompleteOrEmpty<T>): obj is T => {
// Check if object is not null or undefined
if (obj == null) return false;

// Check if it's an object
if (typeof obj !== "object") return false;

// Check if it has any own properties
return Object.keys(obj).length > 0;
};
4 changes: 2 additions & 2 deletions web/ce/components/issues/issue-modal/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { useState } from "react";
import { observer } from "mobx-react-lite";
// plane imports
import { ISearchIssueResponse } from "@plane/types";
import { ISearchIssueResponse, TIssue } from "@plane/types";
// components
import { IssueModalContext } from "@/components/issues";

export type TIssueModalProviderProps = {
templateId?: string;
dataForPreload?: Partial<TIssue>;
children: React.ReactNode;
};

Expand All @@ -32,7 +33,6 @@ export const IssueModalProvider = observer((props: TIssueModalProviderProps) =>
getActiveAdditionalPropertiesLength: () => 0,
handlePropertyValuesValidation: () => true,
handleCreateUpdatePropertyValues: () => Promise.resolve(),
handleParentWorkItemDetails: () => Promise.resolve(undefined),
handleProjectEntitiesFetch: () => Promise.resolve(),
handleTemplateChange: () => Promise.resolve(),
}}
Expand Down
26 changes: 5 additions & 21 deletions web/ce/components/projects/create/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
import { useState, FC } from "react";
import { observer } from "mobx-react";
import { FormProvider, useForm } from "react-hook-form";
import { PROJECT_UNSPLASH_COVERS, PROJECT_CREATED } from "@plane/constants";
import { PROJECT_CREATED, DEFAULT_PROJECT_FORM_VALUES } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
// ui
import { setToast, TOAST_TYPE } from "@plane/ui";
// constants
import ProjectCommonAttributes from "@/components/project/create/common-attributes";
import ProjectCreateHeader from "@/components/project/create/header";
import ProjectCreateButtons from "@/components/project/create/project-create-buttons";
// helpers
import { getRandomEmoji } from "@/helpers/emoji.helper";
// hooks
import { useEventTracker, useProject } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
Expand All @@ -26,26 +24,12 @@ export type TCreateProjectFormProps = {
onClose: () => void;
handleNextStep: (projectId: string) => void;
data?: Partial<TProject>;
templateId?: string;
updateCoverImageStatus: (projectId: string, coverImage: string) => Promise<void>;
};

const defaultValues: Partial<TProject> = {
cover_image_url: PROJECT_UNSPLASH_COVERS[Math.floor(Math.random() * PROJECT_UNSPLASH_COVERS.length)],
description: "",
logo_props: {
in_use: "emoji",
emoji: {
value: getRandomEmoji(),
},
},
identifier: "",
name: "",
network: 2,
project_lead: null,
};

export const CreateProjectForm: FC<TCreateProjectFormProps> = observer((props) => {
const { setToFavorite, workspaceSlug, onClose, handleNextStep, updateCoverImageStatus } = props;
const { setToFavorite, workspaceSlug, data, onClose, handleNextStep, updateCoverImageStatus } = props;
// store
const { t } = useTranslation();
const { captureProjectEvent } = useEventTracker();
Expand All @@ -54,7 +38,7 @@ export const CreateProjectForm: FC<TCreateProjectFormProps> = observer((props) =
const [isChangeInIdentifierRequired, setIsChangeInIdentifierRequired] = useState(true);
// form info
const methods = useForm<TProject>({
defaultValues,
defaultValues: { ...DEFAULT_PROJECT_FORM_VALUES, ...data },
reValidateMode: "onChange",
});
const { handleSubmit, reset, setValue } = methods;
Expand Down Expand Up @@ -105,7 +89,7 @@ export const CreateProjectForm: FC<TCreateProjectFormProps> = observer((props) =
handleNextStep(res.id);
})
.catch((err) => {
Object.keys(err.data).map((key) => {
Object.keys(err?.data ?? {}).map((key) => {
setToast({
type: TOAST_TYPE.ERROR,
title: t("error"),
Expand Down
12 changes: 12 additions & 0 deletions web/ce/components/projects/create/template-select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type TProjectTemplateDropdownSize = "xs" | "sm";

export type TProjectTemplateSelect = {
disabled?: boolean;
size?: TProjectTemplateDropdownSize;
placeholder?: string;
dropDownContainerClassName?: string;
handleModalClose: () => void;
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const ProjectTemplateSelect = (props: TProjectTemplateSelect) => <></>;
Loading