Sidebar and edit updates
This commit is contained in:
parent
d73b194d1f
commit
f13530f043
32 changed files with 3057 additions and 1114 deletions
|
|
@ -17,24 +17,24 @@
|
|||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fontsource/playfair-display": "^5.0.13",
|
||||
"@fontsource/playfair-display": "^5.0.15",
|
||||
"@fontsource/poppins": "^5.0.8",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"@mui/icons-material": "^5",
|
||||
"@mui/lab": "^5.0.0-alpha.146",
|
||||
"@mui/lab": "^5.0.0-alpha.148",
|
||||
"@mui/material": "^5",
|
||||
"@mui/x-data-grid-pro": "^6.16.0",
|
||||
"@mui/x-date-pickers-pro": "^6.16.0",
|
||||
"@opensearch-project/opensearch": "^2.3.1",
|
||||
"@mui/x-data-grid-pro": "^6.16.2",
|
||||
"@mui/x-date-pickers-pro": "^6.16.2",
|
||||
"@opensearch-project/opensearch": "^2.4.0",
|
||||
"cryptr": "^6.3.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"leafcutter-common": "*",
|
||||
"material-ui-popup-state": "^5.0.9",
|
||||
"next": "13.5.3",
|
||||
"next-auth": "^4.23.1",
|
||||
"next-http-proxy-middleware": "^1.2.5",
|
||||
"nodemailer": "^6.9.5",
|
||||
"next": "13.5.4",
|
||||
"next-auth": "^4.23.2",
|
||||
"next-http-proxy-middleware": "^1.2.6",
|
||||
"nodemailer": "^6.9.6",
|
||||
"react": "18.2.0",
|
||||
"react-cookie": "^6.1.1",
|
||||
"react-cookie-consent": "^8.0.1",
|
||||
|
|
@ -48,18 +48,18 @@
|
|||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.0",
|
||||
"@types/node": "^20.8.0",
|
||||
"@types/react": "18.2.24",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@types/node": "^20.8.6",
|
||||
"@types/react": "18.2.28",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"babel-loader": "^9.1.3",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-next": "^13.5.3",
|
||||
"eslint-config-next": "^13.5.4",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
|
|
|
|||
150
apps/link/app/(main)/_components/SearchBox.tsx
Normal file
150
apps/link/app/(main)/_components/SearchBox.tsx
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { FC, useState, useEffect } from "react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
import { Grid, Box, TextField, Autocomplete } from "@mui/material";
|
||||
import { searchQuery } from "@/app/_graphql/searchQuery";
|
||||
import { colors } from "@/app/_styles/theme";
|
||||
|
||||
type SearchResultProps = {
|
||||
props: any;
|
||||
option: any;
|
||||
};
|
||||
|
||||
const SearchInput = (params: any) => (
|
||||
<TextField
|
||||
{...params}
|
||||
placeholder="Search"
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
borderRadius: 10,
|
||||
"& .MuiOutlinedInput-root": {
|
||||
borderRadius: 10,
|
||||
py: 0,
|
||||
legend: {
|
||||
marginLeft: "30px",
|
||||
},
|
||||
},
|
||||
"& .MuiAutocomplete-inputRoot": {
|
||||
paddingLeft: "20px !important",
|
||||
borderRadius: 10,
|
||||
},
|
||||
"& .MuiInputLabel-outlined": {
|
||||
paddingLeft: "20px",
|
||||
},
|
||||
"& .MuiInputLabel-shrink": {
|
||||
marginLeft: "20px",
|
||||
paddingLeft: "10px",
|
||||
paddingRight: 0,
|
||||
background: "white",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const SearchResult: FC<SearchResultProps> = ({ props, option }) => {
|
||||
console.log({ option });
|
||||
|
||||
const { lightGrey, mediumGray, black, white } = colors;
|
||||
|
||||
return (
|
||||
<Box
|
||||
{...props}
|
||||
sx={{
|
||||
px: 2,
|
||||
py: 1.25,
|
||||
":hover": {
|
||||
background: `${lightGrey}`,
|
||||
},
|
||||
a: {
|
||||
color: `${black} !important`,
|
||||
},
|
||||
borderBottom: `1px solid ${mediumGray}`,
|
||||
}}
|
||||
>
|
||||
<Grid container direction="column" spacing={0.1}>
|
||||
<Grid item container direction="row" justifyContent="space-between">
|
||||
<Grid item>
|
||||
<Box
|
||||
sx={{
|
||||
py: 0,
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{option.title}
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
color: "#999",
|
||||
fontSize: 13,
|
||||
wrap: "break-word",
|
||||
}}
|
||||
>
|
||||
{option.note}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const SearchBox: FC = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedValue, setSelectedValue] = useState(null);
|
||||
const [searchTerms, setSearchTerms] = useState(null);
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const { data, error }: any = useSWR({
|
||||
document: searchQuery,
|
||||
variables: {
|
||||
search: searchTerms ?? "",
|
||||
limit: 50,
|
||||
},
|
||||
refreshInterval: 10000,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setOpen(false);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
forcePopupIcon={false}
|
||||
openOnFocus
|
||||
blurOnSelect
|
||||
value={selectedValue}
|
||||
onBlur={() => setOpen(false)}
|
||||
inputValue={searchTerms}
|
||||
onChange={(_event, option, reason) => {
|
||||
if (!option) return;
|
||||
const url = `/tickets/${option.internalId}`;
|
||||
setSelectedValue("");
|
||||
router.push(url);
|
||||
}}
|
||||
onInputChange={(_event, value) => {
|
||||
setSearchTerms(value);
|
||||
}}
|
||||
open={open}
|
||||
onOpen={() => setOpen(true)}
|
||||
noOptionsText="No results"
|
||||
options={data?.search ?? []}
|
||||
getOptionLabel={(option: any) => {
|
||||
if (option) {
|
||||
return option.title;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}}
|
||||
renderOption={(props, option: any) => (
|
||||
<SearchResult props={props} key={option.id} option={option} />
|
||||
)}
|
||||
sx={{ width: "100%" }}
|
||||
renderInput={(params: any) => <SearchInput {...params} />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { FC, useState } from "react";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import useSWR from "swr";
|
||||
import {
|
||||
Box,
|
||||
|
|
@ -26,6 +26,7 @@ import {
|
|||
Assessment as AssessmentIcon,
|
||||
LibraryBooks as LibraryBooksIcon,
|
||||
School as SchoolIcon,
|
||||
Search as SearchIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { usePathname } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
|
|
@ -33,8 +34,8 @@ import Image from "next/image";
|
|||
import LinkLogo from "public/link-logo-small.png";
|
||||
import { useSession, signOut } from "next-auth/react";
|
||||
import { getTicketOverviewCountsQuery } from "app/_graphql/getTicketOverviewCountsQuery";
|
||||
import { SearchBox } from "./SearchBox";
|
||||
|
||||
console.log;
|
||||
const openWidth = 270;
|
||||
const closedWidth = 100;
|
||||
|
||||
|
|
@ -174,13 +175,15 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
},
|
||||
{ refreshInterval: 10000 },
|
||||
);
|
||||
console.log({ overviewData });
|
||||
const findOverviewCountByID = (id: number) =>
|
||||
overviewData?.ticketOverviews?.edges?.find((overview: any) =>
|
||||
overview.node.id.endsWith(`/${id}`),
|
||||
)?.node?.ticketCount ?? 0;
|
||||
const recentCount = 0;
|
||||
const assignedCount = findOverviewCountByID(1);
|
||||
const openCount = findOverviewCountByID(5);
|
||||
const urgentCount = findOverviewCountByID(7);
|
||||
const pendingCount = findOverviewCountByID(3);
|
||||
const unassignedCount = findOverviewCountByID(2);
|
||||
|
||||
const logout = () => {
|
||||
|
|
@ -227,6 +230,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
direction="column"
|
||||
justifyContent="space-between"
|
||||
wrap="nowrap"
|
||||
spacing={0}
|
||||
sx={{ backgroundColor: "#25272A", height: "100%", p: 2 }}
|
||||
>
|
||||
<Grid item container>
|
||||
|
|
@ -313,9 +317,17 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
</Grid>
|
||||
<Grid item>
|
||||
<Box
|
||||
sx={{ height: "0.5px", width: "100%", backgroundColor: "#666" }}
|
||||
sx={{
|
||||
height: "0.5px",
|
||||
width: "100%",
|
||||
backgroundColor: "#666",
|
||||
mb: 2,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<SearchBox />
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
|
|
@ -377,7 +389,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
/>
|
||||
<MenuItem
|
||||
name="Tickets"
|
||||
href="/overview/assigned"
|
||||
href="/overview/recent"
|
||||
Icon={FeaturedPlayListIcon}
|
||||
selected={
|
||||
pathname.startsWith("/overview") ||
|
||||
|
|
@ -397,12 +409,21 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
>
|
||||
<List component="div" disablePadding>
|
||||
<MenuItem
|
||||
name="Assigned"
|
||||
href="/overview/assigned"
|
||||
name="Recent"
|
||||
href="/overview/recent"
|
||||
Icon={FeaturedPlayListIcon}
|
||||
iconSize={0}
|
||||
selected={pathname.endsWith("/overview/assigned")}
|
||||
badge={assignedCount}
|
||||
selected={pathname.endsWith("/overview/recent")}
|
||||
badge={recentCount}
|
||||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Open"
|
||||
href="/overview/open"
|
||||
Icon={FeaturedPlayListIcon}
|
||||
iconSize={0}
|
||||
selected={pathname.endsWith("/overview/open")}
|
||||
badge={openCount}
|
||||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
|
|
@ -415,12 +436,12 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Pending"
|
||||
href="/overview/pending"
|
||||
name="Assigned"
|
||||
href="/overview/assigned"
|
||||
Icon={FeaturedPlayListIcon}
|
||||
iconSize={0}
|
||||
selected={pathname.endsWith("/overview/pending")}
|
||||
badge={pendingCount}
|
||||
selected={pathname.endsWith("/overview/assigned")}
|
||||
badge={assignedCount}
|
||||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { FC, useState, useEffect } from "react";
|
||||
import { FC, useState, useEffect, useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Iframe from "react-iframe";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
|
@ -16,7 +16,8 @@ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
|
|||
hideSidebar = true,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { data: session } = useSession();
|
||||
const { data: session } = useSession({ required: true });
|
||||
const timeoutRef = useRef(null);
|
||||
const [authenticated, setAuthenticated] = useState(false);
|
||||
const [display, setDisplay] = useState("none");
|
||||
const url = `/zammad${path}`;
|
||||
|
|
@ -28,7 +29,7 @@ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
|
|||
method: "GET",
|
||||
redirect: "manual",
|
||||
});
|
||||
|
||||
console.log({ res });
|
||||
if (res.type === "opaqueredirect") {
|
||||
setAuthenticated(true);
|
||||
} else {
|
||||
|
|
@ -39,8 +40,24 @@ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
|
|||
checkAuthenticated();
|
||||
}, [path]);
|
||||
|
||||
if (!session) {
|
||||
console.log("No session");
|
||||
useEffect(() => {
|
||||
if (session === null) {
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
if (session === null) {
|
||||
router.push("/login");
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
if (session !== null) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
|
||||
return () => clearTimeout(timeoutRef.current);
|
||||
}, [session]);
|
||||
|
||||
if (!session || !authenticated) {
|
||||
console.log("Not authenticated");
|
||||
return (
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Grid
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
"use client";
|
||||
|
||||
import { FC, useState } from "react";
|
||||
import {
|
||||
Grid,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
TextField,
|
||||
Autocomplete
|
||||
} from "@mui/material";
|
||||
import { useSWRConfig } from "swr";
|
||||
import { createTicketMutation } from "app/_graphql/createTicketMutation";
|
||||
|
||||
interface TicketCreateDialogProps {
|
||||
open: boolean;
|
||||
closeDialog: () => void;
|
||||
}
|
||||
|
||||
export const TicketCreateDialog: FC<TicketCreateDialogProps> = ({
|
||||
open,
|
||||
closeDialog,
|
||||
}) => {
|
||||
const [kind, setKind] = useState("note");
|
||||
const [customerID, setCustomerID] = useState("");
|
||||
const [groupID, setGroupID] = useState("");
|
||||
const [ownerID, setOwnerID] = useState("");
|
||||
const [priorityID, setPriorityID] = useState("");
|
||||
const [stateID, setStateID] = useState("");
|
||||
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 { fetcher } = useSWRConfig();
|
||||
const ticket = {
|
||||
customerId: customerID,
|
||||
groupId: groupID,
|
||||
ownerId: ownerID,
|
||||
priorityId: priorityID,
|
||||
stateId: stateID,
|
||||
tags,
|
||||
title,
|
||||
article: {
|
||||
body,
|
||||
type: kind,
|
||||
internal: kind === "note",
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const createTicket = async () => {
|
||||
await fetcher({
|
||||
document: createTicketMutation,
|
||||
variables: {
|
||||
input: {
|
||||
ticket
|
||||
},
|
||||
},
|
||||
});
|
||||
closeDialog();
|
||||
setBody("");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} maxWidth="md" fullWidth>
|
||||
<DialogContent>
|
||||
<Grid container direction="column" spacing={2}>
|
||||
<Grid item>
|
||||
<TextField
|
||||
label={"Title"}
|
||||
fullWidth
|
||||
value={title}
|
||||
onChange={(e: any) => setTitle(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Autocomplete
|
||||
disablePortal
|
||||
options={[{ label: "Test One", id: 1 }, { label: "Test Two", id: 2 }]}
|
||||
sx={{ width: 300 }}
|
||||
onChange={(e: any) => setCustomerID(e.target.value)}
|
||||
renderInput={(params) => <TextField {...params} label="Customer" />}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<TextField
|
||||
label={"Details"}
|
||||
multiline
|
||||
rows={10}
|
||||
fullWidth
|
||||
value={body}
|
||||
onChange={(e: any) => setBody(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</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={createTicket}
|
||||
>
|
||||
{kind === "note" ? "Save Note" : "Send Reply"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { Grid, Box } from "@mui/material";
|
||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { StyledDataGrid } from "../../../_components/StyledDataGrid";
|
||||
import { Button } from "../../../../_components/Button";
|
||||
import { typography } from "../../../../_styles/theme";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { TicketCreateDialog } from "./TicketCreateDialog";
|
||||
|
||||
interface TicketListProps {
|
||||
title: string;
|
||||
|
|
@ -14,72 +15,96 @@ interface TicketListProps {
|
|||
}
|
||||
|
||||
export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const router = useRouter();
|
||||
let gridColumns: GridColDef[] = [
|
||||
{
|
||||
field: "number",
|
||||
headerName: "Number",
|
||||
flex: 0.3,
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "title",
|
||||
headerName: "Title",
|
||||
flex: 1.5,
|
||||
flex: 5,
|
||||
},
|
||||
{
|
||||
field: "customer",
|
||||
headerName: "Sender",
|
||||
valueGetter: (params) => params.row?.customer?.fullname,
|
||||
flex: 0.6,
|
||||
flex: 2,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "Created At",
|
||||
valueGetter: (params) => new Date(params.row?.createdAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "updatedAt",
|
||||
headerName: "Updated At",
|
||||
valueGetter: (params) => new Date(params.row?.updatedAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "group",
|
||||
headerName: "Group",
|
||||
valueGetter: (params) => params.row?.group?.name,
|
||||
flex: 0.3,
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
console.log({ tickets });
|
||||
|
||||
const rowClick = ({ row }) => {
|
||||
router.push(`/tickets/${row.internalId}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
|
||||
<Grid container direction="column">
|
||||
<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>
|
||||
<>
|
||||
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
|
||||
<Grid container direction="column">
|
||||
<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={""}
|
||||
onClick={() => setDialogOpen(true)}
|
||||
text="Create"
|
||||
color="#1982FC"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button href="/tickets/create" text="Create" color="#1982FC" />
|
||||
<StyledDataGrid
|
||||
name={title}
|
||||
columns={gridColumns}
|
||||
rows={tickets}
|
||||
onRowClick={rowClick}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<StyledDataGrid
|
||||
name={title}
|
||||
columns={gridColumns}
|
||||
rows={tickets}
|
||||
onRowClick={rowClick}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Box>
|
||||
<TicketCreateDialog
|
||||
open={dialogOpen}
|
||||
closeDialog={() => setDialogOpen(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import useSWR from "swr";
|
||||
import { TicketList } from "./TicketList";
|
||||
import { getTicketsByOverviewQuery } from "../../../../_graphql/getTicketsByOverviewQuery";
|
||||
|
|
@ -11,6 +11,8 @@ type ZammadOverviewProps = {
|
|||
};
|
||||
|
||||
export const ZammadOverview: FC<ZammadOverviewProps> = ({ name, id }) => {
|
||||
const [tickets, setTickets] = useState([]);
|
||||
const [error, setError] = useState(null);
|
||||
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||
{
|
||||
document: getTicketsByOverviewQuery,
|
||||
|
|
@ -19,24 +21,57 @@ export const ZammadOverview: FC<ZammadOverviewProps> = ({ name, id }) => {
|
|||
{ refreshInterval: 10000 },
|
||||
);
|
||||
|
||||
const shouldRender = !ticketError && ticketData;
|
||||
const tickets =
|
||||
ticketData?.ticketsByOverview?.edges.map((edge: any) => edge.node) || [];
|
||||
const restFetcher = (url: string) => fetch(url).then((r) => r.json());
|
||||
const { data: recent } = useSWR("/api/v1/recent_view", restFetcher);
|
||||
|
||||
const sortedTickets = tickets.sort((a: any, b: any) => {
|
||||
if (a.internalId < b.internalId) {
|
||||
return 1;
|
||||
}
|
||||
if (a.internalId > b.internalId) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const sortTickets = (tickets: any) => {
|
||||
return tickets.sort((a: any, b: any) => {
|
||||
if (a.internalId < b.internalId) {
|
||||
return 1;
|
||||
}
|
||||
if (a.internalId > b.internalId) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{shouldRender && <TicketList title={name} tickets={sortedTickets} />}
|
||||
{/*ticketError && <div>{ticketError.toString()}</div>*/}
|
||||
</>
|
||||
);
|
||||
useEffect(() => {
|
||||
if (name != "Recent") {
|
||||
const edges = ticketData?.ticketsByOverview?.edges;
|
||||
if (edges) {
|
||||
const nodes = edges.map((edge: any) => edge.node);
|
||||
setTickets(sortTickets(nodes));
|
||||
}
|
||||
|
||||
if (ticketError) {
|
||||
setError(ticketError);
|
||||
}
|
||||
}
|
||||
}, [ticketData, ticketError]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchRecentTickets = async () => {
|
||||
if (name === "Recent" && recent) {
|
||||
let allTickets = [];
|
||||
for (const rec of recent) {
|
||||
const res = await fetch(`/api/v1/tickets/${rec.o_id}`);
|
||||
const tkt = await res.json();
|
||||
allTickets.push({
|
||||
...tkt,
|
||||
internalId: tkt.id,
|
||||
createdAt: tkt.created_at,
|
||||
updatedAt: tkt.updated_at,
|
||||
});
|
||||
}
|
||||
setTickets(sortTickets(allTickets));
|
||||
console.log({ allTickets });
|
||||
}
|
||||
};
|
||||
fetchRecentTickets();
|
||||
}, [name]);
|
||||
|
||||
const shouldRender = tickets && !error;
|
||||
|
||||
return shouldRender && <TicketList title={name} tickets={tickets} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ export async function generateMetadata({
|
|||
const overviews = {
|
||||
assigned: 1,
|
||||
unassigned: 2,
|
||||
pending: 3,
|
||||
recent: 3,
|
||||
open: 5,
|
||||
urgent: 7,
|
||||
};
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ type PageProps = {
|
|||
|
||||
export default function Page({ params: { overview } }: PageProps) {
|
||||
const section = getSection(overview);
|
||||
console.log({ section });
|
||||
|
||||
return <ZammadOverview name={section} id={overviews[overview]} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ interface ArticleCreateDialogProps {
|
|||
open: boolean;
|
||||
closeDialog: () => void;
|
||||
kind: string;
|
||||
recipient?: string;
|
||||
}
|
||||
|
||||
export const ArticleCreateDialog: FC<ArticleCreateDialogProps> = ({
|
||||
|
|
@ -24,22 +25,29 @@ export const ArticleCreateDialog: FC<ArticleCreateDialogProps> = ({
|
|||
open,
|
||||
closeDialog,
|
||||
kind,
|
||||
recipient,
|
||||
}) => {
|
||||
const [body, setBody] = useState("");
|
||||
const backgroundColor = kind === "note" ? "#FFB620" : "#1982FC";
|
||||
const color = kind === "note" ? "black" : "white";
|
||||
const { fetcher } = useSWRConfig();
|
||||
const article = {
|
||||
body,
|
||||
type: kind,
|
||||
internal: kind === "note",
|
||||
};
|
||||
|
||||
if (kind === "email") {
|
||||
article["to"] = recipient;
|
||||
}
|
||||
|
||||
const createArticle = async () => {
|
||||
await fetcher({
|
||||
document: updateTicketMutation,
|
||||
variables: {
|
||||
ticketId: `gid://zammad/Ticket/${ticketID}`,
|
||||
input: {
|
||||
article: {
|
||||
body,
|
||||
type: kind,
|
||||
internal: kind === "note",
|
||||
},
|
||||
article,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,8 +53,10 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
|||
: null;
|
||||
const mostRecentExternalArticleKind =
|
||||
mostRecentExternalArticle?.type?.name ?? "phone";
|
||||
const mostRecentEmailRecipient = mostRecentExternalArticle?.to?.name ?? "";
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [articleKind, setArticleKind] = useState("phone");
|
||||
const [recipient, setRecipient] = useState("");
|
||||
const closeDialog = () => setDialogOpen(false);
|
||||
|
||||
const shouldRender =
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ import {
|
|||
} from "@mui/material";
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
import { getTicketQuery } from "../../../../../_graphql/getTicketQuery";
|
||||
import { updateTicketMutation } from "../../../../../_graphql/updateTicketMutation";
|
||||
import { updateTagsMutation } from "../../../../../_graphql/updateTagsMutation";
|
||||
import { getTicketQuery } from "app/_graphql/getTicketQuery";
|
||||
import { updateTicketMutation } from "app/_graphql/updateTicketMutation";
|
||||
import { updateTagsMutation } from "app/_graphql/updateTagsMutation";
|
||||
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
|
||||
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
|
||||
|
||||
interface TicketEditProps {
|
||||
id: string;
|
||||
|
|
@ -26,6 +27,8 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
const [selectedOwner, setSelectedOwner] = useState("");
|
||||
const [selectedPriority, setSelectedPriority] = useState("");
|
||||
const [selectedState, setSelectedState] = useState("");
|
||||
const [pendingDate, setPendingDate] = useState(new Date());
|
||||
const [pendingVisible, setPendingVisible] = useState(false);
|
||||
const [selectedTags, setSelectedTags] = useState([]);
|
||||
const handleDelete = () => {
|
||||
console.info("You clicked the delete icon.");
|
||||
|
|
@ -36,8 +39,13 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
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 { 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),
|
||||
);
|
||||
const agents = users?.filter((user: any) => user.role_ids.includes(2)) ?? [];
|
||||
const { fetcher } = useSWRConfig();
|
||||
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||
{
|
||||
|
|
@ -60,6 +68,13 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
setSelectedTags(ticket.tags);
|
||||
}
|
||||
}, [ticketData, ticketError]);
|
||||
|
||||
useEffect(() => {
|
||||
const stateName = filteredStates?.find(
|
||||
(state: any) => state.id === selectedState,
|
||||
)?.name;
|
||||
setPendingVisible(stateName?.includes("pending") ?? false);
|
||||
}, [selectedState]);
|
||||
const updateTicket = async (input: any) => {
|
||||
console.log({ input });
|
||||
const res = await fetcher({
|
||||
|
|
@ -128,14 +143,14 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
{users?.map((user: any) => (
|
||||
<MenuItem key={user.id} value={user.id}>
|
||||
{agents?.map((user: any) => (
|
||||
<MenuItem key={user.id} value={`${user.id}`}>
|
||||
{user.firstname} {user.lastname}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ m: 1, mt: 0 }}>State</Box>
|
||||
<Select
|
||||
value={selectedState}
|
||||
|
|
@ -144,6 +159,7 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
setSelectedState(newState);
|
||||
updateTicket({
|
||||
stateId: `gid://zammad/Ticket::State/${newState}`,
|
||||
pendingTime: pendingDate.toISOString(),
|
||||
});
|
||||
}}
|
||||
size="small"
|
||||
|
|
@ -152,13 +168,35 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
{states?.map((state: any) => (
|
||||
{filteredStates?.map((state: any) => (
|
||||
<MenuItem key={state.id} value={state.id}>
|
||||
{state.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sx={{ display: pendingVisible ? "inherit" : "none" }}
|
||||
>
|
||||
<DatePicker
|
||||
label="Pending Date"
|
||||
value={pendingDate}
|
||||
onChange={(newValue: any) => {
|
||||
console.log(newValue);
|
||||
setPendingDate(newValue);
|
||||
updateTicket({
|
||||
pendingTime: newValue.toISOString(),
|
||||
});
|
||||
}}
|
||||
slotProps={{ textField: { size: "small" } }}
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Box sx={{ m: 1, mt: 0 }}>Priority</Box>
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -8,13 +8,15 @@ interface ButtonProps {
|
|||
text: string;
|
||||
color: string;
|
||||
href: string;
|
||||
onClick: any;
|
||||
}
|
||||
|
||||
export const Button: FC<ButtonProps> = ({ text, color, href }) => (
|
||||
export const Button: FC<ButtonProps> = ({ text, color, href, onClick }) => (
|
||||
<Link href={href} passHref>
|
||||
<MUIButton
|
||||
variant="contained"
|
||||
disableElevation
|
||||
onClick={onClick}
|
||||
sx={{
|
||||
fontFamily: "Poppins, sans-serif",
|
||||
fontWeight: 700,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { FC, PropsWithChildren, useState } from "react";
|
||||
import { FC, PropsWithChildren, useState, useEffect } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { CssBaseline } from "@mui/material";
|
||||
import { CookiesProvider } from "react-cookie";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
|
|
@ -31,22 +32,45 @@ export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
|
|||
});
|
||||
const messages: any = { en: locales.en, fr: locales.fr };
|
||||
const locale = "en";
|
||||
const graphQLFetcher = async ({ document, variables }: any) => {
|
||||
const fetchAndCheckAuth = async ({ document, variables }: any) => {
|
||||
const requestHeaders = {
|
||||
"X-CSRF-Token": csrfToken,
|
||||
};
|
||||
const { data, headers } = await client.rawRequest(
|
||||
const { data, headers, status } = await client.rawRequest(
|
||||
document,
|
||||
variables,
|
||||
requestHeaders,
|
||||
);
|
||||
|
||||
if (status !== 200) {
|
||||
const res = await fetch("/zammad/auth/sso", {
|
||||
method: "GET",
|
||||
redirect: "manual",
|
||||
});
|
||||
|
||||
console.log({ checkAuth: res });
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const token = headers.get("CSRF-Token");
|
||||
setCsrfToken(token);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const graphQLFetcher = async ({ document, variables }: any) => {
|
||||
let checks = 0;
|
||||
let data = null;
|
||||
|
||||
while (!data && checks < 2) {
|
||||
data = await fetchAndCheckAuth({ document, variables });
|
||||
checks++;
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CssBaseline />
|
||||
|
|
|
|||
13
apps/link/app/_graphql/createTicketMutation.ts
Normal file
13
apps/link/app/_graphql/createTicketMutation.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { gql } from "graphql-request";
|
||||
|
||||
export const createTicketMutation = gql`
|
||||
mutation CreateTicket($ticketId: ID!, $input: TicketCreateInput!) {
|
||||
ticketCreate(input: $input) {
|
||||
ticket {
|
||||
id
|
||||
priority {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
20
apps/link/app/_graphql/searchQuery.ts
Normal file
20
apps/link/app/_graphql/searchQuery.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { gql } from 'graphql-request';
|
||||
|
||||
export const searchQuery = gql`
|
||||
query search($search: String!, $limit: Int = 10, $onlyIn: EnumSearchableModels = Ticket) {
|
||||
search(search: $search, limit: $limit, onlyIn: $onlyIn) {
|
||||
... on Ticket {
|
||||
id
|
||||
number
|
||||
internalId
|
||||
title
|
||||
state {
|
||||
id
|
||||
name
|
||||
}
|
||||
stateColorCode
|
||||
note
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
@ -18,7 +18,6 @@ const fetchRoles = async () => {
|
|||
|
||||
const fetchUser = async (email: string) => {
|
||||
const url = `${process.env.ZAMMAD_URL}/api/v1/users/search?query=email:${email}&limit=1`;
|
||||
console.log({ url });
|
||||
const res = await fetch(url, { headers });
|
||||
const users = await res.json();
|
||||
const user = users?.[0];
|
||||
|
|
|
|||
|
|
@ -15,29 +15,29 @@
|
|||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fontsource/playfair-display": "^5.0.13",
|
||||
"@fontsource/playfair-display": "^5.0.15",
|
||||
"@fontsource/poppins": "^5.0.8",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"@mui/icons-material": "^5",
|
||||
"@mui/lab": "^5.0.0-alpha.146",
|
||||
"@mui/lab": "^5.0.0-alpha.148",
|
||||
"@mui/material": "^5",
|
||||
"@mui/x-data-grid-pro": "^6.16.0",
|
||||
"@mui/x-date-pickers-pro": "^6.16.0",
|
||||
"@mui/x-data-grid-pro": "^6.16.2",
|
||||
"@mui/x-date-pickers-pro": "^6.16.2",
|
||||
"cryptr": "^6.3.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"graphql-request": "^6.1.0",
|
||||
"leafcutter-common": "*",
|
||||
"material-ui-popup-state": "^5.0.9",
|
||||
"mui-chips-input": "^2.1.3",
|
||||
"next": "13.5.3",
|
||||
"next-auth": "^4.23.1",
|
||||
"next": "13.5.4",
|
||||
"next-auth": "^4.23.2",
|
||||
"ra-data-graphql": "^4.14.3",
|
||||
"ra-i18n-polyglot": "^4.14.4",
|
||||
"ra-input-rich-text": "^4.14.4",
|
||||
"ra-language-english": "^4.14.4",
|
||||
"ra-postgraphile": "^6.1.1",
|
||||
"ra-i18n-polyglot": "^4.15.0",
|
||||
"ra-input-rich-text": "^4.15.0",
|
||||
"ra-language-english": "^4.15.0",
|
||||
"ra-postgraphile": "^6.1.2",
|
||||
"react": "18.2.0",
|
||||
"react-admin": "^4.14.4",
|
||||
"react-admin": "^4.15.0",
|
||||
"react-cookie": "^6.1.1",
|
||||
"react-digit-input": "^2.1.0",
|
||||
"react-dom": "18.2.0",
|
||||
|
|
@ -51,18 +51,18 @@
|
|||
"twilio-client": "^1.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.0",
|
||||
"@types/node": "^20.8.0",
|
||||
"@types/react": "18.2.24",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@types/node": "^20.8.6",
|
||||
"@types/react": "18.2.28",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"babel-loader": "^9.1.3",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-next": "^13.5.3",
|
||||
"eslint-config-next": "^13.5.4",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
"hapi-auth-bearer-token": "^8.0.0",
|
||||
"hapi-auth-jwt2": "^10.4.0",
|
||||
"hapi-swagger": "^17.1.0",
|
||||
"joi": "^17.10.2",
|
||||
"joi": "^17.11.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwks-rsa": "^3.0.1",
|
||||
"jwks-rsa": "^3.1.0",
|
||||
"long": "^5.2.3",
|
||||
"p-memoize": "^7.1.1",
|
||||
"pg": "^8.11.3",
|
||||
|
|
@ -40,8 +40,8 @@
|
|||
"pg-promise": "^11.5.4",
|
||||
"postgraphile": "4.12.3",
|
||||
"postgraphile-plugin-connection-filter": "^2.3.0",
|
||||
"remeda": "^1.27.0",
|
||||
"twilio": "^4.18.0",
|
||||
"remeda": "^1.27.1",
|
||||
"twilio": "^4.18.1",
|
||||
"typeorm": "^0.3.17",
|
||||
"@whiskeysockets/baileys": "^6.5.0"
|
||||
},
|
||||
|
|
@ -54,11 +54,11 @@
|
|||
"jest-config-link": "*",
|
||||
"nodemon": "^3.0.1",
|
||||
"pg-monitor": "^2.0.0",
|
||||
"pino-pretty": "^10.2.0",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsc-watch": "^6.0.4",
|
||||
"tsconfig-link": "*",
|
||||
"typedoc": "^0.25.1",
|
||||
"typedoc": "^0.25.2",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"@digiresilience/metamigo-db": "*",
|
||||
"@digiresilience/metamigo-api": "*",
|
||||
"@digiresilience/metamigo-worker": "*",
|
||||
"commander": "^11.0.0",
|
||||
"commander": "^11.1.0",
|
||||
"graphile-migrate": "^1.4.1",
|
||||
"graphile-worker": "^0.13.0",
|
||||
"node-jose": "^2.2.0",
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.5",
|
||||
"pino-pretty": "^10.2.0",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"nodemon": "^3.0.1",
|
||||
"tsconfig-link": "*",
|
||||
"eslint-config-link": "*",
|
||||
|
|
@ -40,4 +40,4 @@
|
|||
"lint": "eslint src --ext .ts && prettier \"src/**/*.ts\" --list-different",
|
||||
"test": "echo no tests"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,24 +14,24 @@
|
|||
"html-to-text": "^9.0.5",
|
||||
"node-fetch": "^3",
|
||||
"pg-promise": "^11.5.4",
|
||||
"remeda": "^1.27.0",
|
||||
"twilio": "^4.18.0"
|
||||
"remeda": "^1.27.1",
|
||||
"twilio": "^4.18.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.23.0",
|
||||
"@babel/preset-env": "7.22.20",
|
||||
"@babel/preset-typescript": "7.23.0",
|
||||
"@babel/core": "7.23.2",
|
||||
"@babel/preset-env": "7.23.2",
|
||||
"@babel/preset-typescript": "7.23.2",
|
||||
"@types/fluent-ffmpeg": "^2.1.22",
|
||||
"@types/jest": "^29.5.5",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-circus": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"pino-pretty": "^10.2.0",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"prettier": "^3.0.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"typedoc": "^0.25.1",
|
||||
"typedoc": "^0.25.2",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ RUN if [ "$EMBEDDED" = "true" ] ; then sed -i '/proxy_set_header X-Forwarded-Use
|
|||
USER root
|
||||
RUN set -ex; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends;
|
||||
apt-get install -y --no-install-recommends gpg;
|
||||
WORKDIR ${ZAMMAD_DIR}
|
||||
RUN bundle install --without test development mysql
|
||||
RUN sed -i '/^[[:space:]]*# create install ready file/ i\
|
||||
|
|
|
|||
3274
package-lock.json
generated
3274
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "link-stack",
|
||||
"version": "2.0.0b4",
|
||||
"version": "2.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
"docker:label-studio:down": "docker compose -f docker-compose.label-studio.yml -f docker-compose.metamigo-postgresql.yml down",
|
||||
"upgrade:setup": "npm i -g npm-check-updates",
|
||||
"upgrade:check": "ncu && ncu -ws -x graphql -x postgraphile",
|
||||
"upgrade:all": "ncu -u && ncu -ws -u -x graphql -x postgraphile && npm i",
|
||||
"upgrade:all": "ncu -u && ncu -ws -u -x graphql -x postgraphile -x graphile-worker && npm i",
|
||||
"clean": "rm -f package-lock.json && rm -rf node_modules && rm -rf apps/*/node_modules && rm -rf packages/*/node_modules && rm -rf apps/*/.next && rm -rf packages/*/.turbo && rm -rf apps/*/.turbo && rm -rf docker/zammad/addons/*"
|
||||
},
|
||||
"packageManager": "npm@9.6.7",
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@
|
|||
"lint": "eslint index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "7.23.0",
|
||||
"@babel/preset-env": "7.22.20",
|
||||
"@babel/preset-typescript": "7.23.0"
|
||||
"@babel/core": "7.23.2",
|
||||
"@babel/preset-env": "7.23.2",
|
||||
"@babel/preset-typescript": "7.23.2"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.50.0"
|
||||
"eslint": "^8.51.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@rushstack/eslint-patch": "^1.5.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||
"@typescript-eslint/parser": "^6.7.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.5",
|
||||
"@typescript-eslint/parser": "^6.7.5",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-xo-space": "^0.34.0",
|
||||
"eslint-plugin-cypress": "^2.15.1",
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
"typescript": "^4.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"jest": "^29.7.0",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
"dependencies": {
|
||||
"@hapi/hapi": "^21.3.2",
|
||||
"@hapi/hoek": "^11.0.2",
|
||||
"joi": "^17.10.2",
|
||||
"next-auth": "4.23.1"
|
||||
"joi": "^17.11.0",
|
||||
"next-auth": "4.23.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
|
|
|
|||
|
|
@ -9,22 +9,22 @@
|
|||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fontsource/playfair-display": "^5.0.13",
|
||||
"@fontsource/playfair-display": "^5.0.15",
|
||||
"@fontsource/poppins": "^5.0.8",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"@mui/icons-material": "^5",
|
||||
"@mui/lab": "^5.0.0-alpha.146",
|
||||
"@mui/lab": "^5.0.0-alpha.148",
|
||||
"@mui/material": "^5",
|
||||
"@mui/x-data-grid-pro": "^6.16.0",
|
||||
"@mui/x-date-pickers-pro": "^6.16.0",
|
||||
"@opensearch-project/opensearch": "^2.3.1",
|
||||
"@mui/x-data-grid-pro": "^6.16.2",
|
||||
"@mui/x-date-pickers-pro": "^6.16.2",
|
||||
"@opensearch-project/opensearch": "^2.4.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"material-ui-popup-state": "^5.0.9",
|
||||
"next": "13.5.3",
|
||||
"next-auth": "^4.23.1",
|
||||
"next-http-proxy-middleware": "^1.2.5",
|
||||
"nodemailer": "^6.9.5",
|
||||
"next": "13.5.4",
|
||||
"next-auth": "^4.23.2",
|
||||
"next-http-proxy-middleware": "^1.2.6",
|
||||
"nodemailer": "^6.9.6",
|
||||
"react": "18.2.0",
|
||||
"react-cookie": "^6.1.1",
|
||||
"react-cookie-consent": "^8.0.1",
|
||||
|
|
@ -38,18 +38,18 @@
|
|||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.0",
|
||||
"@types/node": "^20.8.0",
|
||||
"@types/react": "18.2.24",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@types/node": "^20.8.6",
|
||||
"@types/react": "18.2.28",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"babel-loader": "^9.1.3",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-next": "^13.5.3",
|
||||
"eslint-config-next": "^13.5.4",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"typescript": "5.2.2"
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -20,11 +20,11 @@
|
|||
"@types/figlet": "^1.5.6",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"@types/node": "*",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"@types/uuid": "^9.0.5",
|
||||
"camelcase-keys": "^9.1.0",
|
||||
"pg-monitor": "^2.0.0",
|
||||
"tsc-watch": "^6.0.4",
|
||||
"typedoc": "^0.25.1",
|
||||
"typedoc": "^0.25.2",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -36,29 +36,29 @@
|
|||
"@hapi/inert": "^7.1.0",
|
||||
"@hapi/vision": "^7.0.3",
|
||||
"@hapipal/schmervice": "^3.0.0",
|
||||
"@promster/hapi": "^10.0.1",
|
||||
"@promster/server": "^9.0.0",
|
||||
"@promster/types": "^5.0.0",
|
||||
"@promster/hapi": "^12.0.0",
|
||||
"@promster/server": "^12.0.0",
|
||||
"@promster/types": "^12.0.0",
|
||||
"@types/convict": "^6.1.4",
|
||||
"@types/hapi__glue": "^6.1.7",
|
||||
"@types/hapi__hapi": "^20.0.13",
|
||||
"@types/hapi__inert": "^5.2.7",
|
||||
"@types/hapi__inert": "^5.2.8",
|
||||
"@types/hapi__vision": "^5.5.5",
|
||||
"@types/hapipal__schmervice": "^2.0.4",
|
||||
"@types/hapipal__schmervice": "^2.0.5",
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^11.0.0",
|
||||
"commander": "^11.1.0",
|
||||
"convict": "^6.2.4",
|
||||
"decamelcase-keys": "^1.1.1",
|
||||
"figlet": "^1.6.0",
|
||||
"hapi-pino": "^12.1.0",
|
||||
"http-terminator": "^3.2.0",
|
||||
"joi": "^17.10.2",
|
||||
"joi": "^17.11.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next-auth": "^4.23.1",
|
||||
"next-auth": "^4.23.2",
|
||||
"pg-promise": "^11.5.4",
|
||||
"pino": "^8.15.3",
|
||||
"pino-pretty": "^10.2.0",
|
||||
"prom-client": "^14.x.x",
|
||||
"pino": "^8.16.0",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"prom-client": "^15.x.x",
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@
|
|||
"@digiresilience/montar": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.23.0",
|
||||
"@babel/preset-env": "7.22.20",
|
||||
"@babel/preset-typescript": "7.23.0",
|
||||
"eslint": "^8.50.0",
|
||||
"pino-pretty": "^10.2.0",
|
||||
"@babel/core": "7.23.2",
|
||||
"@babel/preset-env": "7.23.2",
|
||||
"@babel/preset-typescript": "7.23.2",
|
||||
"eslint": "^8.51.0",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"prettier": "^3.0.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsc-watch": "^6.0.4",
|
||||
"typedoc": "^0.25.1",
|
||||
"typedoc": "^0.25.2",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -16,18 +16,18 @@
|
|||
"pg-promise": "^11.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.23.0",
|
||||
"@babel/preset-env": "7.22.20",
|
||||
"@babel/preset-typescript": "7.23.0",
|
||||
"@babel/core": "7.23.2",
|
||||
"@babel/preset-env": "7.23.2",
|
||||
"@babel/preset-typescript": "7.23.2",
|
||||
"@types/jest": "^29.5.5",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint": "^8.51.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"pino-pretty": "^10.2.0",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"prettier": "^3.0.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsc-watch": "^6.0.4",
|
||||
"typedoc": "^0.25.1",
|
||||
"typedoc": "^0.25.2",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
"jest-config-link": "*",
|
||||
"tsc-watch": "^6.0.4",
|
||||
"tsconfig-link": "*",
|
||||
"typedoc": "^0.25.1"
|
||||
"typedoc": "^0.25.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"backoff": "^2.5.0",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue