feat: init
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
chuan
2025-11-11 01:56:44 +08:00
commit bba4bb40c8
4638 changed files with 447437 additions and 0 deletions

View File

@@ -0,0 +1 @@
export { ProjectFeaturesList } from "@/components/project/settings/features-list";

View File

@@ -0,0 +1,81 @@
"use client";
import type { FC } from "react";
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { RefreshCcw } from "lucide-react";
// ui
import { EProjectFeatureKey, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { Breadcrumbs, Header } from "@plane/ui";
// components
import { InboxIssueCreateModalRoot } from "@/components/inbox/modals/create-modal";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { useProjectInbox } from "@/hooks/store/use-project-inbox";
import { useUserPermissions } from "@/hooks/store/user";
// plane web
import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common";
export const ProjectInboxHeader: FC = observer(() => {
// states
const [createIssueModal, setCreateIssueModal] = useState(false);
// router
const { workspaceSlug, projectId } = useParams();
// store hooks
const { allowPermissions } = useUserPermissions();
const { t } = useTranslation();
const { currentProjectDetails, loader: currentProjectDetailsLoader } = useProject();
const { loader } = useProjectInbox();
// derived value
const isAuthorized = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
EUserPermissionsLevel.PROJECT
);
return (
<Header>
<Header.LeftItem>
<div className="flex items-center gap-4 flex-grow">
<Breadcrumbs isLoading={currentProjectDetailsLoader === "init-loader"}>
<CommonProjectBreadcrumbs
workspaceSlug={workspaceSlug?.toString() ?? ""}
projectId={projectId?.toString() ?? ""}
featureKey={EProjectFeatureKey.INTAKE}
isLast
/>
</Breadcrumbs>
{loader === "pagination-loading" && (
<div className="flex items-center gap-1.5 text-custom-text-300">
<RefreshCcw className="h-3.5 w-3.5 animate-spin" />
<p className="text-sm">{t("syncing")}...</p>
</div>
)}
</div>
</Header.LeftItem>
<Header.RightItem>
{currentProjectDetails?.inbox_view && workspaceSlug && projectId && isAuthorized ? (
<div className="flex items-center gap-2">
<InboxIssueCreateModalRoot
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
modalState={createIssueModal}
handleModalClose={() => setCreateIssueModal(false)}
/>
<Button variant="primary" size="sm" onClick={() => setCreateIssueModal(true)}>
{t("add_work_item")}
</Button>
</div>
) : (
<></>
)}
</Header.RightItem>
</Header>
);
});

View File

@@ -0,0 +1,138 @@
import { useState } from "react";
// plane imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import type { IWorkspaceMember, TProjectMembership } from "@plane/types";
import { renderFormattedDate } from "@plane/utils";
// components
import { MemberHeaderColumn } from "@/components/project/member-header-column";
import { AccountTypeColumn, NameColumn } from "@/components/project/settings/member-columns";
// hooks
import { useMember } from "@/hooks/store/use-member";
import { useUser, useUserPermissions } from "@/hooks/store/user";
import type { IMemberFilters } from "@/store/member/utils";
export interface RowData extends Pick<TProjectMembership, "original_role"> {
member: IWorkspaceMember;
}
type TUseProjectColumnsProps = {
projectId: string;
workspaceSlug: string;
};
export const useProjectColumns = (props: TUseProjectColumnsProps) => {
const { projectId, workspaceSlug } = props;
// states
const [removeMemberModal, setRemoveMemberModal] = useState<RowData | null>(null);
// store hooks
const { data: currentUser } = useUser();
const { allowPermissions, getProjectRoleByWorkspaceSlugAndProjectId } = useUserPermissions();
const {
project: {
filters: { getFilters, updateFilters },
},
} = useMember();
// derived values
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId.toString()
);
const currentProjectRole =
getProjectRoleByWorkspaceSlugAndProjectId(workspaceSlug.toString(), projectId.toString()) ?? EUserPermissions.GUEST;
const displayFilters = getFilters(projectId);
// handlers
const handleDisplayFilterUpdate = (filters: Partial<IMemberFilters>) => {
updateFilters(projectId, filters);
};
const columns = [
{
key: "Full Name",
content: "Full name",
thClassName: "text-left",
thRender: () => (
<MemberHeaderColumn
property="full_name"
displayFilters={displayFilters}
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
/>
),
tdRender: (rowData: RowData) => (
<NameColumn
rowData={rowData}
workspaceSlug={workspaceSlug as string}
isAdmin={isAdmin}
currentUser={currentUser}
setRemoveMemberModal={setRemoveMemberModal}
/>
),
},
{
key: "Display Name",
content: "Display name",
thRender: () => (
<MemberHeaderColumn
property="display_name"
displayFilters={displayFilters}
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
/>
),
tdRender: (rowData: RowData) => <div className="w-32">{rowData.member.display_name}</div>,
},
{
key: "Email",
content: "Email",
thRender: () => (
<MemberHeaderColumn
property="email"
displayFilters={displayFilters}
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
/>
),
tdRender: (rowData: RowData) => <div className="w-48 text-custom-text-200">{rowData.member.email}</div>,
},
{
key: "Account Type",
content: "Account type",
thRender: () => (
<MemberHeaderColumn
property="role"
displayFilters={displayFilters}
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
/>
),
tdRender: (rowData: RowData) => (
<AccountTypeColumn
rowData={rowData}
currentProjectRole={currentProjectRole}
projectId={projectId as string}
workspaceSlug={workspaceSlug as string}
/>
),
},
{
key: "Joining Date",
content: "Joining date",
thRender: () => (
<MemberHeaderColumn
property="joining_date"
displayFilters={displayFilters}
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
/>
),
tdRender: (rowData: RowData) => <div>{renderFormattedDate(rowData?.member?.joining_date)}</div>,
},
];
return {
columns,
removeMemberModal,
setRemoveMemberModal,
displayFilters,
handleDisplayFilterUpdate,
};
};