-
Notifications
You must be signed in to change notification settings - Fork 3.6k
chore: refactor project states to make way for new features #6156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { Plus } from "lucide-react"; | ||
| // Plane | ||
| import { cn } from "@plane/editor"; | ||
|
|
||
| type Props = { | ||
| workspaceSlug: string; | ||
| projectId: string; | ||
| parentStateId: string; | ||
| onTransitionAdd?: () => void; | ||
| }; | ||
|
|
||
| export const AddStateTransition = (props: Props) => ( | ||
| <div className={cn("flex w-full px-3 h-6 items-center justify-start gap-2 text-sm bg-custom-background-90")}> | ||
| <> | ||
| <Plus className="h-4 w-4" color="#8591AD" /> | ||
| <span className="text-custom-text-400 font-medium"> Add Transition</span> | ||
| <div className="text-white bg-custom-background-80 font-semibold px-2 rounded-lg">Pro</div> | ||
| </> | ||
| </div> | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| export * from "./state-option"; | ||
| export * from "./state-item-child"; | ||
| export * from "./state-transition-count"; | ||
| export * from "./use-workflow-drag-n-drop"; | ||
| export * from "./workflow-disabled-message"; | ||
| export * from "./workflow-group-tree"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { SetStateAction } from "react"; | ||
| import { observer } from "mobx-react"; | ||
| // Plane | ||
| import { IState } from "@plane/types"; | ||
| // components | ||
| import { StateItemTitle } from "@/components/project-states/state-item-title"; | ||
| // | ||
| import { AddStateTransition } from "./add-state-transition"; | ||
|
|
||
| export type StateItemChildProps = { | ||
| workspaceSlug: string; | ||
| projectId: string; | ||
| stateCount: number; | ||
| disabled: boolean; | ||
| state: IState; | ||
| setUpdateStateModal: (value: SetStateAction<boolean>) => void; | ||
| }; | ||
|
|
||
| export const StateItemChild = observer((props: StateItemChildProps) => { | ||
| const { workspaceSlug, projectId, stateCount, setUpdateStateModal, disabled, state } = props; | ||
|
|
||
| return ( | ||
| <div className="flex flex-col w-full items-center justify-between"> | ||
| <StateItemTitle | ||
| workspaceSlug={workspaceSlug} | ||
| projectId={projectId} | ||
| setUpdateStateModal={setUpdateStateModal} | ||
| stateCount={stateCount} | ||
| disabled={disabled} | ||
| state={state} | ||
| /> | ||
| <AddStateTransition workspaceSlug={workspaceSlug} projectId={projectId} parentStateId={state.id} /> | ||
| </div> | ||
| ); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| import { observer } from "mobx-react"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { Check } from "lucide-react"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { Combobox } from "@headlessui/react"; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| type Props = { | ||||||||||||||||||||||||||||||||||||||||||||
| projectId: string | null | undefined; | ||||||||||||||||||||||||||||||||||||||||||||
| option: { | ||||||||||||||||||||||||||||||||||||||||||||
| value: string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||
| query: string; | ||||||||||||||||||||||||||||||||||||||||||||
| content: JSX.Element; | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
| filterAvailableStateIds: boolean; | ||||||||||||||||||||||||||||||||||||||||||||
| selectedValue: string | null | undefined; | ||||||||||||||||||||||||||||||||||||||||||||
| className?: string; | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider strengthening prop types and removing unused props The Props type definition has some potential improvements:
Consider this improvement: type Props = {
- projectId: string | null | undefined;
+ projectId: string;
option: {
- value: string | undefined;
+ value: string;
query: string;
content: JSX.Element;
};
- filterAvailableStateIds: boolean;
- selectedValue: string | null | undefined;
+ selectedValue: string;
className?: string;
};📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| export const StateOption = observer((props: Props) => { | ||||||||||||||||||||||||||||||||||||||||||||
| const { option, className = "" } = props; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||
| <Combobox.Option | ||||||||||||||||||||||||||||||||||||||||||||
| key={option.value} | ||||||||||||||||||||||||||||||||||||||||||||
| value={option.value} | ||||||||||||||||||||||||||||||||||||||||||||
| className={({ active, selected }) => | ||||||||||||||||||||||||||||||||||||||||||||
| `${className} ${active ? "bg-custom-background-80" : ""} ${selected ? "text-custom-text-100" : "text-custom-text-200"}` | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||
| {({ selected }) => ( | ||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||
| <span className="flex-grow truncate">{option.content}</span> | ||||||||||||||||||||||||||||||||||||||||||||
| {selected && <Check className="h-3.5 w-3.5 flex-shrink-0" />} | ||||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||
| </Combobox.Option> | ||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { IStateWorkFlow } from "@/plane-web/types"; | ||
|
|
||
| type Props = { | ||
| currentTransitionMap?: IStateWorkFlow; | ||
| }; | ||
|
|
||
| export const StateTransitionCount = (props: Props) => <></>; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { TIssueGroupByOptions } from "@plane/types"; | ||
|
|
||
| export const useWorkFlowFDragNDrop = ( | ||
| groupBy: TIssueGroupByOptions | undefined, | ||
| subGroupBy?: TIssueGroupByOptions | ||
| ) => ({ | ||
| workflowDisabledSource: undefined, | ||
| isWorkflowDropDisabled: false, | ||
| handleWorkFlowState: ( | ||
| sourceGroupId: string, | ||
| destinationGroupId: string, | ||
| sourceSubGroupId?: string, | ||
| destinationSubGroupId?: string | ||
| ) => {}, | ||
| }); | ||
|
Comment on lines
+1
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty implementation in The |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| type Props = { | ||
| parentStateId: string; | ||
| className?: string; | ||
| }; | ||
|
|
||
| export const WorkFlowDisabledMessage = (props: Props) => <></>; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { TIssueGroupByOptions } from "@plane/types"; | ||
|
|
||
| type Props = { | ||
| groupBy?: TIssueGroupByOptions; | ||
| groupId: string | undefined; | ||
| }; | ||
|
|
||
| export const WorkFlowGroupTree = (props: Props) => <></>; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,2 @@ | ||
| export * from "./project"; | ||
| export * from "./workspace.service"; | ||
| export * from "./workspace.service"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from "@/services/project/project-state.service"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from "@/store/state.store"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export * from "./projects"; | ||
| export * from "./issue-types"; | ||
| export * from "./gantt-chart"; | ||
| export * from "./state.d"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| export interface IStateTransition { | ||
| transition_state_id: string; | ||
| actors: string[]; | ||
| } | ||
|
|
||
| export interface IStateWorkFlow { | ||
| [transitionId: string]: IStateTransition; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,10 @@ | ||
| "use client"; | ||
|
|
||
| import { Fragment, ReactNode, useEffect, useRef, useState } from "react"; | ||
| import { ReactNode, useEffect, useRef, useState } from "react"; | ||
| import { observer } from "mobx-react"; | ||
| import { useParams } from "next/navigation"; | ||
| import { usePopper } from "react-popper"; | ||
| import { Check, ChevronDown, Search } from "lucide-react"; | ||
| import { ChevronDown, Search } from "lucide-react"; | ||
| import { Combobox } from "@headlessui/react"; | ||
| // ui | ||
| import { ComboDropDown, Spinner, StateGroupIcon } from "@plane/ui"; | ||
|
|
@@ -13,6 +13,8 @@ import { cn } from "@/helpers/common.helper"; | |
| // hooks | ||
| import { useProjectState } from "@/hooks/store"; | ||
| import { useDropdown } from "@/hooks/use-dropdown"; | ||
| // Plane-web | ||
| import { StateOption } from "@/plane-web/components/workflow"; | ||
| // components | ||
| import { DropdownButton } from "./buttons"; | ||
| // constants | ||
|
|
@@ -30,6 +32,8 @@ type Props = TDropdownProps & { | |
| showDefaultState?: boolean; | ||
| value: string | undefined | null; | ||
| renderByDefault?: boolean; | ||
| stateIds?: string[]; | ||
| filterAvailableStateIds?: boolean; | ||
| }; | ||
|
|
||
| export const StateDropdown: React.FC<Props> = observer((props) => { | ||
|
|
@@ -52,6 +56,8 @@ export const StateDropdown: React.FC<Props> = observer((props) => { | |
| tabIndex, | ||
| value, | ||
| renderByDefault = true, | ||
| stateIds, | ||
| filterAvailableStateIds = true, | ||
| } = props; | ||
| // states | ||
| const [query, setQuery] = useState(""); | ||
|
|
@@ -78,16 +84,18 @@ export const StateDropdown: React.FC<Props> = observer((props) => { | |
| // store hooks | ||
| const { workspaceSlug } = useParams(); | ||
| const { fetchProjectStates, getProjectStates, getStateById } = useProjectState(); | ||
| const statesList = getProjectStates(projectId); | ||
| const defaultState = statesList?.find((state) => state.default); | ||
| const statesList = stateIds | ||
| ? stateIds.map((stateId) => getStateById(stateId)).filter((state) => !!state) | ||
| : getProjectStates(projectId); | ||
| const defaultState = statesList?.find((state) => state?.default); | ||
| const stateValue = !!value ? value : showDefaultState ? defaultState?.id : undefined; | ||
|
|
||
| const options = statesList?.map((state) => ({ | ||
| value: state.id, | ||
| value: state?.id, | ||
| query: `${state?.name}`, | ||
| content: ( | ||
| <div className="flex items-center gap-2"> | ||
| <StateGroupIcon stateGroup={state.group} color={state.color} className="h-3 w-3 flex-shrink-0" /> | ||
| <StateGroupIcon stateGroup={state?.group ?? "backlog"} color={state?.color} className="h-3 w-3 flex-shrink-0" /> | ||
| <span className="flex-grow truncate">{state?.name}</span> | ||
| </div> | ||
| ), | ||
|
|
@@ -226,22 +234,14 @@ export const StateDropdown: React.FC<Props> = observer((props) => { | |
| {filteredOptions ? ( | ||
| filteredOptions.length > 0 ? ( | ||
| filteredOptions.map((option) => ( | ||
| <Combobox.Option | ||
| <StateOption | ||
| key={option.value} | ||
| value={option.value} | ||
| className={({ active, selected }) => | ||
| `flex w-full cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5 ${ | ||
| active ? "bg-custom-background-80" : "" | ||
| } ${selected ? "text-custom-text-100" : "text-custom-text-200"}` | ||
| } | ||
| > | ||
| {({ selected }) => ( | ||
| <> | ||
| <span className="flex-grow truncate">{option.content}</span> | ||
| {selected && <Check className="h-3.5 w-3.5 flex-shrink-0" />} | ||
| </> | ||
| )} | ||
| </Combobox.Option> | ||
| option={option} | ||
| projectId={projectId} | ||
| filterAvailableStateIds={filterAvailableStateIds} | ||
| selectedValue={value} | ||
| className="flex w-full cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5" | ||
| /> | ||
|
Comment on lines
+237
to
+244
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification StateOption integration looks correct but props mismatch detected The StateOption component in
🔗 Analysis chainLGTM! Verify StateOption component integration. The integration of the custom StateOption component looks good. However, let's verify its implementation to ensure it handles all edge cases properly. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check StateOption component implementation
ast-grep --pattern 'export const StateOption = ({ option, projectId, filterAvailableStateIds, selectedValue, className }) => {
$$$
}'
Length of output: 137 Script: #!/bin/bash
# Let's try a broader search for StateOption component
ast-grep --pattern 'const StateOption = $_'
# Also search for any file containing StateOption
rg "StateOption" -A 10
Length of output: 4245 |
||
| )) | ||
| ) : ( | ||
| <p className="px-1.5 py-1 italic text-custom-text-400">No matches found</p> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AddStateTransitioncomponent lacks interaction handlingThe
onTransitionAddprop is defined but not utilized. Consider adding anonClickhandler to invokeonTransitionAddwhen the component is clicked.Apply this diff to add the
onClickhandler:export const AddStateTransition = (props: Props) => ( <div className={cn("flex w-full px-3 h-6 items-center justify-start gap-2 text-sm bg-custom-background-90")} + onClick={props.onTransitionAdd} > <Plus className="h-4 w-4" color="#8591AD" /> <span className="text-custom-text-400 font-medium"> Add Transition</span> <div className="text-white bg-custom-background-80 font-semibold px-2 rounded-lg">Pro</div> </div> );