mirror of
https://github.com/langgenius/dify.git
synced 2026-04-26 13:45:57 +08:00
fix: web style
This commit is contained in:
@ -1,4 +1,3 @@
|
||||
/* eslint-disable ts/no-explicit-any */
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import AccessControlDialog from '../access-control-dialog'
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable ts/no-explicit-any */
|
||||
import type { App } from '@/types/app'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
|
||||
@ -73,7 +73,6 @@ const ImageInput: FC<UploaderProps> = ({
|
||||
const handleShowImage = () => {
|
||||
if (isAnimatedImage) {
|
||||
return (
|
||||
// eslint-disable-next-line next/no-img-element
|
||||
<img src={inputImage?.url} alt="" data-testid="animated-image" />
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable next/no-img-element */
|
||||
import type { FC } from 'react'
|
||||
import type { ImageFile } from '@/types/app'
|
||||
import { useState } from 'react'
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable next/no-img-element */
|
||||
import type { ExtraProps } from 'streamdown'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
@ -208,10 +208,10 @@ export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick, isAct
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<div
|
||||
className="relative h-10 rounded-br-full rounded-tl-full rounded-tr-full"
|
||||
className="relative h-10 rounded-tl-full rounded-tr-full rounded-br-full"
|
||||
style={{ width: dynamicWidth }}
|
||||
>
|
||||
<div className={`absolute inset-[6px] overflow-hidden rounded-br-full rounded-tl-full rounded-tr-full border bg-components-panel-bg transition-shadow ${
|
||||
<div className={`absolute inset-[6px] overflow-hidden rounded-tl-full rounded-tr-full rounded-br-full border bg-components-panel-bg transition-shadow ${
|
||||
isActive
|
||||
? 'border-primary-500 ring-1 ring-primary-500'
|
||||
: 'border-components-panel-border'
|
||||
|
||||
@ -136,8 +136,8 @@ export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, o
|
||||
className="relative shrink-0 cursor-move"
|
||||
onPointerDown={handleDragPointerDown}
|
||||
>
|
||||
<div className="relative h-8 w-8 overflow-hidden rounded-br-full rounded-tl-full rounded-tr-full bg-primary-500">
|
||||
<div className="absolute inset-[2px] overflow-hidden rounded-br-full rounded-tl-full rounded-tr-full bg-components-panel-bg-blur">
|
||||
<div className="relative h-8 w-8 overflow-hidden rounded-tl-full rounded-tr-full rounded-br-full bg-primary-500">
|
||||
<div className="absolute inset-[2px] overflow-hidden rounded-tl-full rounded-tr-full rounded-br-full bg-components-panel-bg-blur">
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="h-6 w-6 overflow-hidden rounded-full">
|
||||
<Avatar
|
||||
@ -156,7 +156,7 @@ export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, o
|
||||
'relative z-10 flex-1 rounded-xl border border-components-chat-input-border bg-components-panel-bg-blur pb-[4px] shadow-md',
|
||||
)}
|
||||
>
|
||||
<div className="relative pl-[9px] pt-[4px]">
|
||||
<div className="relative pt-[4px] pl-[9px]">
|
||||
<MentionInput
|
||||
value={content}
|
||||
onChange={setContent}
|
||||
|
||||
@ -44,8 +44,8 @@ const CommentPreview: FC<CommentPreviewProps> = ({ comment, onClick }) => {
|
||||
|
||||
<div className="mb-2 flex items-start">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<div className="system-sm-medium truncate text-text-primary">{comment.created_by_account.name}</div>
|
||||
<div className="system-2xs-regular shrink-0 text-text-tertiary">
|
||||
<div className="truncate system-sm-medium text-text-primary">{comment.created_by_account.name}</div>
|
||||
<div className="shrink-0 system-2xs-regular text-text-tertiary">
|
||||
{formatTimeFromNow(comment.updated_at * 1000)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
export { CommentIcon } from './comment-icon'
|
||||
export { CommentInput } from './comment-input'
|
||||
export { CommentCursor } from './cursor'
|
||||
export { MentionInput } from './mention-input'
|
||||
export { CommentThread } from './thread'
|
||||
@ -3,7 +3,6 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type { UserProfile } from '@/service/workflow-comment'
|
||||
import { RiArrowUpLine, RiAtLine, RiLoader2Line } from '@remixicon/react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import {
|
||||
forwardRef,
|
||||
memo,
|
||||
@ -21,6 +20,7 @@ import Textarea from 'react-textarea-autosize'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import Button from '@/app/components/base/button'
|
||||
import EnterKey from '@/app/components/base/icons/src/public/common/EnterKey'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { fetchMentionableUsers } from '@/service/workflow-comment'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useStore, useWorkflowStore } from '../store'
|
||||
@ -511,7 +511,7 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(({
|
||||
<div
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-0 z-0 overflow-hidden whitespace-pre-wrap break-words p-1 leading-6',
|
||||
'inset-0 pointer-events-none absolute z-0 overflow-hidden p-1 leading-6 break-words whitespace-pre-wrap',
|
||||
'body-lg-regular text-text-primary',
|
||||
)}
|
||||
style={{ paddingRight, paddingBottom }}
|
||||
@ -528,7 +528,7 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(({
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
className={cn(
|
||||
'body-lg-regular relative z-10 w-full resize-none bg-transparent p-1 leading-6 text-transparent caret-primary-500 outline-none',
|
||||
'relative z-10 w-full resize-none bg-transparent p-1 body-lg-regular leading-6 text-transparent caret-primary-500 outline-none',
|
||||
'placeholder:text-text-tertiary',
|
||||
)}
|
||||
style={{ paddingRight, paddingBottom }}
|
||||
@ -546,7 +546,7 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(({
|
||||
{!isEditing && (
|
||||
<div
|
||||
ref={setActionContainerRef}
|
||||
className="absolute bottom-0 right-1 z-20 flex items-end gap-1"
|
||||
className="absolute right-1 bottom-0 z-20 flex items-end gap-1"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
@ -575,7 +575,7 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(({
|
||||
{isEditing && (
|
||||
<div
|
||||
ref={setActionContainerRef}
|
||||
className="absolute bottom-0 left-1 right-1 z-20 flex items-end justify-between"
|
||||
className="absolute right-1 bottom-0 left-1 z-20 flex items-end justify-between"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
@ -615,7 +615,7 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(({
|
||||
|
||||
{showMentionDropdown && filteredMentionUsers.length > 0 && typeof document !== 'undefined' && createPortal(
|
||||
<div
|
||||
className="bg-components-panel-bg/95 fixed z-[9999] max-h-[248px] w-[280px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border shadow-lg backdrop-blur-[10px]"
|
||||
className="fixed z-[9999] max-h-[248px] w-[280px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg/95 shadow-lg backdrop-blur-[10px]"
|
||||
style={{
|
||||
left: dropdownPosition.x,
|
||||
[dropdownPosition.placement === 'top' ? 'bottom' : 'top']: dropdownPosition.placement === 'top'
|
||||
@ -628,7 +628,7 @@ const MentionInputInner = forwardRef<HTMLTextAreaElement, MentionInputProps>(({
|
||||
<div
|
||||
key={user.id}
|
||||
className={cn(
|
||||
'flex cursor-pointer items-center gap-2 rounded-md py-1 pl-2 pr-3 hover:bg-state-base-hover',
|
||||
'flex cursor-pointer items-center gap-2 rounded-md py-1 pr-3 pl-2 hover:bg-state-base-hover',
|
||||
index === selectedMentionIndex && 'bg-state-base-hover',
|
||||
)}
|
||||
onClick={() => insertMention(user)}
|
||||
|
||||
@ -3,18 +3,22 @@
|
||||
import type { FC, ReactNode } from 'react'
|
||||
import type { WorkflowCommentDetail, WorkflowCommentDetailReply } from '@/service/workflow-comment'
|
||||
import { RiArrowDownSLine, RiArrowUpSLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiDeleteBinLine, RiMoreFill } from '@remixicon/react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useReactFlow, useViewport } from 'reactflow'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import InlineDeleteConfirm from '@/app/components/base/inline-delete-confirm'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/app/components/base/ui/dropdown-menu'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
|
||||
import { getUserColor } from '@/app/components/workflow/collaboration/utils/user-color'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useStore } from '../store'
|
||||
import { MentionInput } from './mention-input'
|
||||
@ -133,7 +137,7 @@ const ThreadMessage: FC<{
|
||||
<span className="system-sm-medium text-text-primary">{authorName}</span>
|
||||
<span className="system-2xs-regular text-text-tertiary">{formatTimeFromNow(createdAt * 1000)}</span>
|
||||
</div>
|
||||
<div className="system-sm-regular mt-1 whitespace-pre-wrap break-words text-text-secondary">
|
||||
<div className="mt-1 system-sm-regular break-words whitespace-pre-wrap text-text-secondary">
|
||||
{highlightedContent}
|
||||
</div>
|
||||
</div>
|
||||
@ -369,71 +373,75 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
||||
<div className="flex items-center justify-between rounded-t-2xl border-b border-components-panel-border bg-components-panel-bg-blur px-4 py-3">
|
||||
<div
|
||||
id="comment-thread-title"
|
||||
className="font-semibold uppercase text-text-primary"
|
||||
className="font-semibold text-text-primary uppercase"
|
||||
>
|
||||
{t('comments.panelTitle', { ns: 'workflow' })}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Tooltip
|
||||
popupContent={t('comments.aria.deleteComment', { ns: 'workflow' })}
|
||||
position="top"
|
||||
popupClassName="!px-2 !py-1.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
disabled={loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onDelete}
|
||||
aria-label={t('comments.aria.deleteComment', { ns: 'workflow' })}
|
||||
>
|
||||
<RiDeleteBinLine className="h-4 w-4" />
|
||||
</button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<button
|
||||
type="button"
|
||||
disabled={loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onDelete}
|
||||
aria-label={t('comments.aria.deleteComment', { ns: 'workflow' })}
|
||||
>
|
||||
<RiDeleteBinLine className="h-4 w-4" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
|
||||
{t('comments.aria.deleteComment', { ns: 'workflow' })}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
popupContent={t('comments.aria.resolveComment', { ns: 'workflow' })}
|
||||
position="top"
|
||||
popupClassName="!px-2 !py-1.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
disabled={comment.resolved || loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onResolve}
|
||||
aria-label={t('comments.aria.resolveComment', { ns: 'workflow' })}
|
||||
>
|
||||
{comment.resolved ? <RiCheckboxCircleFill className="h-4 w-4" /> : <RiCheckboxCircleLine className="h-4 w-4" />}
|
||||
</button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<button
|
||||
type="button"
|
||||
disabled={comment.resolved || loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onResolve}
|
||||
aria-label={t('comments.aria.resolveComment', { ns: 'workflow' })}
|
||||
>
|
||||
{comment.resolved ? <RiCheckboxCircleFill className="h-4 w-4" /> : <RiCheckboxCircleLine className="h-4 w-4" />}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
|
||||
{t('comments.aria.resolveComment', { ns: 'workflow' })}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Divider type="vertical" className="h-3.5" />
|
||||
<Tooltip
|
||||
popupContent={t('comments.aria.previousComment', { ns: 'workflow' })}
|
||||
position="top"
|
||||
popupClassName="!px-2 !py-1.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!canGoPrev || loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onPrev}
|
||||
aria-label={t('comments.aria.previousComment', { ns: 'workflow' })}
|
||||
>
|
||||
<RiArrowUpSLine className="h-4 w-4" />
|
||||
</button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!canGoPrev || loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onPrev}
|
||||
aria-label={t('comments.aria.previousComment', { ns: 'workflow' })}
|
||||
>
|
||||
<RiArrowUpSLine className="h-4 w-4" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
|
||||
{t('comments.aria.previousComment', { ns: 'workflow' })}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
popupContent={t('comments.aria.nextComment', { ns: 'workflow' })}
|
||||
position="top"
|
||||
popupClassName="!px-2 !py-1.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!canGoNext || loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onNext}
|
||||
aria-label={t('comments.aria.nextComment', { ns: 'workflow' })}
|
||||
>
|
||||
<RiArrowDownSLine className="h-4 w-4" />
|
||||
</button>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!canGoNext || loading}
|
||||
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
|
||||
onClick={onNext}
|
||||
aria-label={t('comments.aria.nextComment', { ns: 'workflow' })}
|
||||
>
|
||||
<RiArrowDownSLine className="h-4 w-4" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
|
||||
{t('comments.aria.nextComment', { ns: 'workflow' })}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<button
|
||||
type="button"
|
||||
@ -470,88 +478,78 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
||||
className="group relative -mx-4 rounded-lg px-4 py-2 transition-colors hover:bg-components-panel-on-panel-item-bg-hover"
|
||||
>
|
||||
{isOwnReply && !isReplyEditing && (
|
||||
<PortalToFollowElem
|
||||
placement="bottom-end"
|
||||
open={activeReplyMenuId === reply.id}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setDeletingReplyId(null)
|
||||
setActiveReplyMenuId(null)
|
||||
}
|
||||
}}
|
||||
<div
|
||||
className={cn(
|
||||
'absolute top-1 right-1 gap-1',
|
||||
activeReplyMenuId === reply.id ? 'flex' : 'hidden group-hover:flex',
|
||||
)}
|
||||
data-reply-menu
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'absolute right-1 top-1 gap-1',
|
||||
activeReplyMenuId === reply.id ? 'flex' : 'hidden group-hover:flex',
|
||||
)}
|
||||
data-reply-menu
|
||||
<DropdownMenu
|
||||
open={activeReplyMenuId === reply.id}
|
||||
onOpenChange={(open) => {
|
||||
if (!open)
|
||||
setDeletingReplyId(null)
|
||||
setActiveReplyMenuId(open ? reply.id : null)
|
||||
}}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setDeletingReplyId(null)
|
||||
setActiveReplyMenuId(prev => prev === reply.id ? null : reply.id)
|
||||
}}
|
||||
aria-label={t('comments.aria.replyActions', { ns: 'workflow' })}
|
||||
>
|
||||
<RiMoreFill className="h-4 w-4" />
|
||||
</button>
|
||||
</PortalToFollowElemTrigger>
|
||||
</div>
|
||||
<PortalToFollowElemContent
|
||||
className="z-[100] w-36 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[10px]"
|
||||
data-reply-menu
|
||||
>
|
||||
{/* Menu buttons - hidden when showing delete confirm */}
|
||||
<div className={cn(deletingReplyId === reply.id ? 'hidden' : 'block')}>
|
||||
<button
|
||||
className="flex w-full items-center justify-start rounded-t-xl px-3 py-2 text-left text-sm text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleStartEdit(reply)
|
||||
}}
|
||||
>
|
||||
{t('comments.actions.editReply', { ns: 'workflow' })}
|
||||
</button>
|
||||
<button
|
||||
className="text-negative flex w-full items-center justify-start rounded-b-xl px-3 py-2 text-left text-sm text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (onReplyDeleteDirect) {
|
||||
setDeletingReplyId(reply.id)
|
||||
}
|
||||
else {
|
||||
setActiveReplyMenuId(null)
|
||||
onReplyDelete?.(reply.id)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('comments.actions.deleteReply', { ns: 'workflow' })}
|
||||
</button>
|
||||
</div>
|
||||
<DropdownMenuTrigger
|
||||
className="flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
aria-label={t('comments.aria.replyActions', { ns: 'workflow' })}
|
||||
>
|
||||
<RiMoreFill className="h-4 w-4" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
placement="bottom-end"
|
||||
sideOffset={4}
|
||||
popupClassName="z-[100] w-36 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[10px]"
|
||||
data-reply-menu
|
||||
>
|
||||
<div className={cn(deletingReplyId === reply.id ? 'hidden' : 'block')}>
|
||||
<button
|
||||
className="flex w-full items-center justify-start rounded-t-xl px-3 py-2 text-left text-sm text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleStartEdit(reply)
|
||||
}}
|
||||
>
|
||||
{t('comments.actions.editReply', { ns: 'workflow' })}
|
||||
</button>
|
||||
<button
|
||||
className="text-negative flex w-full items-center justify-start rounded-b-xl px-3 py-2 text-left text-sm text-text-secondary hover:bg-state-base-hover"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (onReplyDeleteDirect) {
|
||||
setDeletingReplyId(reply.id)
|
||||
}
|
||||
else {
|
||||
setActiveReplyMenuId(null)
|
||||
onReplyDelete?.(reply.id)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('comments.actions.deleteReply', { ns: 'workflow' })}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Delete confirmation - shown when deletingReplyId matches */}
|
||||
<div className={cn(deletingReplyId === reply.id ? 'block' : 'hidden')}>
|
||||
<InlineDeleteConfirm
|
||||
title={t('comments.actions.deleteReply', { ns: 'workflow' })}
|
||||
onConfirm={() => {
|
||||
setDeletingReplyId(null)
|
||||
setActiveReplyMenuId(null)
|
||||
onReplyDeleteDirect?.(reply.id)
|
||||
}}
|
||||
onCancel={() => {
|
||||
setDeletingReplyId(null)
|
||||
}}
|
||||
className="m-0 w-full border-0 shadow-none"
|
||||
/>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
<div className={cn(deletingReplyId === reply.id ? 'block' : 'hidden')}>
|
||||
<InlineDeleteConfirm
|
||||
title={t('comments.actions.deleteReply', { ns: 'workflow' })}
|
||||
onConfirm={() => {
|
||||
setDeletingReplyId(null)
|
||||
setActiveReplyMenuId(null)
|
||||
onReplyDeleteDirect?.(reply.id)
|
||||
}}
|
||||
onCancel={() => {
|
||||
setDeletingReplyId(null)
|
||||
}}
|
||||
className="m-0 w-full border-0 shadow-none"
|
||||
/>
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)}
|
||||
{isReplyEditing
|
||||
? (
|
||||
@ -599,7 +597,7 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
||||
)}
|
||||
</div>
|
||||
{loading && (
|
||||
<div className="bg-components-panel-bg/70 absolute inset-0 z-30 flex items-center justify-center text-sm text-text-tertiary">
|
||||
<div className="inset-0 absolute z-30 flex items-center justify-center bg-components-panel-bg/70 text-sm text-text-tertiary">
|
||||
{t('comments.loading', { ns: 'workflow' })}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -5,11 +5,11 @@ import { useEffect, useState } from 'react'
|
||||
import { useReactFlow } from 'reactflow'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/app/components/base/ui/popover'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { getAvatar } from '@/service/common'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@ -111,7 +111,7 @@ const OnlineUsers = () => {
|
||||
className={cn(
|
||||
'flex h-8 items-center rounded-full border-[0.5px] border-components-panel-border',
|
||||
'bg-components-panel-bg py-1 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[10px]',
|
||||
hasCounter ? 'min-w-[87px] gap-px pl-1 pr-1.5' : 'gap-1 px-1.5',
|
||||
hasCounter ? 'min-w-[87px] gap-px pr-1.5 pl-1' : 'gap-1 px-1.5',
|
||||
)}
|
||||
>
|
||||
<div className="flex h-6 items-center">
|
||||
@ -120,117 +120,104 @@ const OnlineUsers = () => {
|
||||
const isCurrentUser = user.user_id === currentUserId
|
||||
const userColor = isCurrentUser ? undefined : getUserColor(user.user_id)
|
||||
return (
|
||||
<Tooltip
|
||||
key={`${user.sid}-${index}`}
|
||||
popupContent={renderDisplayName(
|
||||
user,
|
||||
'system-xs-medium text-text-secondary',
|
||||
'text-text-quaternary',
|
||||
)}
|
||||
position="bottom"
|
||||
triggerMethod="hover"
|
||||
needsDelay={false}
|
||||
asChild
|
||||
popupClassName="flex h-[28px] w-[85px] items-center justify-center gap-1 rounded-md border-[0.5px] border-components-panel-border bg-components-tooltip-bg px-3 py-[6px] shadow-lg shadow-shadow-shadow-5 backdrop-blur-[10px]"
|
||||
noDecoration
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex size-6 items-center justify-center',
|
||||
index > 0 && '-ml-1.5',
|
||||
!isCurrentUser && 'cursor-pointer transition-transform hover:scale-110',
|
||||
)}
|
||||
style={{ zIndex: visibleUsers.length - index }}
|
||||
onClick={() => !isCurrentUser && jumpToUserCursor(user.user_id)}
|
||||
<Tooltip key={`${user.sid}-${index}`}>
|
||||
<TooltipTrigger>
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex size-6 items-center justify-center',
|
||||
index > 0 && '-ml-1.5',
|
||||
!isCurrentUser && 'cursor-pointer transition-transform hover:scale-110',
|
||||
)}
|
||||
style={{ zIndex: visibleUsers.length - index }}
|
||||
onClick={() => !isCurrentUser && jumpToUserCursor(user.user_id)}
|
||||
>
|
||||
<Avatar
|
||||
name={user.username || 'User'}
|
||||
avatar={getAvatarUrl(user)}
|
||||
size={24}
|
||||
className="ring-1 ring-components-panel-bg"
|
||||
backgroundColor={userColor}
|
||||
/>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
placement="bottom"
|
||||
sideOffset={4}
|
||||
popupClassName="flex h-[28px] w-[85px] items-center justify-center gap-1 rounded-md border-[0.5px] border-components-panel-border bg-components-tooltip-bg px-3 py-[6px] shadow-lg shadow-shadow-shadow-5 backdrop-blur-[10px]"
|
||||
>
|
||||
<Avatar
|
||||
name={user.username || 'User'}
|
||||
avatar={getAvatarUrl(user)}
|
||||
size={24}
|
||||
className="ring-1 ring-components-panel-bg"
|
||||
backgroundColor={userColor}
|
||||
/>
|
||||
</div>
|
||||
{renderDisplayName(
|
||||
user,
|
||||
'system-xs-medium text-text-secondary',
|
||||
'text-text-quaternary',
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
{remainingCount > 0 && (
|
||||
<PortalToFollowElem
|
||||
open={dropdownOpen}
|
||||
onOpenChange={setDropdownOpen}
|
||||
placement="bottom-start"
|
||||
offset={{
|
||||
mainAxis: 8,
|
||||
crossAxis: -48,
|
||||
}}
|
||||
>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={() => setDropdownOpen(prev => !prev)}
|
||||
asChild
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-6 w-6 cursor-pointer select-none items-center justify-center rounded-full bg-components-icon-bg-midnight-solid text-[10px] font-semibold uppercase leading-[12px] text-white ring-1 ring-components-panel-bg',
|
||||
visibleUsers.length > 0 && '-ml-1',
|
||||
)}
|
||||
>
|
||||
+
|
||||
{remainingCount}
|
||||
<Popover open={dropdownOpen} onOpenChange={setDropdownOpen}>
|
||||
<PopoverTrigger
|
||||
render={(
|
||||
<div className="flex items-center gap-1">
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-components-icon-bg-midnight-solid text-[10px] leading-[12px] font-semibold text-white uppercase ring-1 ring-components-panel-bg select-none',
|
||||
visibleUsers.length > 0 && '-ml-1',
|
||||
)}
|
||||
>
|
||||
+
|
||||
{remainingCount}
|
||||
</div>
|
||||
<ChevronDownIcon className="h-3 w-3 cursor-pointer text-gray-500" />
|
||||
</div>
|
||||
<ChevronDownIcon className="h-3 w-3 cursor-pointer text-gray-500" />
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent
|
||||
)}
|
||||
/>
|
||||
<PopoverContent
|
||||
placement="bottom-start"
|
||||
sideOffset={8}
|
||||
alignOffset={-48}
|
||||
className="z-[9999]"
|
||||
popupClassName={cn(
|
||||
'mt-1.5 flex max-h-[200px] w-[240px] flex-col overflow-y-auto',
|
||||
'rounded-xl border-[0.5px] border-components-panel-border',
|
||||
'bg-components-panel-bg-blur p-1 shadow-lg shadow-shadow-shadow-5 backdrop-blur-[10px]',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'mt-1.5',
|
||||
'flex flex-col',
|
||||
'max-h-[200px] w-[240px] overflow-y-auto',
|
||||
'rounded-xl border-[0.5px] border-components-panel-border',
|
||||
'bg-components-panel-bg-blur p-1',
|
||||
'shadow-lg shadow-shadow-shadow-5',
|
||||
'backdrop-blur-[10px]',
|
||||
)}
|
||||
>
|
||||
{onlineUsers.map((user) => {
|
||||
const isCurrentUser = user.user_id === currentUserId
|
||||
const userColor = isCurrentUser ? undefined : getUserColor(user.user_id)
|
||||
return (
|
||||
<div
|
||||
key={user.sid}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-lg px-3 py-1.5',
|
||||
!isCurrentUser && 'cursor-pointer hover:bg-components-panel-on-panel-item-bg-hover',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!isCurrentUser) {
|
||||
jumpToUserCursor(user.user_id)
|
||||
setDropdownOpen(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="relative">
|
||||
<Avatar
|
||||
name={user.username || 'User'}
|
||||
avatar={getAvatarUrl(user)}
|
||||
size={24}
|
||||
backgroundColor={userColor}
|
||||
/>
|
||||
</div>
|
||||
{renderDisplayName(
|
||||
user,
|
||||
'system-xs-medium text-text-secondary',
|
||||
'text-text-tertiary',
|
||||
)}
|
||||
{onlineUsers.map((user) => {
|
||||
const isCurrentUser = user.user_id === currentUserId
|
||||
const userColor = isCurrentUser ? undefined : getUserColor(user.user_id)
|
||||
return (
|
||||
<div
|
||||
key={user.sid}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-lg px-3 py-1.5',
|
||||
!isCurrentUser && 'cursor-pointer hover:bg-components-panel-on-panel-item-bg-hover',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!isCurrentUser) {
|
||||
jumpToUserCursor(user.user_id)
|
||||
setDropdownOpen(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="relative">
|
||||
<Avatar
|
||||
name={user.username || 'User'}
|
||||
avatar={getAvatarUrl(user)}
|
||||
size={24}
|
||||
backgroundColor={userColor}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
{renderDisplayName(
|
||||
user,
|
||||
'system-xs-medium text-text-secondary',
|
||||
'text-text-tertiary',
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -89,7 +89,7 @@ export const useLeaderRestoreListener = () => {
|
||||
ns: 'workflow',
|
||||
userName: data.initiatorName,
|
||||
versionName: data.versionName || data.versionId,
|
||||
}), { duration: 3000 })
|
||||
}), { timeout: 3000 })
|
||||
performRestore(data)
|
||||
})
|
||||
|
||||
@ -102,7 +102,7 @@ export const useLeaderRestoreListener = () => {
|
||||
ns: 'workflow',
|
||||
userName: data.initiatorName,
|
||||
versionName: data.versionName || data.versionId,
|
||||
}), { duration: 3000 })
|
||||
}), { timeout: 3000 })
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { UserProfile, WorkflowCommentDetail, WorkflowCommentList } from '@/service/workflow-comment'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useReactFlow } from 'reactflow'
|
||||
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { createWorkflowComment, createWorkflowCommentReply, deleteWorkflowComment, deleteWorkflowCommentReply, fetchWorkflowComment, fetchWorkflowComments, resolveWorkflowComment, updateWorkflowComment, updateWorkflowCommentReply } from '@/service/workflow-comment'
|
||||
import { useStore } from '../store'
|
||||
import { ControlMode } from '../types'
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useStore, useWorkflowStore } from '../store'
|
||||
import { ControlMode } from '../types'
|
||||
import { useEdgesInteractionsWithoutSync } from './use-edges-interactions-without-sync'
|
||||
@ -29,6 +30,7 @@ export const useWorkflowMoveMode = () => {
|
||||
const setControlMode = useStore(s => s.setControlMode)
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const { handleSelectionCancel } = useSelectionInteractions()
|
||||
const isCommentModeAvailable = useGlobalPublicStore(s => s.systemFeatures.enable_collaboration_mode)
|
||||
|
||||
const handleModePointer = useCallback(() => {
|
||||
if (getNodesReadOnly())
|
||||
@ -45,8 +47,18 @@ export const useWorkflowMoveMode = () => {
|
||||
handleSelectionCancel()
|
||||
}, [getNodesReadOnly, handleSelectionCancel, setControlMode])
|
||||
|
||||
const handleModeComment = useCallback(() => {
|
||||
if (getNodesReadOnly() || !isCommentModeAvailable)
|
||||
return
|
||||
|
||||
setControlMode(ControlMode.Comment)
|
||||
handleSelectionCancel()
|
||||
}, [getNodesReadOnly, handleSelectionCancel, isCommentModeAvailable, setControlMode])
|
||||
|
||||
return {
|
||||
handleModePointer,
|
||||
handleModeHand,
|
||||
handleModeComment,
|
||||
isCommentModeAvailable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,8 +54,11 @@ import { cn } from '@/utils/classnames'
|
||||
import CandidateNode from './candidate-node'
|
||||
import UserCursors from './collaboration/components/user-cursors'
|
||||
import { collaborationManager } from './collaboration/core/collaboration-manager'
|
||||
import { CommentCursor, CommentIcon, CommentInput, CommentThread } from './comment'
|
||||
import CommentManager from './comment-manager'
|
||||
import { CommentIcon } from './comment/comment-icon'
|
||||
import { CommentInput } from './comment/comment-input'
|
||||
import { CommentCursor } from './comment/cursor'
|
||||
import { CommentThread } from './comment/thread'
|
||||
import {
|
||||
CUSTOM_EDGE,
|
||||
CUSTOM_NODE,
|
||||
@ -545,7 +548,7 @@ export const Workflow: FC<WorkflowProps> = memo(({
|
||||
<CandidateNode />
|
||||
<CommentManager />
|
||||
<div
|
||||
className="pointer-events-none absolute left-0 top-0 z-[60] flex w-12 items-center justify-center p-1 pl-2"
|
||||
className="pointer-events-none absolute top-0 left-0 z-[60] flex w-12 items-center justify-center p-1 pl-2"
|
||||
style={{ height: controlHeight }}
|
||||
>
|
||||
<Control />
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { WorkflowCommentList } from '@/service/workflow-comment'
|
||||
import { RiCheckboxCircleFill, RiCheckboxCircleLine, RiCheckLine, RiCloseLine, RiFilter3Line } from '@remixicon/react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { memo, useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
@ -12,6 +11,7 @@ import { useStore } from '@/app/components/workflow/store'
|
||||
import { ControlMode } from '@/app/components/workflow/types'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { resolveWorkflowComment } from '@/service/workflow-comment'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -68,7 +68,7 @@ const CommentsPanel = () => {
|
||||
return (
|
||||
<div className={cn('relative flex h-full w-[420px] flex-col rounded-l-2xl border border-components-panel-border bg-components-panel-bg')}>
|
||||
<div className="flex items-center justify-between p-4 pb-2">
|
||||
<div className="system-xl-semibold font-semibold leading-6 text-text-primary">{t('comments.panelTitle', { ns: 'workflow' })}</div>
|
||||
<div className="system-xl-semibold leading-6 font-semibold text-text-primary">{t('comments.panelTitle', { ns: 'workflow' })}</div>
|
||||
<div className="relative flex items-center gap-2">
|
||||
<button
|
||||
className={cn(
|
||||
@ -85,7 +85,7 @@ const CommentsPanel = () => {
|
||||
/>
|
||||
</button>
|
||||
{showFilter && (
|
||||
<div className="absolute right-10 top-9 z-50 min-w-[184px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-[10px]">
|
||||
<div className="absolute top-9 right-10 z-50 min-w-[184px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-[10px]">
|
||||
<button
|
||||
className={cn('flex w-full items-center justify-between rounded-md px-2 py-2 text-left text-sm hover:bg-state-base-hover', !showOnlyMine && 'bg-components-panel-on-panel-item-bg')}
|
||||
onClick={() => {
|
||||
@ -94,7 +94,7 @@ const CommentsPanel = () => {
|
||||
}}
|
||||
>
|
||||
<span className="text-text-secondary">All</span>
|
||||
{!showOnlyMine && <RiCheckLine className="h-4 w-4 text-primary-600" />}
|
||||
{!showOnlyMine && <RiCheckLine className="text-primary-600 h-4 w-4" />}
|
||||
</button>
|
||||
<button
|
||||
className={cn('mt-1 flex w-full items-center justify-between rounded-md px-2 py-2 text-left text-sm hover:bg-state-base-hover', showOnlyMine && 'bg-components-panel-on-panel-item-bg')}
|
||||
@ -104,7 +104,7 @@ const CommentsPanel = () => {
|
||||
}}
|
||||
>
|
||||
<span className="text-text-secondary">Only your threads</span>
|
||||
{showOnlyMine && <RiCheckLine className="h-4 w-4 text-primary-600" />}
|
||||
{showOnlyMine && <RiCheckLine className="text-primary-600 h-4 w-4" />}
|
||||
</button>
|
||||
<Divider type="horizontal" className="my-1" />
|
||||
<div
|
||||
@ -116,7 +116,7 @@ const CommentsPanel = () => {
|
||||
<span className="text-sm text-text-secondary">Show resolved</span>
|
||||
<Switch
|
||||
size="md"
|
||||
defaultValue={showResolvedComments}
|
||||
value={showResolvedComments}
|
||||
onChange={(checked) => {
|
||||
setShowResolvedComments(checked)
|
||||
}}
|
||||
@ -172,14 +172,14 @@ const CommentsPanel = () => {
|
||||
{/* Header row: creator + time */}
|
||||
<div className="flex items-start">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<div className="system-sm-medium truncate text-text-primary">{c.created_by_account.name}</div>
|
||||
<div className="system-2xs-regular shrink-0 text-text-tertiary">
|
||||
<div className="truncate system-sm-medium text-text-primary">{c.created_by_account.name}</div>
|
||||
<div className="shrink-0 system-2xs-regular text-text-tertiary">
|
||||
{formatTimeFromNow(c.updated_at * 1000)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Content */}
|
||||
<div className="system-sm-regular mt-1 line-clamp-3 break-words text-text-secondary">{c.content}</div>
|
||||
<div className="mt-1 line-clamp-3 system-sm-regular break-words text-text-secondary">{c.content}</div>
|
||||
{/* Footer */}
|
||||
{c.reply_count > 0 && (
|
||||
<div className="mt-2 flex items-center justify-between">
|
||||
@ -195,7 +195,7 @@ const CommentsPanel = () => {
|
||||
)
|
||||
})}
|
||||
{!loading && filteredSorted.length === 0 && (
|
||||
<div className="system-sm-regular mt-6 text-center text-text-tertiary">{t('comments.noComments', { ns: 'workflow' })}</div>
|
||||
<div className="mt-6 text-center system-sm-regular text-text-tertiary">{t('comments.noComments', { ns: 'workflow' })}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -145,7 +145,7 @@ export const VersionHistoryPanel = ({
|
||||
|
||||
const emitRestoreIntent = useCallback(async (item: VersionHistory) => {
|
||||
try {
|
||||
const { collaborationManager } = await import('../../collaboration')
|
||||
const { collaborationManager } = await import('../../collaboration/core/collaboration-manager')
|
||||
collaborationManager.emitRestoreIntent({
|
||||
versionId: item.id,
|
||||
versionName: item.marked_name,
|
||||
@ -160,7 +160,7 @@ export const VersionHistoryPanel = ({
|
||||
|
||||
const emitRestoreComplete = useCallback(async (item: VersionHistory, success: boolean, errorMessage?: string) => {
|
||||
try {
|
||||
const { collaborationManager } = await import('../../collaboration')
|
||||
const { collaborationManager } = await import('../../collaboration/core/collaboration-manager')
|
||||
collaborationManager.emitRestoreComplete({
|
||||
versionId: item.id,
|
||||
success,
|
||||
@ -178,7 +178,7 @@ export const VersionHistoryPanel = ({
|
||||
if (!appId)
|
||||
return
|
||||
|
||||
const { collaborationManager } = await import('../../collaboration')
|
||||
const { collaborationManager } = await import('../../collaboration/core/collaboration-manager')
|
||||
collaborationManager.emitWorkflowUpdate(appId)
|
||||
}
|
||||
catch (error) {
|
||||
@ -259,7 +259,7 @@ export const VersionHistoryPanel = ({
|
||||
return (
|
||||
<div className="flex h-full w-[268px] flex-col rounded-l-2xl border-y-[0.5px] border-l-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5">
|
||||
<div className="flex items-center gap-x-2 px-4 pt-3">
|
||||
<div className="system-xl-semibold flex-1 py-1 text-text-primary">{t('versionHistory.title', { ns: 'workflow' })}</div>
|
||||
<div className="flex-1 py-1 system-xl-semibold text-text-primary">{t('versionHistory.title', { ns: 'workflow' })}</div>
|
||||
<Filter
|
||||
filterValue={filterValue}
|
||||
isOnlyShowNamedVersions={isOnlyShowNamedVersions}
|
||||
@ -315,7 +315,7 @@ export const VersionHistoryPanel = ({
|
||||
? <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-text-accent" />
|
||||
: <RiArrowDownDoubleLine className="h-3.5 w-3.5 text-text-accent" />}
|
||||
</div>
|
||||
<div className="system-xs-medium-uppercase py-px text-text-accent">
|
||||
<div className="py-px system-xs-medium-uppercase text-text-accent">
|
||||
{t('common.loadMore', { ns: 'workflow' })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -78,6 +78,11 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx": {
|
||||
"style/no-trailing-spaces": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx": {
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
@ -1366,10 +1371,18 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/apps/__tests__/list.spec.tsx": {
|
||||
"unused-imports/no-unused-vars": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/apps/app-card.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 3
|
||||
},
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/component-hook-factories": {
|
||||
"count": 1
|
||||
},
|
||||
@ -8330,7 +8343,7 @@
|
||||
},
|
||||
"app/components/workflow/hooks/index.ts": {
|
||||
"no-barrel-files/no-barrel-files": {
|
||||
"count": 25
|
||||
"count": 29
|
||||
}
|
||||
},
|
||||
"app/components/workflow/hooks/use-checklist.ts": {
|
||||
@ -8346,6 +8359,11 @@
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"app/components/workflow/hooks/use-edges-interactions.ts": {
|
||||
"perfectionist/sort-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/workflow/hooks/use-helpline.ts": {
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
@ -9019,6 +9037,9 @@
|
||||
}
|
||||
},
|
||||
"app/components/workflow/nodes/_base/node.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 5
|
||||
},
|
||||
@ -11610,6 +11631,17 @@
|
||||
"count": 4
|
||||
}
|
||||
},
|
||||
"contract/console/apps.ts": {
|
||||
"style/eol-last": {
|
||||
"count": 1
|
||||
},
|
||||
"style/no-multiple-empty-lines": {
|
||||
"count": 1
|
||||
},
|
||||
"style/no-trailing-spaces": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"context/app-context-provider.tsx": {
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
|
||||
@ -1,31 +1,32 @@
|
||||
import type {
|
||||
CreateCommentParams,
|
||||
CreateReplyParams,
|
||||
UpdateCommentParams,
|
||||
WorkflowCommentCreateRes,
|
||||
WorkflowCommentDetail,
|
||||
WorkflowCommentList,
|
||||
WorkflowCommentReply,
|
||||
WorkflowCommentResolveRes,
|
||||
WorkflowCommentUpdateRes,
|
||||
CreateCommentParams as ContractCreateCommentParams,
|
||||
CreateReplyParams as ContractCreateReplyParams,
|
||||
UpdateCommentParams as ContractUpdateCommentParams,
|
||||
UserProfile as ContractUserProfile,
|
||||
WorkflowCommentCreateRes as ContractWorkflowCommentCreateRes,
|
||||
WorkflowCommentDetail as ContractWorkflowCommentDetail,
|
||||
WorkflowCommentDetailMention as ContractWorkflowCommentDetailMention,
|
||||
WorkflowCommentDetailReply as ContractWorkflowCommentDetailReply,
|
||||
WorkflowCommentList as ContractWorkflowCommentList,
|
||||
WorkflowCommentReply as ContractWorkflowCommentReply,
|
||||
WorkflowCommentResolveRes as ContractWorkflowCommentResolveRes,
|
||||
WorkflowCommentUpdateRes as ContractWorkflowCommentUpdateRes,
|
||||
} from '@/contract/console/workflow-comment'
|
||||
import type { CommonResponse } from '@/models/common'
|
||||
import { consoleClient } from './client'
|
||||
|
||||
export type {
|
||||
CreateCommentParams,
|
||||
CreateReplyParams,
|
||||
UpdateCommentParams,
|
||||
UserProfile,
|
||||
WorkflowCommentCreateRes,
|
||||
WorkflowCommentDetail,
|
||||
WorkflowCommentDetailMention,
|
||||
WorkflowCommentDetailReply,
|
||||
WorkflowCommentList,
|
||||
WorkflowCommentReply,
|
||||
WorkflowCommentResolveRes,
|
||||
WorkflowCommentUpdateRes,
|
||||
} from '@/contract/console/workflow-comment'
|
||||
export type CreateCommentParams = ContractCreateCommentParams
|
||||
export type CreateReplyParams = ContractCreateReplyParams
|
||||
export type UpdateCommentParams = ContractUpdateCommentParams
|
||||
export type UserProfile = ContractUserProfile
|
||||
export type WorkflowCommentCreateRes = ContractWorkflowCommentCreateRes
|
||||
export type WorkflowCommentDetail = ContractWorkflowCommentDetail
|
||||
export type WorkflowCommentDetailMention = ContractWorkflowCommentDetailMention
|
||||
export type WorkflowCommentDetailReply = ContractWorkflowCommentDetailReply
|
||||
export type WorkflowCommentList = ContractWorkflowCommentList
|
||||
export type WorkflowCommentReply = ContractWorkflowCommentReply
|
||||
export type WorkflowCommentResolveRes = ContractWorkflowCommentResolveRes
|
||||
export type WorkflowCommentUpdateRes = ContractWorkflowCommentUpdateRes
|
||||
|
||||
export const fetchWorkflowComments = async (appId: string): Promise<WorkflowCommentList[]> => {
|
||||
const response = await consoleClient.workflowComments.list({
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { BlockEnum, ConversationVariable, EnvironmentVariable } from '@/app/components/workflow/types'
|
||||
import type { WorkflowDraftFeaturesPayload } from '@/contract/console/workflow'
|
||||
import type { WorkflowDraftFeaturesPayload as ContractWorkflowDraftFeaturesPayload } from '@/contract/console/workflow'
|
||||
import type { CommonResponse } from '@/models/common'
|
||||
import type { FlowType } from '@/types/common'
|
||||
import type {
|
||||
@ -13,7 +13,7 @@ import { get, post } from './base'
|
||||
import { consoleClient } from './client'
|
||||
import { getFlowPrefix } from './utils'
|
||||
|
||||
export type { WorkflowDraftFeaturesPayload } from '@/contract/console/workflow'
|
||||
export type WorkflowDraftFeaturesPayload = ContractWorkflowDraftFeaturesPayload
|
||||
|
||||
export const fetchWorkflowDraft = (url: string) => {
|
||||
return get(url, {}, { silent: true }) as Promise<FetchWorkflowDraftResponse>
|
||||
@ -130,7 +130,7 @@ export const updateConversationVariables = ({ appId, conversationVariables }: {
|
||||
|
||||
export const updateFeatures = ({ appId, features }: {
|
||||
appId: string
|
||||
features: WorkflowDraftFeaturesPayload
|
||||
features: ContractWorkflowDraftFeaturesPayload
|
||||
}) => {
|
||||
return consoleClient.workflowDraft.updateFeatures({
|
||||
params: { appId },
|
||||
|
||||
Reference in New Issue
Block a user