Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 120 additions & 58 deletions components/inbox-mail/InboxMailAdminPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useCallback } from "react";
import { InboxMailThread, InboxMailThreadSupportProgressState, User } from "./types-mail";
import { IconExternalLink, IconProgressCheck } from "@tabler/icons-react";
import React, { useCallback, useEffect, useState } from "react";
import { InboxMailThread, InboxMailThreadSupportProgressState, JumpDestination, User } from "./types-mail";
import { IconExternalLink, IconProgressCheck, IconAlertTriangle } from "@tabler/icons-react";
import KernDropdown from "../KernDropdown";
import { addUserToOrganization, removeUserFromOrganization, updateInboxMailThreadsUnreadByContent, updateInboxMailThreadsUnreadByProject } from "./service-mail";
import { addUserToOrganization, removeUserFromOrganization, updateInboxMailThreadsUnreadByContent, updateInboxMailThreadsUnreadByProject, updateInboxMailThreadUnreadLast, deleteInboxMailThreadsSimilar } from "./service-mail";
import { Tooltip } from "@nextui-org/react";
import KernButton from "../kern-button/KernButton";


Expand All @@ -17,36 +18,50 @@ interface InboxMailAdminPanelProps {
handleInboxMailProgressChange: (value: InboxMailThreadSupportProgressState) => void;
currentUser: User;
refetchInboxMailOverview: () => void;
translator: (key: string) => string;
}

function InboxMailAdminPanel(props: InboxMailAdminPanelProps) {
// No translations needed, admin only
const assignAndJump = useCallback((toConversation: boolean) => {
const t = props.translator;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we now need translator here?

const [canMarkLastUnread, setCanMarkLastUnread] = useState(true);

useEffect(() => {
if (props.selectedThread.metaData?.unreadMailCountAdmin > 0) {
setCanMarkLastUnread(false);
} else {
setCanMarkLastUnread(true);
}
}, [props.selectedThread.id, props.selectedThread.metaData?.unreadMailCountAdmin]);

const assignAndJump = useCallback((destination: JumpDestination) => {
if (!props.currentUser) return;
const currentOrganizationId = props.currentUser?.organizationId;
if (!currentOrganizationId) {
addUserToOrganization(props.currentUser.mail, props.selectedThread.organizationName, (res) => {
jumptoConversationOrProject(toConversation);

jumpTo(destination);
});
} else if (currentOrganizationId === props.selectedThread.organizationId) {
jumptoConversationOrProject(toConversation);

jumpTo(destination);
} else {
removeUserFromOrganization(props.currentUser.mail, (res) => {
addUserToOrganization(props.currentUser.mail, props.selectedThread.organizationName, (res) => {
jumptoConversationOrProject(toConversation);
jumpTo(destination);
});
});
}
}, [props.currentUser, props.selectedThread]);

const jumptoConversationOrProject = useCallback((toConversation: boolean) => {
if (toConversation) {
window.open(`/cognition/projects/${props.selectedThread.metaData?.projectId}/ui/${props.selectedThread.metaData?.conversationId}`, '_blank');
}
else {
window.open(`/cognition/projects/${props.selectedThread.metaData.projectId}/pipeline`, '_blank');
const jumpTo = useCallback((destination: JumpDestination) => {
switch (destination) {
case JumpDestination.CONVERSATION:
window.open(`/cognition/projects/${props.selectedThread.metaData?.projectId}/ui/${props.selectedThread.metaData?.conversationId}`, '_blank');
break;
case JumpDestination.PROJECT:
window.open(`/cognition/projects/${props.selectedThread.metaData?.projectId}/pipeline`, '_blank');
break;
case JumpDestination.ORGANIZATION:
window.open('/cognition', '_blank');
break;
}
}, [props.selectedThread.metaData]);

Expand All @@ -61,7 +76,18 @@ function InboxMailAdminPanel(props: InboxMailAdminPanelProps) {
updateInboxMailThreadsUnreadByProject(props.selectedThread.id, (res) => {
props.refetchInboxMailOverview();
});
}, [props.selectedThread?.id, props.refetchInboxMailOverview]);

const handleMarkLastUnread = useCallback(() => {
setCanMarkLastUnread(false);
updateInboxMailThreadUnreadLast(props.selectedThread.id, (res) => {
props.refetchInboxMailOverview();
});
}, [props.selectedThread?.id, props.refetchInboxMailOverview]);

const handleDeleteSimilar = useCallback(() => {
if (!confirm("Are you sure you want to delete all similar inbox mails? This action cannot be undone.")) return;
deleteInboxMailThreadsSimilar(props.selectedThread.id, (res) => props.refetchInboxMailOverview());
}, [props.selectedThread?.id, props.refetchInboxMailOverview]);

return (
Expand Down Expand Up @@ -89,20 +115,61 @@ function InboxMailAdminPanel(props: InboxMailAdminPanelProps) {
{props.selectedThread.metaData.supportOwnerName?.last}
</div>
)}
{props.selectedThread?.metaData?.autoGenerated &&
<div className="flex items-center gap-3 ml-auto my-2 px-3 py-1 ">
<KernButton
className="ml-auto"
text="Read all by error"
onClick={handleSameContentRead}
/>
<KernButton
text="Read all by project"
onClick={handleSameProjectRead}
/>
</div>
}
<div className="flex items-center gap-3 ml-auto my-2 px-3 py-1">
{props.selectedThread?.metaData?.autoGenerated && (
<>
<KernButton
text={t("inboxMail.readAllByError")}
onClick={handleSameContentRead}
/>
<KernButton
text={t("inboxMail.readAllByProject")}
onClick={handleSameProjectRead}
/>
</>
)}
<KernButton
text={t("inboxMail.markLastUnread")}
onClick={handleMarkLastUnread}
disabled={!canMarkLastUnread}
/>
{props.selectedThread?.metaData?.autoGenerated && (
<Tooltip
content={<div className="w-52">{t("inboxMail.deleteAllByContentTooltip")}</div>}
placement="top"
color="invert"
>
<KernButton
text={t("inboxMail.deleteAllByContent")}
onClick={handleDeleteSimilar}
icon={() => <IconAlertTriangle className="w-4 h-4 text-red-500" />}
/>
</Tooltip>
)}
</div>
</div>
{props.selectedThread.organizationId && (
<div className="flex items-center gap-3 ml-2 my-2 px-3 py-1 rounded-xl bg-indigo-400/60 text-white w-fit">
<div className="flex items-center gap-1.5 text-xs">
<span className="font-semibold">Organization</span>
</div>

<div className="flex items-center gap-1.5 text-xs bg-indigo-400 px-2 py-0.5 rounded-md">
<span>ID:</span>
<span>{props.selectedThread.organizationId}</span>
</div>
<div className="flex items-center gap-1.5 text-xs bg-indigo-400 px-2 py-0.5 rounded-md">
<span>{props.selectedThread.organizationName}</span>
</div>
<button
className="flex items-center gap-1.5 text-xs bg-indigo-400 px-2 py-0.5 rounded-md"
onClick={() => assignAndJump(JumpDestination.ORGANIZATION)}
>
<IconExternalLink className="w-4 h-4" />
</button>
</div>
)}

{props.selectedThread.metaData?.projectId && (
<div className="flex items-center gap-3 ml-2 my-2 px-3 py-1 rounded-xl bg-slate-400/60 text-white w-fit">
<div className="flex items-center gap-1.5 text-xs">
Expand All @@ -118,39 +185,34 @@ function InboxMailAdminPanel(props: InboxMailAdminPanelProps) {
</div>
<button
className="flex items-center gap-1.5 text-xs bg-slate-400 px-2 py-0.5 rounded-md"
onClick={() =>
assignAndJump(false)
}
onClick={() => assignAndJump(JumpDestination.PROJECT)}
>
<IconExternalLink className="w-4 h-4" />
</button>
</div>
)
}

{
props.selectedThread.metaData?.conversationId && (
<div className="flex items-center gap-3 ml-2 my-2 px-3 py-1 rounded-xl bg-gray-400/60 text-white w-fit">
<div className="flex items-center gap-1.5 text-xs">
<span className="font-semibold">Conversation</span>
</div>

<div className="flex items-center gap-1.5 text-xs bg-gray-400 px-2 py-0.5 rounded-md">
<span>ID:</span>
<span>{props.selectedThread.metaData.conversationId}</span>
</div>
<div className="flex items-center gap-1.5 text-xs bg-gray-400 px-2 py-0.5 rounded-md">
<span>{props.selectedThread.metaData.conversationHeader || "N/A"}</span>
</div>
<button
className="flex items-center gap-1.5 text-xs bg-gray-400 px-2 py-0.5 rounded-md"
onClick={() => assignAndJump(true)}
>
<IconExternalLink className="w-4 h-4" />
</button>
)}

{props.selectedThread.metaData?.conversationId && (
<div className="flex items-center gap-3 ml-2 my-2 px-3 py-1 rounded-xl bg-gray-400/60 text-white w-fit">
<div className="flex items-center gap-1.5 text-xs">
<span className="font-semibold">Conversation</span>
</div>

<div className="flex items-center gap-1.5 text-xs bg-gray-400 px-2 py-0.5 rounded-md">
<span>ID:</span>
<span>{props.selectedThread.metaData.conversationId}</span>
</div>
<div className="flex items-center gap-1.5 text-xs bg-gray-400 px-2 py-0.5 rounded-md">
<span>{props.selectedThread.metaData.conversationHeader || "N/A"}</span>
</div>
)
}
<button
className="flex items-center gap-1.5 text-xs bg-gray-400 px-2 py-0.5 rounded-md"
onClick={() => assignAndJump(JumpDestination.CONVERSATION)}
>
<IconExternalLink className="w-4 h-4" />
</button>
</div>
)}
</div >
);
};
Expand Down
18 changes: 14 additions & 4 deletions components/inbox-mail/InboxMailThreadOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useMemo } from "react";
import { useCallback, useMemo } from "react";
import { InboxMailThread, InboxMailThreadSupportProgressState } from "./types-mail";
import { Tooltip } from "@nextui-org/react";
import { MemoIconAlertTriangle, MemoIconCircleCheck, MemoIconHelpCircle, MemoIconProgressCheck, MemoIconUser } from "../kern-icons/icons";
import { MemoIconAlertTriangle, MemoIconCircleCheck, MemoIconHelpCircle, MemoIconProgressCheck, MemoIconUser, MemoIconUsers } from "../kern-icons/icons";
import { formatDisplayTimestamp } from "@/submodules/javascript-functions/date-parser";
import { useRouter } from "next/router";

interface InboxMailThreadOverviewProps {
thread: InboxMailThread;
Expand All @@ -14,6 +15,7 @@ interface InboxMailThreadOverviewProps {

export default function InboxMailThreadOverview(props: InboxMailThreadOverviewProps) {
const t = useMemo(() => props.translator, [props.translator]);
const router = useRouter();
const {
displayName,
displayInitials,
Expand All @@ -32,6 +34,7 @@ export default function InboxMailThreadOverview(props: InboxMailThreadOverviewPr
}
}, [props.thread, props.isAdmin]);

const clickHome = useCallback(() => router.push("/"), [router]);
return (
<div
key={props.thread.id}
Expand All @@ -55,16 +58,23 @@ export default function InboxMailThreadOverview(props: InboxMailThreadOverviewPr
</div>
) : (
<div className="self-start shrink-0 mt-1 flex items-center justify-center w-10 h-10 border rounded-md p-2 text-sm font-semibold bg-[#18181B]">
<img className="h-8 w-auto" src="/cognition/kernai.svg" alt="Kern AI" />
<img className="h-8 w-auto cursor-pointer" src="/cognition/kernai.svg" alt="Kern AI" onClick={clickHome} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong element for this behaviour probably

</div>
)}

<div className="grow min-w-0">
<div className="flex items-start justify-between gap-x-2 flex-nowrap">
<div className="flex items-center">
<div className="flex items-center gap-x-2">
<div className="text-gray-800 font-medium truncate mt-0.5">
{displayName ?? `<${t("inboxMail.unknownUser")}>`}
</div>
{props.isAdmin && (
<Tooltip content={`Org: ${props.thread.organizationName}`} placement="top" color="invert">
<div className="flex items-center justify-center text-gray-500 cursor-help">
<MemoIconUsers className="h-4 w-4" />
</div>
</Tooltip>
)}
{unreadMailCount > 0 && (
<span className="ml-2 inline-flex items-center justify-center bg-slate-400 text-white text-[0.625rem] font-semibold rounded-full h-4 min-w-4 px-1.5 whitespace-nowrap">
{unreadMailCount} {t("inboxMail.newBadge")}
Expand Down
Loading