mirror of
https://github.com/makeplane/plane.git
synced 2026-07-14 06:25:58 +02:00
* feat: epic updates wip * feat: create endpoint for epic update * feat: delete, get, update endpoint for epic updates * feat: comments endpoint * feat: update reaction endpoints * refactor: code refactor * wip: epic updates * fix: epic update minor bug fixes * WIP * feat: endpoint to list updates * feat: patch and delete endpoint for update feat: get and post for comments feat: post adn delete for reactions * chore: use EntityTypeEnum instead of hardcoding * fix: add cancelled issues count * chore: return project and epic details * refactor: fetching project and epic details * feat: initiative updates * fix: i18n changes * fix: i18n changes * fix: i18n * fix: initiative updates * fix: translations + reaction bug * feat: filter based on status * fix: removed updates for initiatives * fix: initiative updates * chore: added updates in initiative analytics * fix: refactor * chore: optimised the initiative updates query * chore: added description_html * fix: refactor * fix: list view on click * fix: return only latest record * fix: handled epic and project addition and deletion * fix: build * fix: popover component * fix: latest updates returning all status's latest one * fix: local update * fix: text overflow issue * fix: icon colors * fix: update status on project attributes returning same update --------- Co-authored-by: sangeethailango <sangeethailango21@gmail.com> Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
126 lines
4.3 KiB
TypeScript
126 lines
4.3 KiB
TypeScript
import { FC, useMemo, useState } from "react";
|
|
import { observer } from "mobx-react";
|
|
import { useParams } from "next/navigation";
|
|
import { Plus } from "lucide-react";
|
|
// plane package imports
|
|
import { E_SORT_ORDER } from "@plane/constants";
|
|
import { useLocalStorage } from "@plane/hooks";
|
|
// components
|
|
import { useTranslation } from "@plane/i18n";
|
|
import { TUpdate, TUpdateOperations } from "@plane/types";
|
|
import { EUpdateEntityType } from "@plane/types/src/enums";
|
|
import { Loader, setToast, TOAST_TYPE } from "@plane/ui";
|
|
import { ActivitySortRoot } from "@/components/issues";
|
|
import { useUpdateDetail } from "@/plane-web/hooks/use-update-detail";
|
|
import { UpdateBlock } from "./block";
|
|
import { EmptyUpdates } from "./empty";
|
|
import { NewUpdate } from "./new-update";
|
|
type TProps = {
|
|
entityType: EUpdateEntityType;
|
|
entityId: string;
|
|
allowNew?: boolean;
|
|
handleUpdateOperations: TUpdateOperations;
|
|
customTitle?: (updateData: TUpdate) => React.ReactNode;
|
|
};
|
|
|
|
export const UpdatesWrapper: FC<TProps> = observer(
|
|
({ entityType, entityId, allowNew = true, handleUpdateOperations, customTitle }) => {
|
|
const { workspaceSlug } = useParams();
|
|
// state
|
|
const [showInput, setShowInput] = useState(false);
|
|
const { storedValue: sortOrder, setValue: setSortOrder } = useLocalStorage<E_SORT_ORDER>(
|
|
`${entityType}_updates_sort_order`,
|
|
E_SORT_ORDER.ASC
|
|
);
|
|
// hooks
|
|
const { getUpdatesByEntityId, loader } = useUpdateDetail(entityType);
|
|
const { t } = useTranslation();
|
|
|
|
// derived
|
|
const updates = getUpdatesByEntityId(entityId.toString()) ?? [];
|
|
|
|
// handler
|
|
const toggleSortOrder = () => {
|
|
setSortOrder(sortOrder === E_SORT_ORDER.ASC ? E_SORT_ORDER.DESC : E_SORT_ORDER.ASC);
|
|
};
|
|
|
|
const handleNewUpdate = async (data: Partial<TUpdate>) => {
|
|
try {
|
|
await handleUpdateOperations.createUpdate(data);
|
|
setShowInput(false);
|
|
setToast({
|
|
message: t("updates.create.success.message"),
|
|
type: TOAST_TYPE.SUCCESS,
|
|
title: t("updates.create.success.title"),
|
|
});
|
|
} catch (error) {
|
|
setToast({
|
|
message: t("updates.create.error.message"),
|
|
type: TOAST_TYPE.ERROR,
|
|
title: t("updates.create.error.title"),
|
|
});
|
|
console.error("error", error);
|
|
}
|
|
};
|
|
|
|
const sortedUpdates = useMemo(
|
|
() => (sortOrder === E_SORT_ORDER.ASC ? [...updates].reverse() : updates),
|
|
[sortOrder, updates]
|
|
);
|
|
|
|
return loader ? (
|
|
<Loader className="flex flex-col gap-4 py-4">
|
|
<Loader.Item height="125px" width="100%" />
|
|
<Loader.Item height="125px" width="100%" />
|
|
<Loader.Item height="125px" width="100%" />
|
|
</Loader>
|
|
) : (
|
|
<>
|
|
{/* New Update */}
|
|
{showInput && allowNew && <NewUpdate handleClose={() => setShowInput(false)} handleCreate={handleNewUpdate} />}
|
|
|
|
{/* No Updates */}
|
|
{!showInput && updates.length === 0 && (
|
|
<EmptyUpdates handleNewUpdate={() => setShowInput(true)} allowNew={allowNew} />
|
|
)}
|
|
|
|
{/* Add update */}
|
|
{!showInput && updates.length !== 0 && allowNew && (
|
|
<div className="flex justify-between h-7 items-center">
|
|
<button
|
|
className="flex text-custom-primary-100 text-sm font-medium rounded w-fit py-1 px-2"
|
|
onClick={() => setShowInput(true)}
|
|
>
|
|
<Plus size={15} className="my-auto mr-1" />
|
|
<div>{t("updates.add_update")}</div>
|
|
</button>
|
|
<ActivitySortRoot
|
|
sortOrder={sortOrder ?? E_SORT_ORDER.ASC}
|
|
toggleSort={toggleSortOrder}
|
|
className="flex-shrink-0"
|
|
iconClassName="size-3"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Updates */}
|
|
{sortedUpdates.length > 0 && (
|
|
<div className="flex flex-col gap-3 pb-20">
|
|
{sortedUpdates.map((updateId) => (
|
|
<UpdateBlock
|
|
updateId={updateId}
|
|
key={updateId}
|
|
workspaceSlug={workspaceSlug.toString()}
|
|
entityId={entityId.toString()}
|
|
handleUpdateOperations={handleUpdateOperations}
|
|
entityType={entityType}
|
|
customTitle={customTitle}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
);
|