mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 19:58:11 +00:00
feat: WIP element Chat intergration
This commit is contained in:
@@ -115,6 +115,7 @@ import FaydaCallbackPage from "./pages/FaydaCallbackPage";
|
||||
import { UserManagementRoutes } from "./user-management/route";
|
||||
import SetPassword from "./shared/components/SetPassword";
|
||||
import SupportInboxPage from "./pages/support/SupportInboxPage";
|
||||
import ChatLaunchPage from "./pages/chat/ChatLaunchPage";
|
||||
import {
|
||||
APP_TITLE,
|
||||
buildSidebarSections,
|
||||
@@ -298,6 +299,14 @@ const App = () => {
|
||||
</RequirePermission>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="chat"
|
||||
element={
|
||||
<RequirePermission permission={FREIGHT_PERMS.chat.view}>
|
||||
<ChatLaunchPage />
|
||||
</RequirePermission>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="customers"
|
||||
element={
|
||||
|
||||
@@ -71,6 +71,13 @@ const ROUTE_META: Array<{ prefix: string; meta: PageMeta }> = [
|
||||
subtitle: "Manage your account and signature",
|
||||
},
|
||||
},
|
||||
{
|
||||
prefix: "/dashboard/chat",
|
||||
meta: {
|
||||
title: "Chat",
|
||||
subtitle: "Internal messaging for EDR staff",
|
||||
},
|
||||
},
|
||||
{
|
||||
// Invoices, Payments, and USD Payments are tabs on one page now
|
||||
// (FinanceHubPage); the header title itself is set per-tab there.
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
Users,
|
||||
Wallet,
|
||||
LifeBuoy,
|
||||
MessageSquare,
|
||||
TrainFront,
|
||||
XCircle,
|
||||
} from "lucide-react";
|
||||
@@ -124,6 +125,12 @@ export const buildSidebarSections = (
|
||||
icon: <LifeBuoy />,
|
||||
permission: FREIGHT_PERMS.support.agentView,
|
||||
},
|
||||
{
|
||||
label: "Chat",
|
||||
href: "/dashboard/chat",
|
||||
icon: <MessageSquare />,
|
||||
permission: FREIGHT_PERMS.chat.view,
|
||||
},
|
||||
...demoItems,
|
||||
],
|
||||
},
|
||||
|
||||
13
apps/edr-freight-web/backoffice/src/features/chat/chatApi.ts
Normal file
13
apps/edr-freight-web/backoffice/src/features/chat/chatApi.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { api } from "@/auth/http";
|
||||
|
||||
/**
|
||||
* Internal chat (Matrix/Element) REST calls. Just the one endpoint — Chat
|
||||
* itself is a separate app (chat.edr.et); this backoffice only ever asks for
|
||||
* a fresh sign-in link into it.
|
||||
*/
|
||||
export const chatApi = {
|
||||
getSsoUrl: async (): Promise<string> => {
|
||||
const { data } = await api.get<{ url: string }>("/chat/sso");
|
||||
return data.url;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { chatApi } from "./chatApi";
|
||||
|
||||
export const CHAT_SSO_KEY = ["chat", "sso"] as const;
|
||||
|
||||
/**
|
||||
* The login_token this resolves to is single-use and expires in 5 minutes
|
||||
* (Synapse default) — the global `staleTime: 0` (queryClient.ts) already
|
||||
* means every fresh mount of the launch page refetches rather than reusing
|
||||
* a possibly-spent link.
|
||||
*/
|
||||
export function useChatSso() {
|
||||
return useQuery({
|
||||
queryKey: CHAT_SSO_KEY,
|
||||
queryFn: chatApi.getSsoUrl,
|
||||
retry: false,
|
||||
});
|
||||
}
|
||||
@@ -33,6 +33,10 @@ export const FREIGHT_PERMS = {
|
||||
staffUsers: {
|
||||
view: "edr_freight_app:staff:users:view",
|
||||
},
|
||||
chat: {
|
||||
view: "edr_freight_app:chat:view",
|
||||
sync: "edr_freight_app:chat:sync",
|
||||
},
|
||||
bookings: {
|
||||
view: "edr_freight_app:bookings:view",
|
||||
create: "edr_freight_app:bookings:create",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Alert, Button, Card, Center, Loader, Stack, Text } from "@mantine/core";
|
||||
import { MessageSquare, TriangleAlert } from "lucide-react";
|
||||
|
||||
import { PageContainer, PageHeader } from "@/components/page";
|
||||
import { useChatSso } from "@/features/chat/useChatSso";
|
||||
|
||||
/**
|
||||
* Chat itself lives at chat.edr.et (Element), not in this app — this page's
|
||||
* only job is a fresh one-click sign-in link into it. A real `<a>` (not
|
||||
* `window.open()` in a click handler) so the browser never treats it as a
|
||||
* blocked popup, and no iframe: Element's own CSP refuses to be framed.
|
||||
*/
|
||||
export default function ChatLaunchPage() {
|
||||
const { data: url, isLoading, isError, refetch } = useChatSso();
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader title="Chat" subtitle="Internal messaging for EDR staff" />
|
||||
<Card withBorder radius="md" p="xl">
|
||||
<Center>
|
||||
<Stack align="center" gap="md" py="xl">
|
||||
{isLoading && <Loader />}
|
||||
|
||||
{isError && (
|
||||
<Stack align="center" gap="sm">
|
||||
<Alert
|
||||
icon={<TriangleAlert size={18} />}
|
||||
color="red"
|
||||
title="Couldn't get a sign-in link"
|
||||
variant="light"
|
||||
>
|
||||
Something went wrong reaching chat. Try again.
|
||||
</Alert>
|
||||
<Button variant="light" onClick={() => refetch()}>
|
||||
Retry
|
||||
</Button>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{url && (
|
||||
<Stack align="center" gap="sm">
|
||||
<MessageSquare size={40} strokeWidth={1.5} />
|
||||
<Text c="dimmed" ta="center" maw={360}>
|
||||
Opens EDR Chat in a new tab, already signed in as you.
|
||||
</Text>
|
||||
<Button
|
||||
component="a"
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
leftSection={<MessageSquare size={16} />}
|
||||
>
|
||||
Open EDR Chat
|
||||
</Button>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Center>
|
||||
</Card>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user