Overview & recent fixes, auth updates, SWR fixes
This commit is contained in:
parent
7df947f35a
commit
8d86db882d
29 changed files with 1252 additions and 1221 deletions
|
|
@ -96,7 +96,7 @@ const SearchResult: FC<SearchResultProps> = ({ props, option }) => {
|
|||
export const SearchBox: FC = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedValue, setSelectedValue] = useState(null);
|
||||
const [searchTerms, setSearchTerms] = useState(null);
|
||||
const [searchTerms, setSearchTerms] = useState("");
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const { data, error }: any = useSWR({
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ export const StyledDataGrid: FC<StyledDataGridProps> = ({
|
|||
height,
|
||||
".MuiDataGrid-row": {
|
||||
cursor: "pointer",
|
||||
"&:hover": {
|
||||
backgroundColor: "#1982fc33 !important",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
".MuiDataGrid-row:nth-of-type(1n)": {
|
||||
backgroundColor: "#f3f3f3",
|
||||
|
|
@ -71,7 +75,7 @@ export const StyledDataGrid: FC<StyledDataGridProps> = ({
|
|||
density="compact"
|
||||
pagination
|
||||
initialState={{
|
||||
pagination: { paginationModel: { pageSize: 20 } },
|
||||
pagination: { paginationModel: { pageSize: 25 } },
|
||||
}}
|
||||
pageSizeOptions={[5, 10, 25]}
|
||||
paginationMode="client"
|
||||
|
|
|
|||
|
|
@ -18,12 +18,24 @@ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
|
|||
const router = useRouter();
|
||||
const { data: session } = useSession({ required: true });
|
||||
const timeoutRef = useRef(null);
|
||||
const [hashCheckComplete, setHashCheckComplete] = useState(false);
|
||||
const [authenticated, setAuthenticated] = useState(false);
|
||||
const [display, setDisplay] = useState("none");
|
||||
const url = `/zammad${path}`;
|
||||
const id = url.replace(/[^a-zA-Z0-9]/g, "");
|
||||
|
||||
useEffect(() => {
|
||||
const hash = window?.location?.hash;
|
||||
if (hash && hash.startsWith("#ticket/zoom/")) {
|
||||
const ticketID = hash.split("/").pop();
|
||||
router.push(`/tickets/${ticketID}`);
|
||||
}
|
||||
setHashCheckComplete(true);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!hashCheckComplete) return;
|
||||
|
||||
const checkAuthenticated = async () => {
|
||||
const res = await fetch("/zammad/auth/sso", {
|
||||
method: "GET",
|
||||
|
|
@ -38,7 +50,7 @@ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
|
|||
};
|
||||
|
||||
checkAuthenticated();
|
||||
}, [path]);
|
||||
}, [path, hashCheckComplete]);
|
||||
|
||||
useEffect(() => {
|
||||
if (session === null) {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import {
|
|||
DialogActions,
|
||||
DialogContent,
|
||||
TextField,
|
||||
Autocomplete
|
||||
Autocomplete,
|
||||
} from "@mui/material";
|
||||
import { useSWRConfig } from "swr";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
import { createTicketMutation } from "app/_graphql/createTicketMutation";
|
||||
|
||||
interface TicketCreateDialogProps {
|
||||
|
|
@ -31,8 +31,8 @@ export const TicketCreateDialog: FC<TicketCreateDialogProps> = ({
|
|||
const [tags, setTags] = useState([]);
|
||||
const [title, setTitle] = useState("");
|
||||
const [body, setBody] = useState("");
|
||||
const backgroundColor = kind === "note" ? "#FFB620" : "#1982FC";
|
||||
const color = kind === "note" ? "black" : "white";
|
||||
const backgroundColor = "#1982FC";
|
||||
const color = "white";
|
||||
const { fetcher } = useSWRConfig();
|
||||
const ticket = {
|
||||
customerId: customerID,
|
||||
|
|
@ -46,16 +46,28 @@ export const TicketCreateDialog: FC<TicketCreateDialogProps> = ({
|
|||
body,
|
||||
type: kind,
|
||||
internal: kind === "note",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const { data: users, error: usersError }: any = useSWR({
|
||||
url: "/api/v1/users",
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
console.log({ users, usersError });
|
||||
|
||||
const customers =
|
||||
users?.filter((user: any) => user.role_ids.includes(3)) ?? [];
|
||||
const formattedCustomers = customers.map((customer: any) => ({
|
||||
label: customer.login,
|
||||
id: `${customer.id}`,
|
||||
}));
|
||||
const createTicket = async () => {
|
||||
await fetcher({
|
||||
document: createTicketMutation,
|
||||
variables: {
|
||||
input: {
|
||||
ticket
|
||||
ticket,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -78,10 +90,13 @@ export const TicketCreateDialog: FC<TicketCreateDialogProps> = ({
|
|||
<Grid item>
|
||||
<Autocomplete
|
||||
disablePortal
|
||||
options={[{ label: "Test One", id: 1 }, { label: "Test Two", id: 2 }]}
|
||||
options={formattedCustomers}
|
||||
value={customerID}
|
||||
sx={{ width: 300 }}
|
||||
onChange={(e: any) => setCustomerID(e.target.value)}
|
||||
renderInput={(params) => <TextField {...params} label="Customer" />}
|
||||
onChange={(e: any) => setCustomerID(e.target.value.id)}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label="Customer" />
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
|
|
@ -129,7 +144,7 @@ export const TicketCreateDialog: FC<TicketCreateDialogProps> = ({
|
|||
}}
|
||||
onClick={createTicket}
|
||||
>
|
||||
{kind === "note" ? "Save Note" : "Send Reply"}
|
||||
Create Ticket
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -3,23 +3,57 @@
|
|||
import { FC, useEffect, useState } from "react";
|
||||
import useSWR from "swr";
|
||||
import { TicketList } from "./TicketList";
|
||||
import { getTicketOverviewCountsQuery } from "app/_graphql/getTicketOverviewCountsQuery";
|
||||
import { getTicketsByOverviewQuery } from "app/_graphql/getTicketsByOverviewQuery";
|
||||
|
||||
type ZammadOverviewProps = {
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const ZammadOverview: FC<ZammadOverviewProps> = ({ name, id }) => {
|
||||
export const ZammadOverview: FC<ZammadOverviewProps> = ({ name }) => {
|
||||
const [overviewID, setOverviewID] = useState(null);
|
||||
const [tickets, setTickets] = useState([]);
|
||||
const [error, setError] = useState(null);
|
||||
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||
|
||||
const { data: overviewData, error: overviewError }: any = useSWR(
|
||||
{
|
||||
document: getTicketsByOverviewQuery,
|
||||
variables: { overviewId: `gid://zammad/Overview/${id}`, pageSize: 250 },
|
||||
document: getTicketOverviewCountsQuery,
|
||||
},
|
||||
{ refreshInterval: 10000 },
|
||||
);
|
||||
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||
{
|
||||
document: getTicketsByOverviewQuery,
|
||||
variables: { overviewId: overviewID, pageSize: 250 },
|
||||
},
|
||||
{ refreshInterval: 10000 },
|
||||
);
|
||||
const overviewLookup = {
|
||||
Assigned: "My Assigned Tickets",
|
||||
Open: "Open Tickets",
|
||||
Urgent: "Escalated Tickets",
|
||||
Unassigned: "Unassigned & Open Tickets",
|
||||
};
|
||||
|
||||
const findOverviewByName = (name: string) => {
|
||||
const fullName = overviewLookup[name];
|
||||
return overviewData?.ticketOverviews?.edges?.find(
|
||||
(overview: any) => overview.node.name === fullName,
|
||||
)?.node?.id;
|
||||
};
|
||||
useEffect(() => {
|
||||
if (overviewData) {
|
||||
setOverviewID(findOverviewByName(name));
|
||||
}
|
||||
}, [overviewData, name]);
|
||||
console.log({
|
||||
name,
|
||||
overviewID,
|
||||
overviewData,
|
||||
overviewError,
|
||||
ticketData,
|
||||
ticketError,
|
||||
});
|
||||
|
||||
const restFetcher = (url: string) => fetch(url).then((r) => r.json());
|
||||
const { data: recent } = useSWR("/api/v1/recent_view", restFetcher);
|
||||
|
|
@ -41,6 +75,8 @@ export const ZammadOverview: FC<ZammadOverviewProps> = ({ name, id }) => {
|
|||
const edges = ticketData?.ticketsByOverview?.edges;
|
||||
if (edges) {
|
||||
const nodes = edges.map((edge: any) => edge.node);
|
||||
console.log({ nodes });
|
||||
setError(null);
|
||||
setTickets(sortTickets(nodes));
|
||||
}
|
||||
|
||||
|
|
@ -72,6 +108,6 @@ export const ZammadOverview: FC<ZammadOverviewProps> = ({ name, id }) => {
|
|||
}, [name]);
|
||||
|
||||
const shouldRender = tickets && !error;
|
||||
|
||||
console.log({ shouldRender, tickets, error });
|
||||
return shouldRender && <TicketList title={name} tickets={tickets} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,14 +21,6 @@ export async function generateMetadata({
|
|||
};
|
||||
}
|
||||
|
||||
const overviews = {
|
||||
assigned: 1,
|
||||
unassigned: 2,
|
||||
recent: 3,
|
||||
open: 5,
|
||||
urgent: 7,
|
||||
};
|
||||
|
||||
type PageProps = {
|
||||
params: {
|
||||
overview: string;
|
||||
|
|
@ -37,7 +29,6 @@ type PageProps = {
|
|||
|
||||
export default function Page({ params: { overview } }: PageProps) {
|
||||
const section = getSection(overview);
|
||||
console.log({ section });
|
||||
|
||||
return <ZammadOverview name={section} id={overviews[overview]} />;
|
||||
return <ZammadOverview name={section} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
|||
{ refreshInterval: 2000 },
|
||||
);
|
||||
|
||||
const { data: recentViewData, error: recentViewError }: any = useSWR({
|
||||
url: "/api/v1/recent_view",
|
||||
method: "POST",
|
||||
body: JSON.stringify({ object: "Ticket", o_id: id }),
|
||||
});
|
||||
|
||||
console.log({ recentViewData, recentViewError });
|
||||
|
||||
const ticket = ticketData?.ticket;
|
||||
const ticketArticles = ticketArticlesData?.ticketArticles;
|
||||
const externalArticles = ticketArticles?.edges.filter(
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
const { data: states } = useSWR("/api/v1/ticket_states", restFetcher);
|
||||
const { data: priorities } = useSWR("/api/v1/ticket_priorities", restFetcher);
|
||||
const { data: recent } = useSWR("/api/v1/recent_view", restFetcher);
|
||||
console.log({ recent });
|
||||
|
||||
// const { data: tags } = useSWR("/api/v1/tags", restFetcher);
|
||||
const filteredStates = states?.filter(
|
||||
(state: any) => !["new", "merged", "removed"].includes(state.name),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { FC, PropsWithChildren, useState, useEffect } from "react";
|
||||
import { FC, PropsWithChildren, useState } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { CssBaseline } from "@mui/material";
|
||||
import { CookiesProvider } from "react-cookie";
|
||||
|
|
@ -24,25 +24,46 @@ export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
|
|||
typeof window !== "undefined" && window.location.origin
|
||||
? window.location.origin
|
||||
: null;
|
||||
const client = new GraphQLClient(`${origin}/zammad/graphql`, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
});
|
||||
const client = new GraphQLClient(`${origin}/zammad/graphql`);
|
||||
const messages: any = { en: locales.en, fr: locales.fr };
|
||||
const locale = "en";
|
||||
const fetchAndCheckAuth = async ({ document, variables }: any) => {
|
||||
const fetchAndCheckAuth = async ({
|
||||
document,
|
||||
variables,
|
||||
url,
|
||||
method,
|
||||
body,
|
||||
}: any) => {
|
||||
const requestHeaders = {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
"X-CSRF-Token": csrfToken,
|
||||
};
|
||||
const { data, headers, status } = await client.rawRequest(
|
||||
document,
|
||||
variables,
|
||||
requestHeaders,
|
||||
);
|
||||
let responseData = null;
|
||||
let responseHeaders = new Headers();
|
||||
let responseStatus = null;
|
||||
|
||||
if (status !== 200) {
|
||||
if (document) {
|
||||
const { data, headers, status } = await client.rawRequest(
|
||||
document,
|
||||
variables,
|
||||
requestHeaders,
|
||||
);
|
||||
responseData = data;
|
||||
responseHeaders = headers;
|
||||
responseStatus = status;
|
||||
} else {
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: requestHeaders,
|
||||
body,
|
||||
});
|
||||
responseData = await res.json();
|
||||
responseHeaders = res.headers;
|
||||
responseStatus = res.status;
|
||||
}
|
||||
|
||||
if (responseStatus !== 200) {
|
||||
const res = await fetch("/zammad/auth/sso", {
|
||||
method: "GET",
|
||||
redirect: "manual",
|
||||
|
|
@ -53,18 +74,30 @@ export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const token = headers.get("CSRF-Token");
|
||||
const token = responseHeaders.get("CSRF-Token");
|
||||
setCsrfToken(token);
|
||||
|
||||
return data;
|
||||
return responseData;
|
||||
};
|
||||
|
||||
const graphQLFetcher = async ({ document, variables }: any) => {
|
||||
const multiFetcher = async ({
|
||||
document,
|
||||
variables,
|
||||
url,
|
||||
method,
|
||||
body,
|
||||
}: any) => {
|
||||
let checks = 0;
|
||||
let data = null;
|
||||
|
||||
while (!data && checks < 2) {
|
||||
data = await fetchAndCheckAuth({ document, variables });
|
||||
data = await fetchAndCheckAuth({
|
||||
document,
|
||||
variables,
|
||||
url,
|
||||
method,
|
||||
body,
|
||||
});
|
||||
checks++;
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +108,7 @@ export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
|
|||
<>
|
||||
<CssBaseline />
|
||||
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
|
||||
<SWRConfig value={{ fetcher: graphQLFetcher }}>
|
||||
<SWRConfig value={{ fetcher: multiFetcher }}>
|
||||
<SessionProvider>
|
||||
<CookiesProvider>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue