Move in progress apps temporarily

This commit is contained in:
Darren Clarke 2023-03-07 14:09:49 +00:00
parent ba04aa108c
commit 6eaaf8e9be
360 changed files with 6171 additions and 55 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@ node_modules
build/**
dist/**
.next/**
docker/zammad/auto_install/**

View file

@ -0,0 +1,97 @@
import { FC, useState } from "react";
import { Grid, Button, Dialog, DialogActions, DialogContent, TextField } from "@mui/material";
// import { request, gql } from "graphql-request";
interface ArticleCreateDialogProps {
ticketID: string;
open: boolean;
closeDialog: () => void;
kind: "reply" | "note";
}
export const ArticleCreateDialog: FC<ArticleCreateDialogProps> = ({ ticketID, open, closeDialog, kind }) => {
console.log({ ticketID })
const [body, setBody] = useState("");
const backgroundColor = kind === "reply" ? "#1982FC" : "#FFB620";
const color = kind === "reply" ? "white" : "black";
const origin = typeof window !== 'undefined' && window.location.origin
? window.location.origin
: '';
const createArticle = async () => {
// const token = document?.querySelector('meta[name="csrf-token"]').getAttribute('content');
// console.log({ token })
const res = await fetch(`${origin}/api/v1/ticket_articles`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": "BG3wYuvTgi4ALfaZ-Mdq6i08wRFRJHeCPJbfGjfVarLRhwaxRC8J-AZvGiSNOiWrN38WT3C9WGLhcmaMb0AqBQ",
},
body: JSON.stringify({
ticket_id: ticketID,
body,
internal: kind === "note",
sender: "Agent",
}),
});
console.log({ res })
/*
const document = gql`
mutation {
ticketUpdate(
input: {
ticketId: "1"
body: "This is a test article"
internal: false
}
) {
article {
id
}
}
}
`;
const data = await request({
url: `${origin}/graphql`,
document,
});
console.log({ data })
*/
closeDialog();
setBody("");
}
return (
<Dialog open={open} maxWidth="sm" fullWidth >
<DialogContent>
<TextField label={kind === "reply" ? "Write reply" : "Write internal note"} multiline rows={10} fullWidth value={body} onChange={(e: any) => setBody(e.target.value)} />
</DialogContent>
<DialogActions sx={{ px: 3, pt: 0, pb: 3 }}>
<Grid container justifyContent="space-between">
<Grid item>
<Button sx={{
backgroundColor: "white", color: "#666", fontFamily: "Poppins, sans-serif",
fontWeight: 700,
borderRadius: 2,
textTransform: "none",
}}
onClick={() => { setBody(""); closeDialog() }}>Cancel</Button>
</Grid>
<Grid item>
<Button sx={{
backgroundColor, color, fontFamily: "Poppins, sans-serif",
fontWeight: 700,
borderRadius: 2,
textTransform: "none",
px: 3
}}
onClick={createArticle}
>{kind === "reply" ? "Send Reply" : "Save Note"}</Button>
</Grid>
</Grid>
</DialogActions>
</Dialog >
);
};

View file

@ -1,23 +1,25 @@
import { FC, useEffect } from "react";
import { Grid, Box, Typography, Button } from "@mui/material";
import { FC, useState } from "react";
import { Grid, Box, Typography, Button, Dialog, DialogActions, DialogContent } from "@mui/material";
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
import {
MainContainer,
ChatContainer,
MessageList,
Message,
MessageInput,
Conversation,
ConversationHeader,
} from "@chatscope/chat-ui-kit-react";
import { ArticleCreateDialog } from "./ArticleCreateDialog";
interface TicketDetailProps {
ticket: any;
articles: any[];
}
export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
console.log({ here: "here", ticket });
export const TicketDetail: FC<TicketDetailProps> = ({ ticket }) => {
console.log({ ticket })
const [dialogOpen, setDialogOpen] = useState(false);
const [articleKind, setArticleKind] = useState<"reply" | "note">("reply");
const closeDialog = () => setDialogOpen(false);
return (
<>
<MainContainer>
@ -27,7 +29,6 @@ export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
<Box
sx={{
width: "100%",
textAlign: "center",
fontWeight: "bold",
}}
@ -42,18 +43,18 @@ export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
variant="h6"
sx={{ fontFamily: "Roboto", fontWeight: 400 }}
>{`Ticket #${ticket.number} (created ${new Date(
ticket.created_at
ticket.createdAt
).toLocaleDateString()})`}</Typography>
</Box>
</ConversationHeader.Content>
</ConversationHeader>
<MessageList style={{ marginBottom: 80 }}>
{articles.map((article: any) => (
{ticket.articles.edges.map(({ node: article }: any) => (
<Message
className={
article.internal
? "internal-note"
: article.sender === "Agent"
: article.sender.name === "Agent"
? "outgoing-message"
: "incoming-message"
}
@ -63,8 +64,7 @@ export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
sender: article.from,
direction:
article.sender === "Agent" ? "outgoing" : "incoming",
position: "last",
type: "html",
position: "single",
}}
/>
))}
@ -92,8 +92,7 @@ export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
spacing={4}
justifyContent="center"
alignItems="center"
alignContent={"center"}
sx={{ height: 72, pt: 2 }}
alignContent="center"
>
<Grid item>
<Button
@ -109,6 +108,11 @@ export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
margin: "20px 0px",
whiteSpace: "nowrap",
py: "10px",
mt: 2
}}
onClick={() => {
setArticleKind("reply");
setDialogOpen(true);
}}
>
Reply to ticket
@ -129,6 +133,11 @@ export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
margin: "20px 0px",
whiteSpace: "nowrap",
py: "10px",
mt: 2
}}
onClick={() => {
setArticleKind("note");
setDialogOpen(true);
}}
>
Write note to agent
@ -137,6 +146,7 @@ export const TicketDetail: FC<TicketDetailProps> = ({ ticket, articles }) => {
</Grid>
</Box>
</MainContainer>
<ArticleCreateDialog ticketID={ticket.internalId} open={dialogOpen} closeDialog={closeDialog} kind={articleKind} />
</>
);
};

View file

@ -1,15 +1,6 @@
import { FC, useEffect } from "react";
import { Grid, Box, Typography, TextField } from "@mui/material";
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
import {
MainContainer,
ChatContainer,
MessageList,
Message,
MessageInput,
Conversation,
ConversationHeader,
} from "@chatscope/chat-ui-kit-react";
interface TicketEditProps {
ticket: any;

View file

@ -3,18 +3,13 @@ module.exports = {
locales: ["en", "fr"],
defaultLocale: "en",
},
/*
async redirects() {
return [
{
source: "/zammad#ticket/zoom/:path*",
destination: "/ticket/:path*",
permanent: true,
},
];
},
*/
rewrites: async () => ({
beforeFiles: [
{
source: "/#ticket/zoom/:path*",
destination: "/ticket/:path*",
},
],
fallback: [
{
source: "/:path*",

View file

@ -18,6 +18,7 @@
"@fontsource/playfair-display": "^4.5.13",
"@fontsource/poppins": "^4.5.10",
"@fontsource/roboto": "^4.5.8",
"graphql-request": "^5.2.0",
"@mui/icons-material": "^5",
"@mui/lab": "^5.0.0-alpha.118",
"@mui/material": "^5",

View file

@ -13,8 +13,16 @@ const withAuthInfo =
return res.redirect("/login");
}
try {
const res2 = await fetch(`http://127.0.0.1:8001`);
console.log({ res2 })
} catch (e) {
console.log({ e })
}
console.log({ zammad: process.env.ZAMMAD_URL })
req.headers['X-Forwarded-User'] = session.email.toLowerCase();
req.headers['Host'] = 'zammad.example.com';
req.headers['host'] = 'zammad.example.com';
console.log({ headers: req.headers })

View file

@ -1,6 +1,7 @@
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import Head from "next/head";
import useSWR from "swr";
import { request, gql } from "graphql-request";
import { NextPage } from "next";
import { Grid, } from "@mui/material";
import { Layout } from "components/Layout";
@ -15,22 +16,72 @@ const Ticket: NextPage<TicketProps> = ({ id }) => {
const origin = typeof window !== 'undefined' && window.location.origin
? window.location.origin
: '';
const fetcher = async (url: string) => {
const res = await fetch(url);
return res.json();
}
const graphQLFetcher = async ({ document, variables }: any) => {
const data = await request({
url: `${origin}/graphql`,
document,
variables
})
console.log({ data })
const { data: ticketData, error: ticketError } = useSWR(
`${origin}/api/v1/tickets/${id}`,
fetcher
return data;
};
const { data: ticketData, error: ticketError }: any = useSWR(
{
document: gql`
query getTicket($ticketId: Int!) {
ticket(
ticket: {
ticketInternalId: $ticketId
}
) {
id,
internalId,
title,
note,
number,
createdAt,
updatedAt,
closeAt,
articles {
edges {
node {
id,
body,
internal,
sender {
name
}
}
}
}
}}`, variables: { ticketId: parseInt(id, 10) }
},
graphQLFetcher,
{ refreshInterval: 1000 }
);
const { data: articlesData, error: articlesError } = useSWR(
`${origin}/api/v1/ticket_articles/by_ticket/${id}`,
fetcher
const { data: graphqlData2, error: graphqlError2 } = useSWR(
{
document: gql`
{
__schema {
queryType {
name
fields {
name
}
}
}
}`, variables: {}
},
graphQLFetcher
);
const shouldRender = !ticketError && !articlesError && ticketData && articlesData && !ticketData.error && !articlesData.error;
const shouldRender = !ticketError && ticketData;
return (
<Layout>
@ -39,13 +90,13 @@ const Ticket: NextPage<TicketProps> = ({ id }) => {
</Head>
{shouldRender && (
<Grid container spacing={0} sx={{ height: "100vh" }} direction="row">
<Grid item sx={{ height: "100vh" }} xs={10}>
<Grid item sx={{ height: "100vh" }} xs={12}>
<TicketDetail ticket={ticketData} articles={articlesData} />
</Grid>
<Grid item xs={2} sx={{ height: "100vh" }}>
<TicketEdit ticket={ticketData} />
<TicketDetail ticket={ticketData.ticket} />
</Grid>
{/*<Grid item xs={0} sx={{ height: "100vh" }}>
<TicketEdit ticket={ticketData.ticket} />
</Grid>*/}
</Grid>)}
{ticketError && <div>{ticketError.toString()}</div>}
</Layout>

View file

@ -12,16 +12,30 @@ body {
font-family: Roboto !important;
font-size: 16px !important;
padding: 20px !important;
width: 300px;
}
.incoming-message .cs-message__content {
color: white !important;
background-color: #43CC47 !important;
color: black !important;
background-color: #ddd !important;
border-radius: 14px !important;
margin: 12px;
font-family: Roboto !important;
font-size: 16px !important;
padding: 20px !important;
}
.outgoing-message {
margin-left: calc(100% - 550px) !important;
margin-right: 0 !important;
}
.internal-note {
margin-left: calc(100% - 350px) !important;
margin-right: 0 !important;
}
.outgoing-message .cs-message__content {
@ -32,6 +46,7 @@ body {
font-family: Roboto !important;
font-size: 16px !important;
padding: 20px !important;
width: 500px;
}
.incoming-message .cs-message__content a {
@ -40,6 +55,7 @@ body {
.outgoing-message .cs-message__content a {
color: white !important;
}
.cs-message-input__content-editor-wrapper {

View file

@ -4,6 +4,7 @@ x-zammad-vars:
&common-zammad-variables
MEMCACHE_SERVERS: "zammad-memcached:11211"
REDIS_URL: "redis://zammad-redis:6379"
ENABLE_EXPERIMENTAL_MOBILE_FRONTEND: "true"
x-metamigo-vars:
&common-metamigo-variables
@ -41,7 +42,6 @@ services:
container_name: zammad-elasticsearch
environment:
- discovery.type=single-node
- ingest.geoip.downloader.enabled=false
build: ./docker/elasticsearch
restart: ${RESTART}
volumes:

View file

@ -1 +1,6 @@
FROM zammad/zammad-docker-compose:5.3.1-53 AS builder
COPY auto_install ${ZAMMAD_TMP_DIR}/auto_install
RUN sed -i "s/# create install ready file/bundle exec rake zammad:package:migrate/g" contrib/docker/docker-entrypoint.sh
FROM zammad/zammad-docker-compose:5.3.1-53
COPY --from=builder ${ZAMMAD_TMP_DIR} ${ZAMMAD_TMP_DIR}

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 520 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 557 B

After

Width:  |  Height:  |  Size: 557 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 875 B

After

Width:  |  Height:  |  Size: 875 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 686 KiB

After

Width:  |  Height:  |  Size: 686 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 215 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 632 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 625 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 844 B

After

Width:  |  Height:  |  Size: 844 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 322 B

After

Width:  |  Height:  |  Size: 322 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 570 B

After

Width:  |  Height:  |  Size: 570 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 473 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 447 B

After

Width:  |  Height:  |  Size: 447 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more