Compare commits
7 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7e19bae8 | ||
|
|
6440c402cf | ||
|
|
047ef094fc | ||
|
|
dca61e1459 | ||
|
|
89fdd955fe | ||
|
|
566d3643de | ||
|
|
b621014178 |
10 changed files with 254 additions and 62 deletions
|
|
@ -1,9 +1,30 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, PropsWithChildren, useState } from "react";
|
import { FC, PropsWithChildren, useEffect, useState } from "react";
|
||||||
import { Grid, Box } from "@mui/material";
|
|
||||||
import { Sidebar } from "./Sidebar";
|
|
||||||
import { SetupModeWarning } from "./SetupModeWarning";
|
import { SetupModeWarning } from "./SetupModeWarning";
|
||||||
|
import {
|
||||||
|
AppBar as MuiAppBar,
|
||||||
|
ToolbarProps,
|
||||||
|
Box,
|
||||||
|
Grid,
|
||||||
|
IconButton,
|
||||||
|
styled,
|
||||||
|
Toolbar,
|
||||||
|
Typography,
|
||||||
|
Menu,
|
||||||
|
useTheme,
|
||||||
|
useMediaQuery,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { Sidebar } from "./Sidebar";
|
||||||
|
import {
|
||||||
|
ExpandCircleDown,
|
||||||
|
MenuBookTwoTone,
|
||||||
|
MenuSharp,
|
||||||
|
} from "@mui/icons-material";
|
||||||
|
import { fonts } from "@link-stack/ui";
|
||||||
|
import Image from "next/image";
|
||||||
|
import LinkLogo from "public/link-logo-small.png";
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
interface InternalLayoutProps extends PropsWithChildren {
|
interface InternalLayoutProps extends PropsWithChildren {
|
||||||
setupModeActive: boolean;
|
setupModeActive: boolean;
|
||||||
|
|
@ -15,20 +36,102 @@ export const InternalLayout: FC<InternalLayoutProps> = ({
|
||||||
setupModeActive,
|
setupModeActive,
|
||||||
leafcutterEnabled,
|
leafcutterEnabled,
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = useState(true);
|
const { poppins } = fonts;
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const mobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [openWidth, setOpenWidth] = useState<number>(0);
|
||||||
|
const [closedWidth, setClosedWidth] = useState<number>(0);
|
||||||
|
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// On mobile, close menu when stuff is selected
|
||||||
|
if (mobile) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setClosedWidth(mobile ? 0 : 70);
|
||||||
|
setOpenWidth(270);
|
||||||
|
setOpen(!mobile);
|
||||||
|
}, [mobile]);
|
||||||
|
|
||||||
|
interface HeaderBarProps extends ToolbarProps {
|
||||||
|
open?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeaderBar = styled(Toolbar, {
|
||||||
|
shouldForwardProp: (prop) => prop !== "open",
|
||||||
|
})<HeaderBarProps>(({ theme, open }) => ({
|
||||||
|
backgroundColor: "#25272A",
|
||||||
|
width: "100%",
|
||||||
|
height: "56px",
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ position: "relative" }}>
|
<Box sx={{ position: "relative" }}>
|
||||||
<SetupModeWarning setupModeActive={setupModeActive} />
|
<SetupModeWarning setupModeActive={setupModeActive} />
|
||||||
<Grid container direction="row">
|
<Grid container direction="row">
|
||||||
|
{mobile ? (<MuiAppBar>
|
||||||
|
<HeaderBar>
|
||||||
|
<IconButton
|
||||||
|
sx={{ color: "white" }}
|
||||||
|
aria-label="open drawer"
|
||||||
|
onClick={() => setOpen(true)}
|
||||||
|
>
|
||||||
|
<MenuSharp />
|
||||||
|
</IconButton>
|
||||||
|
<Typography
|
||||||
|
variant="h2"
|
||||||
|
sx={{
|
||||||
|
fontSize: 26,
|
||||||
|
color: "white",
|
||||||
|
fontWeight: 700,
|
||||||
|
fontFamily: poppins.style.fontFamily,
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
CDR Link
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "40px",
|
||||||
|
height: "40px",
|
||||||
|
margin: open ? "0" : "0 auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={LinkLogo}
|
||||||
|
alt="Link logo"
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
style={{
|
||||||
|
objectFit: "cover",
|
||||||
|
filter: "grayscale(100) brightness(100)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</HeaderBar>
|
||||||
|
</MuiAppBar>) : (null)}
|
||||||
<Sidebar
|
<Sidebar
|
||||||
open={open}
|
open={open}
|
||||||
setOpen={setOpen}
|
setOpen={setOpen}
|
||||||
leafcutterEnabled={leafcutterEnabled}
|
openWidth={openWidth}
|
||||||
|
closedWidth={closedWidth}
|
||||||
/>
|
/>
|
||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
sx={{ ml: open ? "270px" : "70px", width: "100%", height: "100vh" }}
|
sx={{
|
||||||
|
mt: { xs: "56px", sm: "0" },
|
||||||
|
ml: { xs: open ? "270px" : "0px", sm: open ? "270px" : "70px" },
|
||||||
|
width: "100%",
|
||||||
|
height: { xs: "calc(100vh - 56px)", sm: "100vh" },
|
||||||
|
position: "relative",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children as any}
|
{children as any}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import {
|
||||||
ListItemSecondaryAction,
|
ListItemSecondaryAction,
|
||||||
Drawer,
|
Drawer,
|
||||||
Collapse,
|
Collapse,
|
||||||
|
useTheme,
|
||||||
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {
|
import {
|
||||||
FeaturedPlayList as FeaturedPlayListIcon,
|
FeaturedPlayList as FeaturedPlayListIcon,
|
||||||
|
|
@ -35,9 +37,6 @@ import { getOverviewTicketCountsAction } from "app/_actions/overviews";
|
||||||
import { SearchBox } from "./SearchBox";
|
import { SearchBox } from "./SearchBox";
|
||||||
import { fonts } from "@link-stack/ui";
|
import { fonts } from "@link-stack/ui";
|
||||||
|
|
||||||
const openWidth = 270;
|
|
||||||
const closedWidth = 70;
|
|
||||||
|
|
||||||
const MenuItem = ({
|
const MenuItem = ({
|
||||||
name,
|
name,
|
||||||
href,
|
href,
|
||||||
|
|
@ -178,14 +177,12 @@ const MenuItem = ({
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: (open: boolean) => void;
|
setOpen: (open: boolean) => void;
|
||||||
|
openWidth: number;
|
||||||
|
closedWidth: number;
|
||||||
leafcutterEnabled?: boolean;
|
leafcutterEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Sidebar: FC<SidebarProps> = ({
|
export const Sidebar: FC<SidebarProps> = ({ open, setOpen, openWidth, closedWidth, leafcutterEnabled = false }) => {
|
||||||
open,
|
|
||||||
setOpen,
|
|
||||||
leafcutterEnabled = false,
|
|
||||||
}) => {
|
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const [overviewCounts, setOverviewCounts] = useState<any>(null);
|
const [overviewCounts, setOverviewCounts] = useState<any>(null);
|
||||||
|
|
@ -207,6 +204,9 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const mobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
signOut({ callbackUrl: "/login" });
|
signOut({ callbackUrl: "/login" });
|
||||||
};
|
};
|
||||||
|
|
@ -214,14 +214,15 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
sx={{ width: open ? openWidth : closedWidth, flexShrink: 0 }}
|
sx={{ width: open ? openWidth : closedWidth, flexShrink: 0 }}
|
||||||
variant="permanent"
|
variant={mobile ? "temporary" : "permanent"}
|
||||||
anchor="left"
|
anchor="left"
|
||||||
open={open}
|
open={open}
|
||||||
|
onClose={() => {if (mobile) setOpen(false)}}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
sx: {
|
sx: {
|
||||||
width: open ? openWidth : closedWidth,
|
width: open ? openWidth : closedWidth,
|
||||||
border: 0,
|
border: 0,
|
||||||
overflow: "visible",
|
overflow: openWidth == 0 ? "hidden" : "visible",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -493,14 +494,14 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
selected={pathname.endsWith("/docs")}
|
selected={pathname.endsWith("/docs")}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
{!mobile && (<MenuItem
|
||||||
name="Reporting"
|
name="Reporting"
|
||||||
href="/reporting"
|
href="/reporting"
|
||||||
Icon={AssessmentIcon}
|
Icon={AssessmentIcon}
|
||||||
iconSize={20}
|
iconSize={20}
|
||||||
selected={pathname.endsWith("/reporting")}
|
selected={pathname.endsWith("/reporting")}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>)}
|
||||||
{leafcutterEnabled && (
|
{leafcutterEnabled && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Leafcutter"
|
name="Leafcutter"
|
||||||
|
|
@ -557,6 +558,7 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
{!mobile && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Profile"
|
name="Profile"
|
||||||
href="/profile"
|
href="/profile"
|
||||||
|
|
@ -565,7 +567,8 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
selected={pathname.endsWith("/profile")}
|
selected={pathname.endsWith("/profile")}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
{roles.includes("admin") && (
|
)}
|
||||||
|
{roles.includes("admin") && !mobile && (
|
||||||
<>
|
<>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Admin"
|
name="Admin"
|
||||||
|
|
@ -655,7 +658,7 @@ export const Sidebar: FC<SidebarProps> = ({
|
||||||
)}
|
)}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Zammad Interface"
|
name="Zammad Interface"
|
||||||
href="/zammad"
|
href={mobile ? "/mobile" : "/zammad"}
|
||||||
Icon={DvrIcon}
|
Icon={DvrIcon}
|
||||||
iconSize={20}
|
iconSize={20}
|
||||||
open={open}
|
open={open}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,14 @@ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
linkElement.contentDocument.querySelector("#navigation").style =
|
linkElement.contentDocument.querySelector("#navigation").style =
|
||||||
"display: none";
|
"display: none";
|
||||||
|
// @ts-ignore
|
||||||
|
if (linkElement.contentDocument.querySelector(".content")) {
|
||||||
|
// If navigation removed, set content margin to 0 to avoid gap.
|
||||||
|
// @ts-ignore
|
||||||
|
linkElement.contentDocument.querySelector(".content").style =
|
||||||
|
"margin-left: 0";
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
linkElement.contentDocument.querySelector("body").style =
|
linkElement.contentDocument.querySelector("body").style =
|
||||||
"font-family: Arial";
|
"font-family: Arial";
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import { Grid, Box } from "@mui/material";
|
import { Grid, Box, useTheme, useMediaQuery } from "@mui/material";
|
||||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||||
import { StyledDataGrid } from "app/(main)/_components/StyledDataGrid";
|
import { StyledDataGrid } from "app/(main)/_components/StyledDataGrid";
|
||||||
import { Button, List, typography } from "@link-stack/ui";
|
import { Button, List, typography } from "@link-stack/ui";
|
||||||
|
|
@ -16,6 +16,10 @@ interface TicketListProps {
|
||||||
export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
|
export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const mobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||||
|
|
||||||
let gridColumns: GridColDef[] = [
|
let gridColumns: GridColDef[] = [
|
||||||
{
|
{
|
||||||
field: "number",
|
field: "number",
|
||||||
|
|
@ -32,7 +36,9 @@ export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
|
||||||
headerName: "Sender",
|
headerName: "Sender",
|
||||||
valueGetter: (value: any) => value?.fullname,
|
valueGetter: (value: any) => value?.fullname,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
}];
|
||||||
|
if (!mobile) {
|
||||||
|
gridColumns.push(
|
||||||
{
|
{
|
||||||
field: "createdAt",
|
field: "createdAt",
|
||||||
headerName: "Created At",
|
headerName: "Created At",
|
||||||
|
|
@ -50,8 +56,8 @@ export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
|
||||||
headerName: "Group",
|
headerName: "Group",
|
||||||
valueGetter: (value: any) => value?.name,
|
valueGetter: (value: any) => value?.name,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
});
|
||||||
];
|
}
|
||||||
|
|
||||||
const onRowClick = (id: any) => {
|
const onRowClick = (id: any) => {
|
||||||
router.push(`/tickets/${id}`);
|
router.push(`/tickets/${id}`);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { FC, useState, useEffect } from "react";
|
import { FC, useState, useEffect } from "react";
|
||||||
import { getTicketAction, getTicketArticlesAction } from "app/_actions/tickets";
|
import { getTicketAction, getTicketArticlesAction } from "app/_actions/tickets";
|
||||||
import { Grid, Box, Typography } from "@mui/material";
|
import { Grid, Box, Typography, useTheme, useMediaQuery } from "@mui/material";
|
||||||
import { Button, fonts, colors } from "@link-stack/ui";
|
import { Button, fonts, colors } from "@link-stack/ui";
|
||||||
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
|
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
|
||||||
import {
|
import {
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
ConversationHeader,
|
ConversationHeader,
|
||||||
} from "@chatscope/chat-ui-kit-react";
|
} from "@chatscope/chat-ui-kit-react";
|
||||||
import { ArticleCreateDialog } from "./ArticleCreateDialog";
|
import { ArticleCreateDialog } from "./ArticleCreateDialog";
|
||||||
|
import { TicketHeader } from "../../_components/TicketHeader";
|
||||||
|
|
||||||
interface TicketDetailProps {
|
interface TicketDetailProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -26,6 +27,9 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const [articleKind, setArticleKind] = useState("note");
|
const [articleKind, setArticleKind] = useState("note");
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const mobile = useMediaQuery(theme.breakpoints.down("md"));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTicket = async () => {
|
const fetchTicket = async () => {
|
||||||
const result = await getTicketAction(id);
|
const result = await getTicketAction(id);
|
||||||
|
|
@ -66,36 +70,11 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||||
<>
|
<>
|
||||||
<MainContainer>
|
<MainContainer>
|
||||||
<ChatContainer>
|
<ChatContainer>
|
||||||
<ConversationHeader>
|
{!mobile && (<ConversationHeader>
|
||||||
<ConversationHeader.Content>
|
<ConversationHeader.Content>
|
||||||
<Box
|
<TicketHeader ticket={ticket} />
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
textAlign: "center",
|
|
||||||
fontWeight: "bold",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
variant="h5"
|
|
||||||
sx={{
|
|
||||||
fontFamily: poppins.style.fontFamily,
|
|
||||||
fontWeight: 700,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{ticket.title}
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
sx={{
|
|
||||||
fontFamily: roboto.style.fontFamily,
|
|
||||||
fontWeight: 400,
|
|
||||||
}}
|
|
||||||
>{`Ticket #${ticket.number} (created ${new Date(
|
|
||||||
ticket.createdAt,
|
|
||||||
).toLocaleDateString()})`}</Typography>
|
|
||||||
</Box>
|
|
||||||
</ConversationHeader.Content>
|
</ConversationHeader.Content>
|
||||||
</ConversationHeader>
|
</ConversationHeader>)}
|
||||||
<MessageList style={{ marginBottom: 80 }}>
|
<MessageList style={{ marginBottom: 80 }}>
|
||||||
{ticketArticles.edges.map(({ node: article }: any) => (
|
{ticketArticles.edges.map(({ node: article }: any) => (
|
||||||
<Message
|
<Message
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
||||||
const shouldRender = !!ticket;
|
const shouldRender = !!ticket;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ height: "100vh", background: "#ddd", p: 2 }}>
|
<Box sx={{ background: "#ddd", p: 2 }}>
|
||||||
{shouldRender && (
|
{shouldRender && (
|
||||||
<Grid container direction="column" spacing={3}>
|
<Grid container direction="column" spacing={3}>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
|
|
||||||
12
apps/link/app/(main)/tickets/[id]/@header/page.tsx
Normal file
12
apps/link/app/(main)/tickets/[id]/@header/page.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { getTicketAction } from "@/app/_actions/tickets";
|
||||||
|
import { TicketHeader } from "../_components/TicketHeader";
|
||||||
|
|
||||||
|
type PageProps = {
|
||||||
|
params: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Page({ params: { id } }: PageProps) {
|
||||||
|
return (<TicketHeader id={id} />);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FC, useState, useEffect } from "react";
|
||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import { fonts } from "@link-stack/ui";
|
||||||
|
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
|
||||||
|
import {
|
||||||
|
ConversationHeader,
|
||||||
|
ConversationHeaderProps,
|
||||||
|
} from "@chatscope/chat-ui-kit-react";
|
||||||
|
import { getTicketAction } from "@/app/_actions/tickets";
|
||||||
|
|
||||||
|
interface TicketHeaderProps {
|
||||||
|
id?: string;
|
||||||
|
ticket?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TicketHeader: FC<ConversationHeaderProps & TicketHeaderProps> = (
|
||||||
|
props,
|
||||||
|
) => {
|
||||||
|
const { poppins, roboto } = fonts;
|
||||||
|
|
||||||
|
const [ticket, setTicket] = useState<any>(props.ticket || null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ticket) {
|
||||||
|
const fetchTicket = async () => {
|
||||||
|
const result = await getTicketAction(props.id);
|
||||||
|
setTicket(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTicket();
|
||||||
|
|
||||||
|
const interval = setInterval(fetchTicket, 20000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, [props.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
textAlign: "center",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="h5"
|
||||||
|
sx={{
|
||||||
|
fontFamily: poppins.style.fontFamily,
|
||||||
|
fontWeight: 700,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ticket?.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="h6"
|
||||||
|
sx={{
|
||||||
|
fontFamily: roboto.style.fontFamily,
|
||||||
|
fontWeight: 400,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ticket
|
||||||
|
? `Ticket #${ticket.number} (created ${new Date(
|
||||||
|
ticket.createdAt,
|
||||||
|
).toLocaleDateString()})`
|
||||||
|
: ""}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,22 +1,31 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { ConversationHeader } from "@chatscope/chat-ui-kit-react";
|
||||||
import { Grid } from "@mui/material";
|
import { Grid } from "@mui/material";
|
||||||
|
|
||||||
type LayoutProps = {
|
type LayoutProps = {
|
||||||
detail: any;
|
detail: any;
|
||||||
edit: any;
|
edit: any;
|
||||||
|
header: any;
|
||||||
params: {
|
params: {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Layout({ detail, edit, params: { id } }: LayoutProps) {
|
export default function Layout({ detail, edit, header, params: { id } }: LayoutProps) {
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={0} sx={{ height: "100vh" }} direction="row">
|
<Grid container spacing={0} sx={{ height: "100%" }} direction="row" wrap="wrap">
|
||||||
<Grid item sx={{ height: "100vh" }} xs={9}>
|
<Grid item sx={{ order: 0, display: { xs: "block", md: "none" } }} xs={12}>
|
||||||
|
<ConversationHeader>
|
||||||
|
<ConversationHeader.Content>
|
||||||
|
{header}
|
||||||
|
</ConversationHeader.Content>
|
||||||
|
</ConversationHeader>
|
||||||
|
</Grid>
|
||||||
|
<Grid item sx={{ height: {xs: "auto", md: "100%"}, order: {xs: 3, md: 1} }} xs={12} md={9}>
|
||||||
{detail}
|
{detail}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={3} sx={{ height: "100vh" }}>
|
<Grid item xs={12} md={3} sx={{ height: {xs: "auto", md: "100%"}, order: 2 }}>
|
||||||
{edit}
|
{edit}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ export const List: FC<ListProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ height: "100vh", backgroundColor: lightGray, p: 3 }}>
|
<Box sx={{ height: "100%", p: 3 }}>
|
||||||
<Grid container direction="column">
|
<Grid container direction="column" sx={{ height: "100%", flexWrap: "nowrap" }}>
|
||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
container
|
container
|
||||||
|
|
@ -52,14 +52,13 @@ export const List: FC<ListProps> = ({
|
||||||
{buttons}
|
{buttons}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item sx={{flexGrow: 1}}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
mt: 2,
|
mt: 2,
|
||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
border: 0,
|
border: 0,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "calc(100vh - 100px)",
|
|
||||||
".MuiDataGrid-row": {
|
".MuiDataGrid-row": {
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue