edit logic

This commit is contained in:
Robert 2024-03-12 01:59:57 +07:00
parent 8f0b8c48a9
commit aec66cdc2c
No known key found for this signature in database
GPG Key ID: F631C7FD957D5F22
3 changed files with 173 additions and 51 deletions

View File

@ -0,0 +1 @@
export { default as ActionCard } from "./ui/ActionCard";

View File

@ -0,0 +1,138 @@
import type { Chain } from "@/entities/chain/schema";
import type { Action } from "@/entities/action/schema";
import { useState } from "react";
import humanizeDuration from "humanize-duration";
import { ChangeWaitForButton } from "@/features/change-wait-for";
import { getActionAttachmentType } from "@/entities/action/lib";
import { IconButton } from "@mui/material";
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
import EditIcon from "@mui/icons-material/Edit";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { Menu, MenuItem, Divider } from "@mui/material";
interface ActionCardProps {
chain: Chain;
action: Action;
actionIndex: number;
onEdit?: (actionIndex: number) => void;
}
export default function ActionCard({
chain,
action,
actionIndex,
onEdit,
}: ActionCardProps) {
const [isOpen, setIsOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
if (action.actionType === "comment") {
const attachmentType = getActionAttachmentType(action);
return (
<div className="relative w-[450px] select-none whitespace-pre-line rounded-md bg-white text-[#4C4E64DE] opacity-[84%] shadow-lg after:absolute after:-bottom-12 after:left-1/2 after:h-8 after:w-1 after:-translate-x-1/2 after:rounded-md after:bg-[#666CFF] after:opacity-[12%]">
<div className="absolute -right-3 top-0 translate-x-full">
<IconButton
onClick={(event) => {
setAnchorEl(event.currentTarget);
setIsOpen(true);
}}
sx={{
borderRadius: "4px",
border: "1px solid #4C4E641F",
width: "32px",
height: "32px",
}}
size="small"
>
<MoreHorizIcon />
</IconButton>
<Menu
className="mt-2"
anchorEl={anchorEl}
open={isOpen}
onClose={() => setIsOpen(false)}
>
<MenuItem
onClick={() => {
setIsOpen(false);
if (onEdit) onEdit(actionIndex);
}}
>
<EditIcon
sx={{ color: "#4C4E64DE", opacity: "87%" }}
className="mr-2"
/>
<span className="font-normal text-[#4C4E64DE] opacity-[87%]">
Редактировать
</span>
</MenuItem>
<Divider sx={{ my: 0.5 }} />
<MenuItem>
<ArrowUpwardIcon
sx={{ color: "#4C4E64DE", opacity: "87%" }}
className="mr-2"
/>
<span className="font-normal text-[#4C4E64DE] opacity-[87%]">
Переместить выше
</span>
</MenuItem>
<MenuItem>
<ArrowDownwardIcon
sx={{ color: "#4C4E64DE", opacity: "87%" }}
className="mr-2"
/>
<span className="font-normal text-[#4C4E64DE] opacity-[87%]">
Переместить ниже
</span>
</MenuItem>
<Divider sx={{ my: 0.5 }} />
<MenuItem>
<DeleteOutlineIcon
sx={{ color: "#4C4E64DE", opacity: "87%" }}
className="mr-2"
/>
<span className="font-normal text-[#4C4E64DE] opacity-[87%]">
Удалить
</span>
</MenuItem>
</Menu>
</div>
{attachmentType === "image" && (
<img
src={action.fileUrls[0]}
alt="Image"
draggable={false}
className={`w-full select-none ${!action.text && "rounded-b-md"} rounded-t-md`}
/>
)}
{action.text && <div className="p-4">{action.text}</div>}
</div>
);
}
return (
<div className="relative flex select-none items-center gap-x-2 text-[#4C4E64DE] opacity-[84%] before:absolute before:-top-6 before:left-1/2 before:h-4 before:w-4 before:-translate-x-1/2 before:rounded-full before:border-[2.5px] before:border-primary after:absolute after:left-1/2 after:top-11 after:h-16 after:w-1 after:-translate-x-1/2 after:rounded-md after:bg-[#666CFF] after:opacity-[12%]">
<span>
Задержка: {humanizeDuration(action.waitFor * 1000, { language: "ru" })}
</span>
<ChangeWaitForButton
chainId={chain._id!}
actionIndex={actionIndex}
action={action}
/>
</div>
);
}

View File

@ -2,19 +2,17 @@ import type { Chain } from "@/entities/chain/schema";
import { useChainState } from "@/entities/chain/model"; import { useChainState } from "@/entities/chain/model";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { getActionAttachmentType } from "@/entities/action/lib";
import { useState, useRef } from "react"; import { useState, useRef } from "react";
import humanizeDuration from "humanize-duration";
import { upsertChain } from "@/entities/chain/api/upsert"; import { upsertChain } from "@/entities/chain/api/upsert";
import { ChangeWaitForButton } from "@/features/change-wait-for";
import { ActionEditor } from "@/widgets/action-editor"; import { ActionEditor } from "@/widgets/action-editor";
import { ActionCard } from "@/widgets/action-card";
import { RenameChainButton } from "@/features/rename-chain"; import { RenameChainButton } from "@/features/rename-chain";
import { DeleteChainButton } from "@/features/delete-chain"; import { DeleteChainButton } from "@/features/delete-chain";
import { Typography, Button } from "@mui/material"; import { Typography, Button } from "@mui/material";
import Breadcrumbs from "@mui/material/Breadcrumbs"; import Breadcrumbs from "@mui/material/Breadcrumbs";
import { CommentAction } from "@/entities/action/schema";
interface ChainEditorProps { interface ChainEditorProps {
chain: Chain; chain: Chain;
@ -22,11 +20,16 @@ interface ChainEditorProps {
export default function ChainEditor({ chain }: ChainEditorProps) { export default function ChainEditor({ chain }: ChainEditorProps) {
const actionsListRef = useRef<HTMLDivElement>(null); const actionsListRef = useRef<HTMLDivElement>(null);
const [addingNewAction, setAddingNewAction] = useState(false); const [addingNewAction, setAddingNewAction] = useState(
!chain.actions || chain.actions.length === 0,
);
const navigate = useNavigate({ from: "/$namespace/$chainId" }); const navigate = useNavigate({ from: "/$namespace/$chainId" });
const addCommentAction = useChainState((state) => state.addCommentAction); const addCommentAction = useChainState((state) => state.addCommentAction);
const updateAction = useChainState((state) => state.updateAction);
const [actionToEdit, setActionToEdit] = useState<number | null>(null);
if (!chain.actions || chain.actions.length === 0 || addingNewAction) if (!chain.actions || addingNewAction || actionToEdit !== null)
return ( return (
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -55,11 +58,21 @@ export default function ChainEditor({ chain }: ChainEditorProps) {
<div className="flex grow flex-col items-center justify-center"> <div className="flex grow flex-col items-center justify-center">
<ActionEditor <ActionEditor
onSave={async (_, action) => { initialAction={
addCommentAction(chain._id!, action); actionToEdit !== null
await upsertChain(chain); ? (chain.actions?.[actionToEdit] as CommentAction)
: undefined
}
onSave={async (actionIndex, action) => {
if (addingNewAction) {
addCommentAction(chain._id!, action);
setAddingNewAction(false);
} else if (actionToEdit !== null) {
updateAction(chain._id!, actionIndex, action);
setActionToEdit(null);
}
if (addingNewAction) setAddingNewAction(false); await upsertChain(chain);
navigate({ to: "/$namespace/$chainId" }); navigate({ to: "/$namespace/$chainId" });
setTimeout( setTimeout(
@ -73,8 +86,9 @@ export default function ChainEditor({ chain }: ChainEditorProps) {
}} }}
onClose={() => { onClose={() => {
if (addingNewAction) setAddingNewAction(false); if (addingNewAction) setAddingNewAction(false);
if (actionToEdit !== null) setActionToEdit(null);
}} }}
canExit={addingNewAction} canExit={chain.actions?.length !== 0}
/> />
</div> </div>
</div> </div>
@ -100,46 +114,15 @@ export default function ChainEditor({ chain }: ChainEditorProps) {
ref={actionsListRef} ref={actionsListRef}
className="no-scrollbar flex grow flex-col items-center gap-y-24 overflow-y-scroll pb-20 pt-8" className="no-scrollbar flex grow flex-col items-center gap-y-24 overflow-y-scroll pb-20 pt-8"
> >
{chain.actions.map((action, index) => { {chain.actions.map((action, index) => (
if (action.actionType === "comment") { <ActionCard
const attachmentType = getActionAttachmentType(action); key={index}
chain={chain}
return ( action={action}
<div actionIndex={index}
key={index} onEdit={setActionToEdit}
className="relative w-[450px] select-none whitespace-pre-line rounded-md bg-white text-[#4C4E64DE] opacity-[84%] shadow-lg after:absolute after:-bottom-12 after:left-1/2 after:h-8 after:w-1 after:-translate-x-1/2 after:rounded-md after:bg-[#666CFF] after:opacity-[12%]" />
> ))}
{attachmentType === "image" && (
<img
src={action.fileUrls[0]}
alt="Image"
draggable={false}
className={`w-full select-none ${!action.text && "rounded-b-md"} rounded-t-md`}
/>
)}
{action.text && <div className="p-4">{action.text}</div>}
</div>
);
}
return (
<div
key={index}
className="relative flex select-none items-center gap-x-2 text-[#4C4E64DE] opacity-[84%] before:absolute before:-top-6 before:left-1/2 before:h-4 before:w-4 before:-translate-x-1/2 before:rounded-full before:border-[2.5px] before:border-primary after:absolute after:left-1/2 after:top-11 after:h-16 after:w-1 after:-translate-x-1/2 after:rounded-md after:bg-[#666CFF] after:opacity-[12%]"
>
<span>
Задержка:{" "}
{humanizeDuration(action.waitFor * 1000, { language: "ru" })}
</span>
<ChangeWaitForButton
chainId={chain._id!}
actionIndex={index}
action={action}
/>
</div>
);
})}
<div className="-mt-4"> <div className="-mt-4">
<Button <Button