GraphQL and MUI license updates

This commit is contained in:
Darren Clarke 2023-07-17 12:23:12 +00:00 committed by GitHub
parent 90143e5e41
commit 21db95a8e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 811 additions and 207 deletions

View file

@ -4,6 +4,7 @@ import { FC } from "react";
import { Grid, Box } from "@mui/material";
import { GridColDef } from "@mui/x-data-grid-pro";
import { StyledDataGrid } from "../../../_components/StyledDataGrid";
import { Button } from "app/_components/Button";
import { typography } from "app/_styles/theme";
import { useRouter } from "next/navigation";
@ -46,18 +47,29 @@ export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
return (
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
<Grid container direction="column">
<Grid item>
<Box
sx={{
backgroundColor: "#ddd",
px: "8px",
pb: "16px",
...typography.h4,
fontSize: 24,
}}
>
{title}
</Box>
<Grid
item
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Box
sx={{
backgroundColor: "#ddd",
px: "8px",
pb: "16px",
...typography.h4,
fontSize: 24,
}}
>
{title}
</Box>
</Grid>
<Grid item>
<Button href="/tickets/create" text="Create" color="#1982FC" />
</Grid>
</Grid>
<Grid item>
<StyledDataGrid

View file

@ -25,7 +25,7 @@ export const ZammadOverview: FC<ZammadOverviewProps> = ({ name, id }) => {
return (
<>
{shouldRender && <TicketList title="Assigned" tickets={tickets} />}
{shouldRender && <TicketList title={name} tickets={tickets} />}
{ticketError && <div>{ticketError.toString()}</div>}
</>
);

View file

@ -1,6 +1,10 @@
import { Metadata } from "next";
import { ZammadOverview } from "./_components/ZammadOverview";
const getSection = (overview: string) => {
return overview.charAt(0).toUpperCase() + overview.slice(1);
};
type MetadataProps = {
params: {
overview: string;
@ -10,7 +14,7 @@ type MetadataProps = {
export async function generateMetadata({
params: { overview },
}: MetadataProps): Promise<Metadata> {
const section = overview.charAt(0).toUpperCase() + overview.slice(1);
const section = getSection(overview);
return {
title: `Link - ${section} Tickets`,
@ -31,6 +35,7 @@ type PageProps = {
};
export default function Page({ params: { overview } }: PageProps) {
console.log({ overview });
return <ZammadOverview name={overview} id={overviews[overview]} />;
const section = getSection(overview);
return <ZammadOverview name={section} id={overviews[overview]} />;
}

View file

@ -3,6 +3,7 @@
import { FC, useState } from "react";
import useSWR from "swr";
import { getTicketQuery } from "app/_graphql/getTicketQuery";
import { getTicketArticlesQuery } from "app/_graphql/getTicketArticlesQuery";
import {
Grid,
Box,
@ -34,18 +35,26 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
},
{ refreshInterval: 100000 }
);
const { data: ticketArticlesData, error: ticketArticlesError }: any = useSWR(
{
document: getTicketArticlesQuery,
variables: { ticketId: `gid://zammad/Ticket/${id}` },
},
{ refreshInterval: 2000 }
);
console.log({ ticketData, ticketError });
const ticket = ticketData?.ticket;
const ticketArticles = ticketArticlesData?.ticketArticles;
const [dialogOpen, setDialogOpen] = useState(false);
const [articleKind, setArticleKind] = useState<"reply" | "note">("reply");
const closeDialog = () => setDialogOpen(false);
const shouldRender = ticketData && !ticketError;
const shouldRender =
ticketData && !ticketError && ticketArticlesData && !ticketArticlesError;
return (
shouldRender && (
<>
<Box sx={{ height: "100%", width: "100%" }}>
<MainContainer>
<ChatContainer>
<ConversationHeader>
@ -73,13 +82,13 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
</ConversationHeader.Content>
</ConversationHeader>
<MessageList style={{ marginBottom: 80 }}>
{ticket.articles.edges.map(({ node: article }: any) => (
{ticketArticles.edges.map(({ node: article }: any) => (
<Message
key={article.id}
className={
article.internal
? "internal-note"
: article.sender.name === "Agent"
: article?.sender?.name === "Agent"
? "outgoing-message"
: "incoming-message"
}
@ -171,7 +180,7 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
closeDialog={closeDialog}
kind={articleKind}
/>
</>
</Box>
)
);
};

View file

@ -1,167 +1,186 @@
"use client";
import { FC, /* useEffect, */ useState } from "react";
import { FC, useEffect, useState } from "react";
import {
Grid,
Box,
// Typography,
// TextField,
Stack,
Chip,
Select,
MenuItem,
} from "@mui/material";
import { /* useSWR, */ useSWRConfig } from "swr";
import { MuiChipsInput } from "mui-chips-input";
import useSWR, { useSWRConfig } from "swr";
import { getTicketQuery } from "app/_graphql/getTicketQuery";
import { updateTicketMutation } from "app/_graphql/updateTicketMutation";
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
interface TicketEditProps {
ticket: any;
id: string;
}
export const TicketEdit: FC<TicketEditProps> = ({ ticket }) => {
const [selectedGroup, setSelectedGroup] = useState(1);
const [selectedOwner, setSelectedOwner] = useState(1);
const [selectedPriority, setSelectedPriority] = useState(1);
const [selectedState, setSelectedState] = useState(1);
const [selectedTags] = useState(["tag1", "tag2"]);
export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
const [selectedGroup, setSelectedGroup] = useState("");
const [selectedOwner, setSelectedOwner] = useState("");
const [selectedPriority, setSelectedPriority] = useState("");
const [selectedState, setSelectedState] = useState("");
const [selectedTags, setSelectedTags] = useState([]);
const handleDelete = () => {
console.info("You clicked the delete icon.");
};
const restFetcher = (url: string) => fetch(url).then((r) => r.json());
/*
const { data: groups } = useSWR("/api/v1/groups", restFetcher);
console.log({ groups });
const { data: users } = useSWR("/api/v1/users", restFetcher);
console.log({ users });
const { data: states } = useSWR("/api/v1/ticket_states", restFetcher);
console.log({ states });
const { data: priorities } = useSWR("/api/v1/ticket_priorities", restFetcher);
console.log({ priorities });
*/
const groups = [];
const users = [];
const states = [];
const priorities = [];
const { data: groups } = useSWR("/api/v1/groups", restFetcher);
const { data: users } = useSWR("/api/v1/users", restFetcher);
const { data: states } = useSWR("/api/v1/ticket_states", restFetcher);
const { data: priorities } = useSWR("/api/v1/ticket_priorities", restFetcher);
const { data: tags } = useSWR("/api/v1/tags", restFetcher);
const { fetcher } = useSWRConfig();
const { data: ticketData, error: ticketError }: any = useSWR(
{
document: getTicketQuery,
variables: { ticketId: `gid://zammad/Ticket/${id}` },
},
{ refreshInterval: 100000 }
);
useEffect(() => {
const ticket = ticketData?.ticket;
if (ticket) {
const groupID = ticket.group.id?.split("/").pop();
setSelectedGroup(groupID);
const ownerID = ticket.owner.id?.split("/").pop();
setSelectedOwner(ownerID);
const priorityID = ticket.priority.id?.split("/").pop();
setSelectedPriority(priorityID);
const stateID = ticket.state.id?.split("/").pop();
setSelectedState(stateID);
setSelectedTags(ticket.tags);
}
}, [ticketData, ticketError]);
const updateTicket = async () => {
await fetcher({
const input = {
ownerId: `gid://zammad/User/${selectedOwner}`,
priorityId: `gid://zammad/Ticket::Priority/${selectedPriority}`,
stateId: `gid://zammad/Ticket::State/${selectedState}`,
groupId: `gid://zammad/Group/${selectedGroup}`,
// tags: selectedTags,
};
const res = await fetcher({
document: updateTicketMutation,
variables: {
ticketId: ticket.id,
input: {
ownerId: `gid://zammad/User/${selectedOwner}`,
tags: ["tag1", "tag2"],
},
ticketId: `gid://zammad/Ticket/${id}`,
input,
},
});
console.log({ res });
};
const shouldRender = ticketData && !ticketError;
return (
<Box sx={{ height: "100vh", background: "#ddd", p: 2 }}>
<Grid container direction="column" spacing={3}>
<Grid item>
<Box sx={{ m: 1 }}>Group</Box>
<Select
defaultValue={selectedGroup}
value={selectedGroup}
onChange={(e: any) => {
setSelectedGroup(e.target.value);
updateTicket();
}}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{groups?.map((group: any) => (
<MenuItem key={group.id} value={group.id}>
{group.name}
</MenuItem>
))}
</Select>
</Grid>
<Grid item>
<Box sx={{ m: 1, mt: 0 }}>Owner</Box>
<Select
value={selectedOwner}
onChange={(e: any) => {
setSelectedOwner(e.target.value);
updateTicket();
}}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{users?.map((user: any) => (
<MenuItem key={user.id} value={user.id}>
{user.firstname} {user.lastname}
</MenuItem>
))}
</Select>
</Grid>
<Grid item>
<Box sx={{ m: 1, mt: 0 }}>State</Box>
<Select
value={selectedState}
onChange={(e: any) => setSelectedState(e.target.value)}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{states?.map((state: any) => (
<MenuItem key={state.id} value={state.id}>
{state.name}
</MenuItem>
))}
</Select>
</Grid>
<Grid item>
<Box sx={{ m: 1, mt: 0 }}>Priority</Box>
<Select
value={selectedPriority}
onChange={(e: any) => setSelectedPriority(e.target.value)}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{priorities?.map((priority: any) => (
<MenuItem key={priority.id} value={priority.id}>
{priority.name}
</MenuItem>
))}
</Select>
</Grid>
shouldRender && (
<Box sx={{ height: "100vh", background: "#ddd", p: 2 }}>
<Grid container direction="column" spacing={3}>
<Grid item>
<Box sx={{ m: 1 }}>Group</Box>
<Select
defaultValue={selectedGroup}
value={selectedGroup}
onChange={(e: any) => {
setSelectedGroup(e.target.value);
updateTicket();
}}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{groups?.map((group: any) => (
<MenuItem key={group.id} value={`${group.id}`}>
{group.name}
</MenuItem>
))}
</Select>
</Grid>
<Grid item>
<Box sx={{ m: 1, mt: 0 }}>Owner</Box>
<Select
value={selectedOwner}
onChange={(e: any) => {
setSelectedOwner(e.target.value);
updateTicket();
}}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{users?.map((user: any) => (
<MenuItem key={user.id} value={user.id}>
{user.firstname} {user.lastname}
</MenuItem>
))}
</Select>
</Grid>
<Grid item>
<Box sx={{ m: 1, mt: 0 }}>State</Box>
<Select
value={selectedState}
onChange={(e: any) => {
setSelectedState(e.target.value);
updateTicket();
}}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{states?.map((state: any) => (
<MenuItem key={state.id} value={state.id}>
{state.name}
</MenuItem>
))}
</Select>
</Grid>
<Grid item>
<Box sx={{ m: 1, mt: 0 }}>Priority</Box>
<Select
value={selectedPriority}
onChange={(e: any) => {
setSelectedPriority(e.target.value);
updateTicket();
}}
size="small"
sx={{
width: "100%",
backgroundColor: "white",
}}
>
{priorities?.map((priority: any) => (
<MenuItem key={priority.id} value={priority.id}>
{priority.name}
</MenuItem>
))}
</Select>
</Grid>
<Grid item>
<Box sx={{ mb: 1 }}>Tags</Box>
<Stack
direction="row"
spacing={1}
sx={{
backgroundColor: "white",
p: 1,
borderRadius: "6px",
border: "1px solid #bbb",
minHeight: 120,
}}
flexWrap="wrap"
>
{selectedTags.map((tag: string) => (
<Chip key={tag} label={tag} onDelete={handleDelete} />
))}
</Stack>
<Grid item>
<Box sx={{ mb: 1 }}>Tags</Box>
<MuiChipsInput
sx={{ backgroundColor: "white", width: "100%" }}
value={selectedTags}
onChange={(tags: any) => {
setSelectedTags(tags);
updateTicket();
}}
/>
</Grid>
</Grid>
</Grid>
</Box>
</Box>
)
);
};

View file

@ -1,5 +1,11 @@
import { TicketEdit } from "./_components/TicketEdit";
export default function Page() {
return <TicketEdit ticket={undefined} />;
type PageProps = {
params: {
id: string;
};
};
export default function Page({ params: { id } }: PageProps) {
return <TicketEdit id={id} />;
}

View file

@ -1,6 +1,5 @@
"use client";
import { ReactNode } from "react";
import { Grid } from "@mui/material";
type LayoutProps = {

View file

@ -18,11 +18,12 @@ export const Button: FC<ButtonProps> = ({ text, color, href }) => (
sx={{
fontFamily: "Poppins, sans-serif",
fontWeight: 700,
borderRadius: 999,
borderRadius: 2,
backgroundColor: color,
padding: "6px 30px",
margin: "20px 0px",
whiteSpace: "nowrap",
textTransform: "none",
}}
>
{text}

View file

@ -9,6 +9,11 @@ import { SWRConfig } from "swr";
import { GraphQLClient } from "graphql-request";
import { AdapterDateFns } from "@mui/x-date-pickers-pro/AdapterDateFns";
import { LocalizationProvider } from "@mui/x-date-pickers-pro";
import { LicenseInfo } from "@mui/x-date-pickers-pro";
LicenseInfo.setLicenseKey(
"7c9bf25d9e240f76e77cbf7d2ba58a23Tz02NjU4OCxFPTE3MTU4NjIzMzQ2ODgsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI="
);
export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
const [csrfToken, setCsrfToken] = useState("");

View file

@ -0,0 +1,21 @@
import { gql } from 'graphql-request';
export const getTicketArticlesQuery = gql`
query getTicketArticles($ticketId: ID!) {
ticketArticles(ticket: { ticketId: $ticketId }) {
edges {
node {
id
body
internal
type {
name
}
sender {
name
}
}
}
}
}
`;

View file

@ -20,20 +20,13 @@ query getTicket($ticketId: ID!) {
id
email
}
articles {
edges {
node {
id
body
internal
type {
name
}
sender {
name
}
}
}
group {
id
name
}
priority {
id
name
}
}
}

View file

@ -6,8 +6,6 @@ import "@fontsource/poppins/700.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/700.css";
import "@fontsource/playfair-display/900.css";
// import getConfig from "next/config";
// import { LicenseInfo } from "@mui/x-data-grid-pro";
import { MultiProvider } from "./_components/MultiProvider";
export const metadata: Metadata = {
@ -19,9 +17,6 @@ type LayoutProps = {
};
export default function Layout({ children }: LayoutProps) {
// const { publicRuntimeConfig } = getConfig();
// LicenseInfo.setLicenseKey(publicRuntimeConfig.muiLicenseKey);
return (
<html lang="en">
<body>