'use client' import type { RecentAppResponse } from '@dify/contracts/api/console/apps/types.gen' import { cn } from '@langgenius/dify-ui/cn' import { toast } from '@langgenius/dify-ui/toast' import { useSuspenseQuery } from '@tanstack/react-query' import { useAtomValue } from 'jotai' import { useId, useState } from 'react' import { useTranslation } from 'react-i18next' import { AppTypeIcon } from '@/app/components/app/type-selector' import AppIcon from '@/app/components/base/app-icon' import { workspacePermissionKeysAtom } from '@/context/permission-state' import { userProfileQueryOptions } from '@/features/account-profile/client' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' import Link from '@/next/link' import { getRedirectionPath } from '@/utils/app-redirection' import { hasOnlyAppPreviewPermission } from '@/utils/permission' const appModeLabelKeys = { 'advanced-chat': 'types.advanced', 'agent-chat': 'types.agent', chat: 'types.chatbot', completion: 'types.completion', workflow: 'types.workflow', } as const satisfies Record type ContinueWorkItemProps = { app: RecentAppResponse } export function ContinueWorkItem({ app }: ContinueWorkItemProps) { const { t } = useTranslation() const { formatTimeFromNow } = useFormatTimeFromNow() const { data: currentUserId } = useSuspenseQuery({ ...userProfileQueryOptions(), select: (data) => data.profile.id, }) const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom) const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions()) const appNameId = useId() const appModeId = useId() const appMetadataId = useId() const [isPrefetchEnabled, setIsPrefetchEnabled] = useState(false) const isRbacEnabled = systemFeatures.rbac_enabled const updatedAt = app.updated_at * 1000 const appModeLabel = t(($) => $[appModeLabelKeys[app.mode]], { ns: 'app' }) const isPreviewOnly = hasOnlyAppPreviewPermission(app.permission_keys) const href = getRedirectionPath(app, { currentUserId, resourceMaintainer: app.maintainer, workspacePermissionKeys, isRbacEnabled, }) const cardClassName = cn( 'relative flex min-w-0 items-center gap-3 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg px-4 pt-4 pb-4 text-left shadow-xs shadow-shadow-shadow-3', isPreviewOnly && 'cursor-not-allowed opacity-60', ) const showPreviewOnlyAccessWarning = () => { toast.warning(t(($) => $.noAccessResourcePermission, { ns: 'app' })) } const cardContent = ( <>

{app.name}

{appModeLabel}
{app.author_name && ( <> {app.author_name} )} {t(($) => $['continueWork.editedAt'], { ns: 'explore', time: formatTimeFromNow(updatedAt), })}
) if (isPreviewOnly) { return (
) } return (
setIsPrefetchEnabled(true)} onFocus={() => setIsPrefetchEnabled(true)} aria-labelledby={`${appNameId} ${appModeId}`} aria-describedby={appMetadataId} className="absolute inset-0 z-10 touch-manipulation rounded-xl outline-hidden focus-visible:inset-ring-2 focus-visible:inset-ring-state-accent-solid" /> {cardContent}
) }