Bridge integration
This commit is contained in:
parent
42a5e09c94
commit
162390008b
56 changed files with 776 additions and 591 deletions
|
|
@ -1,4 +1,5 @@
|
|||
export default function Page() {
|
||||
return <h1>Home</h1>;
|
||||
}
|
||||
import { Home } from "bridge-ui";
|
||||
|
||||
export default function Page() {
|
||||
return <Home />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { signIn } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import LinkLogo from "@/app/_images/link-logo-small.png";
|
||||
import { colors } from "ui";
|
||||
import { colors, fonts } from "ui";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
type LoginProps = {
|
||||
|
|
@ -34,6 +34,7 @@ export const Login: FC<LoginProps> = ({ session }) => {
|
|||
const params = useSearchParams();
|
||||
const error = params.get("error");
|
||||
const { darkGray, cdrLinkOrange, white } = colors;
|
||||
const { poppins } = fonts;
|
||||
const buttonStyles = {
|
||||
borderRadius: 500,
|
||||
width: "100%",
|
||||
|
|
@ -102,7 +103,7 @@ export const Login: FC<LoginProps> = ({ session }) => {
|
|||
fontWeight: 700,
|
||||
mt: 1,
|
||||
ml: 0.5,
|
||||
fontFamily: "Poppins",
|
||||
fontFamily: poppins.style.fontFamily,
|
||||
}}
|
||||
>
|
||||
CDR Bridge
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
FROM node:20 AS base
|
||||
|
||||
FROM base AS builder
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { signIn } from "next-auth/react";
|
||||
import Image from "next/image";
|
||||
import LinkLogo from "public/link-logo-small.png";
|
||||
import { colors } from "app/_styles/theme";
|
||||
import { colors, fonts } from "ui";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
type LoginProps = {
|
||||
|
|
@ -34,6 +34,7 @@ export const Login: FC<LoginProps> = ({ session }) => {
|
|||
const params = useSearchParams();
|
||||
const error = params.get("error");
|
||||
const { darkGray, cdrLinkOrange, white } = colors;
|
||||
const { poppins } = fonts;
|
||||
const buttonStyles = {
|
||||
borderRadius: 500,
|
||||
width: "100%",
|
||||
|
|
@ -102,7 +103,7 @@ export const Login: FC<LoginProps> = ({ session }) => {
|
|||
fontWeight: 700,
|
||||
mt: 1,
|
||||
ml: 0.5,
|
||||
fontFamily: "Poppins",
|
||||
fontFamily: poppins.style.fontFamily,
|
||||
}}
|
||||
>
|
||||
CDR Link
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const InternalLayout: FC<PropsWithChildren> = ({ children }) => {
|
|||
<Sidebar open={open} setOpen={setOpen} />
|
||||
<Grid
|
||||
item
|
||||
sx={{ ml: open ? "270px" : "100px", width: "100%", height: "100vh" }}
|
||||
sx={{ ml: open ? "270px" : "70px", width: "100%", height: "100vh" }}
|
||||
>
|
||||
{children as any}
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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";
|
||||
import { colors } from "ui";
|
||||
|
||||
type SearchResultProps = {
|
||||
props: any;
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ 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";
|
||||
import { fonts } from "app/_styles/theme";
|
||||
import { fonts } from "ui";
|
||||
|
||||
const openWidth = 270;
|
||||
const closedWidth = 100;
|
||||
const closedWidth = 70;
|
||||
|
||||
const MenuItem = ({
|
||||
name,
|
||||
|
|
@ -49,6 +49,7 @@ const MenuItem = ({
|
|||
selected = false,
|
||||
open = true,
|
||||
badge,
|
||||
depth = 0,
|
||||
target = "_self",
|
||||
}: any) => {
|
||||
const { roboto } = fonts;
|
||||
|
|
@ -90,8 +91,8 @@ const MenuItem = ({
|
|||
width: 30,
|
||||
height: "28px",
|
||||
position: "relative",
|
||||
ml: "9px",
|
||||
mr: "1px",
|
||||
ml: "8px",
|
||||
mr: "2px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
|
|
@ -114,9 +115,21 @@ const MenuItem = ({
|
|||
border: "solid 1px #fff",
|
||||
borderColor: "transparent transparent transparent #fff",
|
||||
borderRadius: "60px",
|
||||
rotate: "-35deg",
|
||||
rotate: "-50deg",
|
||||
}}
|
||||
/>
|
||||
{depth > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: depth * 22,
|
||||
height: "1px",
|
||||
backgroundColor: "white",
|
||||
position: "absolute",
|
||||
left: "26px",
|
||||
top: "14px",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{open && (
|
||||
|
|
@ -132,6 +145,7 @@ const MenuItem = ({
|
|||
border: 0,
|
||||
textAlign: "left",
|
||||
color: "white",
|
||||
ml: depth * 3,
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
|
|
@ -220,7 +234,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 20,
|
||||
top: 24,
|
||||
right: open ? -8 : -16,
|
||||
color: "#1C75FD",
|
||||
rotate: open ? "90deg" : "-90deg",
|
||||
|
|
@ -231,8 +245,8 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
>
|
||||
<ExpandCircleDownIcon
|
||||
sx={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
width: 24,
|
||||
height: 24,
|
||||
background: "white",
|
||||
borderRadius: 500,
|
||||
}}
|
||||
|
|
@ -338,9 +352,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<SearchBox />
|
||||
</Grid>
|
||||
<Grid item>{open && <SearchBox />}</Grid>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
|
|
@ -558,7 +570,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
<>
|
||||
<MenuItem
|
||||
name="Admin"
|
||||
href="/admin/zammad"
|
||||
href="/admin/bridge"
|
||||
Icon={SettingsIcon}
|
||||
iconSize={20}
|
||||
open={open}
|
||||
|
|
@ -570,6 +582,56 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
onClick={undefined}
|
||||
>
|
||||
<List component="div" disablePadding>
|
||||
<MenuItem
|
||||
name="CDR Bridge"
|
||||
href="/admin/bridge"
|
||||
selected={pathname.endsWith("/admin/bridge")}
|
||||
open={open}
|
||||
/>
|
||||
<Collapse
|
||||
in={pathname.startsWith("/admin/bridge")}
|
||||
timeout="auto"
|
||||
unmountOnExit
|
||||
onClick={undefined}
|
||||
>
|
||||
<List component="div" disablePadding>
|
||||
<MenuItem
|
||||
name="WhatsApp"
|
||||
href="/admin/bridge/whatsapp"
|
||||
depth={1}
|
||||
selected={pathname.endsWith("/admin/bridge/whatsapp")}
|
||||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Signal"
|
||||
href="/admin/bridge/signal"
|
||||
depth={1}
|
||||
selected={pathname.endsWith("/admin/bridge/signal")}
|
||||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Facebook"
|
||||
href="/admin/bridge/facebook"
|
||||
depth={1}
|
||||
selected={pathname.endsWith("/admin/bridge/facebook")}
|
||||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Voice"
|
||||
href="/admin/bridge/voice"
|
||||
depth={1}
|
||||
selected={pathname.endsWith("/admin/bridge/voice")}
|
||||
open={open}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Webhooks"
|
||||
href="/admin/bridge/webhooks"
|
||||
depth={1}
|
||||
selected={pathname.endsWith("/admin/bridge/webhooks")}
|
||||
open={open}
|
||||
/>
|
||||
</List>
|
||||
</Collapse>
|
||||
<MenuItem
|
||||
name="Zammad Settings"
|
||||
href="/admin/zammad"
|
||||
|
|
@ -578,16 +640,6 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
|||
selected={pathname.endsWith("/admin/zammad")}
|
||||
open={open}
|
||||
/>
|
||||
{false && roles.includes("bridge") && (
|
||||
<MenuItem
|
||||
name="Bridge"
|
||||
href="/admin/bridge"
|
||||
Icon={FeaturedPlayListIcon}
|
||||
iconSize={0}
|
||||
selected={pathname.endsWith("/admin/bridge")}
|
||||
open={open}
|
||||
/>
|
||||
)}
|
||||
{roles.includes("label_studio") && (
|
||||
<MenuItem
|
||||
name="Label Studio"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import { Create } from "bridge-ui";
|
||||
|
||||
type PageProps = {
|
||||
params: { segment: string[] };
|
||||
};
|
||||
|
||||
export default function Page({ params: { segment } }: PageProps) {
|
||||
const service = segment[0];
|
||||
|
||||
return <Create service={service} />;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { db } from "bridge-common";
|
||||
import { serviceConfig, Detail } from "bridge-ui";
|
||||
|
||||
type Props = {
|
||||
params: { segment: string[] };
|
||||
};
|
||||
|
||||
export default async function Page({ params: { segment } }: Props) {
|
||||
const service = segment[0];
|
||||
const id = segment?.[1];
|
||||
|
||||
if (!id) return null;
|
||||
|
||||
const {
|
||||
[service]: { table },
|
||||
} = serviceConfig;
|
||||
|
||||
const row = await db
|
||||
.selectFrom(table)
|
||||
.selectAll()
|
||||
.where("id", "=", id)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!row) return null;
|
||||
|
||||
return <Detail service={service} row={row} />;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { db } from "bridge-common";
|
||||
import { serviceConfig, Edit } from "bridge-ui";
|
||||
|
||||
type PageProps = {
|
||||
params: { segment: string[] };
|
||||
};
|
||||
|
||||
export default async function Page({ params: { segment } }: PageProps) {
|
||||
const service = segment[0];
|
||||
const id = segment?.[1];
|
||||
|
||||
if (!id) return null;
|
||||
|
||||
const {
|
||||
[service]: { table },
|
||||
} = serviceConfig;
|
||||
|
||||
const row = await db
|
||||
.selectFrom(table)
|
||||
.selectAll()
|
||||
.where("id", "=", id)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!row) return null;
|
||||
|
||||
return <Edit service={service} row={row} />;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { ServiceLayout } from "bridge-ui";
|
||||
|
||||
export default ServiceLayout;
|
||||
22
apps/link/app/(main)/admin/bridge/[...segment]/page.tsx
Normal file
22
apps/link/app/(main)/admin/bridge/[...segment]/page.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { db } from "bridge-common";
|
||||
import { serviceConfig, List } from "bridge-ui";
|
||||
|
||||
type PageProps = {
|
||||
params: {
|
||||
segment: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export default async function Page({ params: { segment } }: PageProps) {
|
||||
const service = segment[0];
|
||||
|
||||
if (!service) return null;
|
||||
|
||||
const config = serviceConfig[service];
|
||||
|
||||
if (!config) return null;
|
||||
|
||||
const rows = await db.selectFrom(config.table).selectAll().execute();
|
||||
|
||||
return <List service={service} rows={rows} />;
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
// import { Admin } from "./_components/Admin";
|
||||
import { Box } from "@mui/material";
|
||||
import { Home } from "bridge-ui";
|
||||
|
||||
export default function Page() {
|
||||
return <Box />;
|
||||
return <Home />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ export const DocsWrapper: FC = () => (
|
|||
sx={{ height: "100%", width: "100%" }}
|
||||
direction="column"
|
||||
>
|
||||
<Grid item sx={{ height: "100vh", width: "100%" }}>
|
||||
<Grid
|
||||
item
|
||||
sx={{ height: "calc(100vh + 120px)", width: "100%", mt: "-121px" }}
|
||||
>
|
||||
<Iframe
|
||||
id="docs"
|
||||
url={"https://digiresilience.org/docs/link/about/"}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export const dynamic = "force-dynamic";
|
|||
export default async function Page() {
|
||||
return (
|
||||
<LeafcutterWrapper>
|
||||
<Home visualizations={{}} />
|
||||
<Home visualizations={[]} showWelcome={false} />
|
||||
</LeafcutterWrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
"use client";
|
||||
|
||||
import { FC, useState } from "react";
|
||||
import {
|
||||
Grid,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
TextField,
|
||||
Autocomplete,
|
||||
} from "@mui/material";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
import { createTicketMutation } from "app/_graphql/createTicketMutation";
|
||||
import { FC } from "react";
|
||||
import { useFormState } from "react-dom";
|
||||
import { Grid } from "@mui/material";
|
||||
import { Dialog, Button, TextField, Autocomplete } from "ui";
|
||||
import { createTicketAction } from "app/_actions/tickets";
|
||||
import useSWR from "swr";
|
||||
|
||||
interface TicketCreateDialogProps {
|
||||
open: boolean;
|
||||
|
|
@ -22,133 +16,83 @@ 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 = "#1982FC";
|
||||
const color = "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 initialState = {
|
||||
messages: [],
|
||||
errors: [],
|
||||
values: {
|
||||
customerId: "",
|
||||
groupId: "",
|
||||
ownerId: "",
|
||||
priorityId: "",
|
||||
stateId: "",
|
||||
tags: [],
|
||||
title: "",
|
||||
article: {
|
||||
body: "",
|
||||
type: "note",
|
||||
internal: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [formState, formAction] = useFormState(
|
||||
createTicketAction,
|
||||
initialState,
|
||||
);
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
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={formattedCustomers}
|
||||
value={customerID}
|
||||
sx={{ width: 300 }}
|
||||
onChange={(e: any) => setCustomerID(e.target.value.id)}
|
||||
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 }}>
|
||||
<Dialog
|
||||
title="Create Ticket"
|
||||
open={open}
|
||||
onClose={closeDialog}
|
||||
formAction={formAction}
|
||||
buttons={
|
||||
<Grid container justifyContent="space-between">
|
||||
<Grid item>
|
||||
<Button
|
||||
sx={{
|
||||
backgroundColor: "white",
|
||||
color: "#666",
|
||||
fontFamily: "Poppins, sans-serif",
|
||||
fontWeight: 700,
|
||||
borderRadius: 2,
|
||||
textTransform: "none",
|
||||
}}
|
||||
text="Cancel"
|
||||
kind="secondary"
|
||||
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}
|
||||
>
|
||||
Create Ticket
|
||||
</Button>
|
||||
<Button text="Save" type="submit" kind="primary" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogActions>
|
||||
}
|
||||
>
|
||||
<Grid container direction="column" spacing={3}>
|
||||
<Grid item>
|
||||
<Autocomplete
|
||||
name="customerId"
|
||||
label="Customer"
|
||||
options={formattedCustomers}
|
||||
formState={formState}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<TextField name="title" label="Title" formState={formState} />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<TextField
|
||||
name="details"
|
||||
label="Details"
|
||||
lines={10}
|
||||
formState={formState}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import { FC, useState } from "react";
|
|||
import { Grid, Box } from "@mui/material";
|
||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { StyledDataGrid } from "app/(main)/_components/StyledDataGrid";
|
||||
import { Button } from "app/_components/Button";
|
||||
import { typography } from "app/_styles/theme";
|
||||
import { Button, List, typography } from "ui";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { TicketCreateDialog } from "./TicketCreateDialog";
|
||||
|
||||
|
|
@ -26,83 +25,61 @@ export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
|
|||
{
|
||||
field: "title",
|
||||
headerName: "Title",
|
||||
flex: 5,
|
||||
flex: 3,
|
||||
},
|
||||
{
|
||||
field: "customer",
|
||||
headerName: "Sender",
|
||||
valueGetter: (params: any) => params.row?.customer?.fullname,
|
||||
flex: 2,
|
||||
valueGetter: (value: any) => value?.fullname,
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "Created At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.createdAt).toLocaleString(),
|
||||
valueGetter: (value: any) => new Date(value).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "updatedAt",
|
||||
headerName: "Updated At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.updatedAt).toLocaleString(),
|
||||
valueGetter: (value: any) => new Date(value).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "group",
|
||||
headerName: "Group",
|
||||
valueGetter: (params: any) => params.row?.group?.name,
|
||||
valueGetter: (value: any) => value?.name,
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const rowClick = ({ row }) => {
|
||||
router.push(`/tickets/${row.internalId}`);
|
||||
const onRowClick = (id: any) => {
|
||||
router.push(`/tickets/${id}`);
|
||||
};
|
||||
|
||||
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>
|
||||
</Grid>
|
||||
<List
|
||||
title={title}
|
||||
rows={tickets}
|
||||
columns={gridColumns}
|
||||
onRowClick={onRowClick}
|
||||
getRowID={(row: any) => {
|
||||
console.log({ row });
|
||||
return row.internalId;
|
||||
}}
|
||||
buttons={
|
||||
<Grid container direction="row-reverse" alignItems="center">
|
||||
<Grid item>
|
||||
<Button
|
||||
href={""}
|
||||
onClick={() => setDialogOpen(true)}
|
||||
text="Create"
|
||||
color="#1982FC"
|
||||
color="primary"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<StyledDataGrid
|
||||
name={title}
|
||||
columns={gridColumns}
|
||||
rows={tickets}
|
||||
onRowClick={rowClick}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
<TicketCreateDialog
|
||||
open={dialogOpen}
|
||||
closeDialog={() => setDialogOpen(false)}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,6 @@ export const ZammadOverview: FC<ZammadOverviewProps> = ({ name }) => {
|
|||
}, [name]);
|
||||
|
||||
const shouldRender = tickets && !error;
|
||||
console.log({ shouldRender, tickets, error });
|
||||
|
||||
return shouldRender && <TicketList title={name} tickets={tickets} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,18 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import useSWR from "swr";
|
||||
import { getTicketQuery } from "app/_graphql/getTicketQuery";
|
||||
import { getTicketArticlesQuery } from "app/_graphql/getTicketArticlesQuery";
|
||||
import {
|
||||
Grid,
|
||||
Box,
|
||||
Typography,
|
||||
Button,
|
||||
// Dialog,
|
||||
// DialogActions,
|
||||
// DialogContent,
|
||||
} from "@mui/material";
|
||||
import { Grid, Box, Typography } from "@mui/material";
|
||||
import { Button, fonts, colors } from "ui";
|
||||
|
||||
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
|
||||
import {
|
||||
MainContainer,
|
||||
|
|
@ -28,6 +22,8 @@ interface TicketDetailProps {
|
|||
}
|
||||
|
||||
export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
||||
const { poppins, roboto } = fonts;
|
||||
const { veryLightGray, lightGray } = colors;
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [articleKind, setArticleKind] = useState("note");
|
||||
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||
|
|
@ -52,7 +48,6 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
|||
});
|
||||
|
||||
const closeDialog = () => setDialogOpen(false);
|
||||
console.log({ recentViewData, recentViewError });
|
||||
|
||||
const ticket = ticketData?.ticket;
|
||||
const ticketArticles = ticketArticlesData?.ticketArticles;
|
||||
|
|
@ -79,13 +74,19 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
|||
>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{ fontFamily: "Poppins", fontWeight: 700 }}
|
||||
sx={{
|
||||
fontFamily: poppins.style.fontFamily,
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{ticket.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ fontFamily: "Roboto", fontWeight: 400 }}
|
||||
sx={{
|
||||
fontFamily: roboto.style.fontFamily,
|
||||
fontWeight: 400,
|
||||
}}
|
||||
>{`Ticket #${ticket.number} (created ${new Date(
|
||||
ticket.createdAt,
|
||||
).toLocaleDateString()})`}</Typography>
|
||||
|
|
@ -118,8 +119,8 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
|||
<Box
|
||||
sx={{
|
||||
height: 80,
|
||||
background: "#eeeeee",
|
||||
borderTop: "1px solid #ddd",
|
||||
background: veryLightGray,
|
||||
borderTop: `1px solid ${lightGray}`,
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
width: "100%",
|
||||
|
|
@ -128,59 +129,31 @@ export const TicketDetail: FC<TicketDetailProps> = ({ id }) => {
|
|||
>
|
||||
<Grid
|
||||
container
|
||||
spacing={4}
|
||||
spacing={6}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
alignContent="center"
|
||||
sx={{ height: "100%", pt: 6 }}
|
||||
>
|
||||
<Grid item>
|
||||
<Button
|
||||
variant="contained"
|
||||
disableElevation
|
||||
sx={{
|
||||
fontFamily: "Poppins, sans-serif",
|
||||
fontWeight: 700,
|
||||
borderRadius: 2,
|
||||
textTransform: "none",
|
||||
backgroundColor: "#1982FC",
|
||||
padding: "6px 30px",
|
||||
margin: "20px 0px",
|
||||
whiteSpace: "nowrap",
|
||||
py: "10px",
|
||||
mt: 2,
|
||||
}}
|
||||
onClick={() => {
|
||||
setArticleKind(firstArticleKind);
|
||||
setDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Reply to ticket
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
variant="contained"
|
||||
disableElevation
|
||||
sx={{
|
||||
fontFamily: "Poppins, sans-serif",
|
||||
fontWeight: 700,
|
||||
borderRadius: 2,
|
||||
textTransform: "none",
|
||||
color: "black",
|
||||
backgroundColor: "#FFB620",
|
||||
padding: "6px 30px",
|
||||
margin: "20px 0px",
|
||||
whiteSpace: "nowrap",
|
||||
py: "10px",
|
||||
mt: 2,
|
||||
}}
|
||||
text="Write note to agent"
|
||||
color="#FFB620"
|
||||
onClick={() => {
|
||||
setArticleKind("note");
|
||||
setDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Write note to agent
|
||||
</Button>
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button
|
||||
text="Reply to ticket"
|
||||
kind="primary"
|
||||
onClick={() => {
|
||||
setArticleKind(firstArticleKind);
|
||||
setDialogOpen(true);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,23 @@
|
|||
"use client";
|
||||
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import {
|
||||
Grid,
|
||||
Box,
|
||||
// Typography,
|
||||
// TextField,
|
||||
// Stack,
|
||||
Select,
|
||||
MenuItem,
|
||||
} from "@mui/material";
|
||||
import { Grid, Box, MenuItem } from "@mui/material";
|
||||
import { useFormState } from "react-dom";
|
||||
import { Select, Button } from "ui";
|
||||
import { MuiChipsInput } from "mui-chips-input";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
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";
|
||||
import { updateTicketAction } from "app/_actions/tickets";
|
||||
|
||||
interface TicketEditProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
||||
const [selectedGroup, setSelectedGroup] = useState("");
|
||||
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 selectedTags = [];
|
||||
const pendingVisible = false;
|
||||
const pendingDate = new Date();
|
||||
const handleDelete = () => {
|
||||
console.info("You clicked the delete icon.");
|
||||
};
|
||||
|
|
@ -58,24 +46,28 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
const ticket = ticketData?.ticket;
|
||||
if (ticket) {
|
||||
const groupID = ticket.group.id?.split("/").pop();
|
||||
setSelectedGroup(groupID);
|
||||
// setSelectedGroup(groupID);
|
||||
const ownerID = ticket.owner.id?.split("/").pop();
|
||||
setSelectedOwner(ownerID);
|
||||
// setSelectedOwner(ownerID);
|
||||
const priorityID = ticket.priority.id?.split("/").pop();
|
||||
setSelectedPriority(priorityID);
|
||||
// setSelectedPriority(priorityID);
|
||||
const stateID = ticket.state.id?.split("/").pop();
|
||||
setSelectedState(stateID);
|
||||
setSelectedTags(ticket.tags);
|
||||
// setSelectedState(stateID);
|
||||
// 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({
|
||||
document: updateTicketMutation,
|
||||
|
|
@ -85,8 +77,10 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
},
|
||||
});
|
||||
console.log({ res });
|
||||
*/
|
||||
};
|
||||
const updateTags = async (tags: string[]) => {
|
||||
/*
|
||||
console.log({ tags });
|
||||
const res = await fetcher({
|
||||
document: updateTagsMutation,
|
||||
|
|
@ -96,144 +90,175 @@ export const TicketEdit: FC<TicketEditProps> = ({ id }) => {
|
|||
},
|
||||
});
|
||||
console.log({ res });
|
||||
*/
|
||||
};
|
||||
const initialState = {
|
||||
messages: [],
|
||||
errors: [],
|
||||
values: {
|
||||
customer: "",
|
||||
group: "",
|
||||
owner: "",
|
||||
priority: "",
|
||||
state: "",
|
||||
tags: [],
|
||||
title: "",
|
||||
article: {
|
||||
body: "",
|
||||
type: "note",
|
||||
internal: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const [formState, formAction] = useFormState(
|
||||
updateTicketAction,
|
||||
initialState,
|
||||
);
|
||||
const shouldRender = ticketData && !ticketError;
|
||||
|
||||
return (
|
||||
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) => {
|
||||
const newGroup = e.target.value;
|
||||
setSelectedGroup(newGroup);
|
||||
updateTicket({
|
||||
groupId: `gid://zammad/Group/${newGroup}`,
|
||||
});
|
||||
}}
|
||||
size="small"
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
<form action={formAction}>
|
||||
<Grid container direction="column" spacing={3}>
|
||||
<Grid item>
|
||||
<Box sx={{ m: 1 }}>Group</Box>
|
||||
<Select
|
||||
name="group"
|
||||
label="Group"
|
||||
formState={formState}
|
||||
getOptions={() =>
|
||||
groups?.map((group: any) => ({
|
||||
value: group.id,
|
||||
label: group.name,
|
||||
})) ?? []
|
||||
}
|
||||
/*
|
||||
onChange={(e: any) => {
|
||||
const newGroup = e.target.value;
|
||||
setSelectedGroup(newGroup);
|
||||
updateTicket({
|
||||
groupId: `gid://zammad/Group/${newGroup}`,
|
||||
});
|
||||
}}
|
||||
*/
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Box sx={{ m: 1, mt: 0 }}>Owner</Box>
|
||||
<Select
|
||||
name="owner"
|
||||
label="Owner"
|
||||
formState={formState}
|
||||
getOptions={() =>
|
||||
agents?.map((user: any) => ({
|
||||
value: user.id,
|
||||
label: `${user.firstname} ${user.lastname}`,
|
||||
})) ?? []
|
||||
}
|
||||
/*
|
||||
onChange={(e: any) => {
|
||||
const newOwner = e.target.value;
|
||||
setSelectedOwner(newOwner);
|
||||
updateTicket({ ownerId: `gid://zammad/User/${newOwner}` });
|
||||
}}
|
||||
*/
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ m: 1, mt: 0 }}>State</Box>
|
||||
<Select
|
||||
name="state"
|
||||
label="State"
|
||||
formState={formState}
|
||||
getOptions={() =>
|
||||
filteredStates?.map((state: any) => ({
|
||||
value: state.id,
|
||||
label: state.name,
|
||||
})) ?? []
|
||||
}
|
||||
/*
|
||||
onChange={(e: any) => {
|
||||
const newState = e.target.value;
|
||||
setSelectedState(newState);
|
||||
updateTicket({
|
||||
stateId: `gid://zammad/Ticket::State/${newState}`,
|
||||
pendingTime: pendingDate.toISOString(),
|
||||
});
|
||||
}}
|
||||
*/
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sx={{ display: pendingVisible ? "inherit" : "none" }}
|
||||
>
|
||||
{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) => {
|
||||
const newOwner = e.target.value;
|
||||
setSelectedOwner(newOwner);
|
||||
updateTicket({ ownerId: `gid://zammad/User/${newOwner}` });
|
||||
}}
|
||||
size="small"
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
{agents?.map((user: any) => (
|
||||
<MenuItem key={user.id} value={`${user.id}`}>
|
||||
{user.firstname} {user.lastname}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ m: 1, mt: 0 }}>State</Box>
|
||||
<Select
|
||||
value={selectedState}
|
||||
onChange={(e: any) => {
|
||||
const newState = e.target.value;
|
||||
setSelectedState(newState);
|
||||
updateTicket({
|
||||
stateId: `gid://zammad/Ticket::State/${newState}`,
|
||||
pendingTime: pendingDate.toISOString(),
|
||||
});
|
||||
}}
|
||||
size="small"
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
{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
|
||||
value={selectedPriority}
|
||||
onChange={(e: any) => {
|
||||
const newPriority = e.target.value;
|
||||
setSelectedPriority(newPriority);
|
||||
updateTicket({
|
||||
priorityId: `gid://zammad/Ticket::Priority/${newPriority}`,
|
||||
});
|
||||
}}
|
||||
size="small"
|
||||
sx={{
|
||||
width: "100%",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
{priorities?.map((priority: any) => (
|
||||
<MenuItem key={priority.id} value={priority.id}>
|
||||
{priority.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Grid>
|
||||
{/* <DatePicker
|
||||
label="Pending Date"
|
||||
value={pendingDate}
|
||||
onChange={(newValue: any) => {
|
||||
|
||||
<Grid item>
|
||||
<Box sx={{ mb: 1 }}>Tags</Box>
|
||||
<MuiChipsInput
|
||||
sx={{ backgroundColor: "white", width: "100%" }}
|
||||
value={selectedTags}
|
||||
onChange={(tags: any) => {
|
||||
setSelectedTags(tags);
|
||||
updateTags(tags);
|
||||
}}
|
||||
/>
|
||||
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
|
||||
name="priority"
|
||||
label="Priority"
|
||||
formState={formState}
|
||||
getOptions={() =>
|
||||
priorities?.map((priority: any) => ({
|
||||
value: priority.id,
|
||||
label: priority.name,
|
||||
})) ?? []
|
||||
}
|
||||
/*
|
||||
onChange={(e: any) => {
|
||||
|
||||
const newPriority = e.target.value;
|
||||
setSelectedPriority(newPriority);
|
||||
updateTicket({
|
||||
priorityId: `gid://zammad/Ticket::Priority/${newPriority}`,
|
||||
});
|
||||
|
||||
}}
|
||||
*/
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item>
|
||||
<Box sx={{ mb: 1 }}>Tags</Box>
|
||||
<MuiChipsInput
|
||||
sx={{ backgroundColor: "white", width: "100%" }}
|
||||
value={selectedTags}
|
||||
onChange={(tags: any) => {
|
||||
/*
|
||||
setSelectedTags(tags);
|
||||
updateTags(tags);
|
||||
*/
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item container direction="row-reverse">
|
||||
<Grid item>
|
||||
<Button text="Save" kind="primary" type="submit" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</Box>
|
||||
)
|
||||
);
|
||||
|
|
|
|||
113
apps/link/app/_actions/tickets.ts
Normal file
113
apps/link/app/_actions/tickets.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { createTicketMutation } from "app/_graphql/createTicketMutation";
|
||||
import { updateTicketMutation } from "app/_graphql/updateTicketMutation";
|
||||
import { updateTagsMutation } from "app/_graphql/updateTagsMutation";
|
||||
// import { executeMutation } from "app/_lib/graphql";
|
||||
// import { AddAssetMutation } from "../_graphql/AddAssetMutation";
|
||||
|
||||
export const createTicketAction = async (
|
||||
currentState: any,
|
||||
formData: FormData,
|
||||
) => {
|
||||
/*
|
||||
|
||||
const createTicket = async () => {
|
||||
await fetcher({
|
||||
document: createTicketMutation,
|
||||
variables: {
|
||||
input: {
|
||||
ticket,
|
||||
},
|
||||
},
|
||||
});
|
||||
closeDialog();
|
||||
setBody("");
|
||||
};
|
||||
|
||||
*/
|
||||
try {
|
||||
const ticket = {
|
||||
title: formData.get("title"),
|
||||
};
|
||||
|
||||
const result = await executeMutation({
|
||||
project,
|
||||
mutation: AddAssetMutation,
|
||||
variables: {
|
||||
input: asset,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(`/${project}/assets`);
|
||||
|
||||
return {
|
||||
...currentState,
|
||||
values: { ...asset, ...result.addAsset, project },
|
||||
success: true,
|
||||
};
|
||||
} catch (e: any) {
|
||||
return { success: false, message: e?.message ?? "Unknown error" };
|
||||
}
|
||||
};
|
||||
|
||||
export const updateTicketAction = async (
|
||||
currentState: any,
|
||||
formData: FormData,
|
||||
) => {
|
||||
try {
|
||||
const { id, project } = currentState.values;
|
||||
const updatedTicket = {
|
||||
title: formData.get("title"),
|
||||
};
|
||||
await executeMutation({
|
||||
project,
|
||||
mutation: UpdateAssetMutation,
|
||||
variables: {
|
||||
id,
|
||||
input: updatedAsset,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(`/${project}/assets/${id}`);
|
||||
|
||||
return {
|
||||
...currentState,
|
||||
values: { ...currentState.values, ...updatedAsset, id, project },
|
||||
success: true,
|
||||
};
|
||||
} catch (e: any) {
|
||||
return { success: false, message: e?.message ?? "Unknown error" };
|
||||
}
|
||||
};
|
||||
|
||||
export const updateTicketTagsAction = async (
|
||||
currentState: any,
|
||||
formData: FormData,
|
||||
) => {
|
||||
try {
|
||||
const { id, project } = currentState.values;
|
||||
const updatedTicket = {
|
||||
title: formData.get("title"),
|
||||
};
|
||||
await executeMutation({
|
||||
project,
|
||||
mutation: UpdateAssetMutation,
|
||||
variables: {
|
||||
id,
|
||||
input: updatedAsset,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(`/${project}/assets/${id}`);
|
||||
|
||||
return {
|
||||
...currentState,
|
||||
values: { ...currentState.values, ...updatedAsset, id, project },
|
||||
success: true,
|
||||
};
|
||||
} catch (e: any) {
|
||||
return { success: false, message: e?.message ?? "Unknown error" };
|
||||
}
|
||||
};
|
||||
3
apps/link/app/_actions/users.ts
Normal file
3
apps/link/app/_actions/users.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"use server";
|
||||
|
||||
const fetchUsersAction = async () => {};
|
||||
|
|
@ -104,23 +104,19 @@ export const MultiProvider: FC<PropsWithChildren> = ({ children }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
|
||||
<CssBaseline />
|
||||
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
|
||||
<SWRConfig value={{ fetcher: multiFetcher }}>
|
||||
<SessionProvider>
|
||||
<CookiesProvider>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<I18n locale={locale} messages={messages[locale]}>
|
||||
<LeafcutterProvider>
|
||||
{children}
|
||||
</LeafcutterProvider>
|
||||
</I18n>
|
||||
</LocalizationProvider>
|
||||
</CookiesProvider>
|
||||
</SessionProvider>
|
||||
</SWRConfig>
|
||||
</NextAppDirEmotionCacheProvider>
|
||||
</>
|
||||
<SWRConfig value={{ fetcher: multiFetcher }}>
|
||||
<SessionProvider>
|
||||
<CookiesProvider>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<I18n locale={locale} messages={messages[locale]}>
|
||||
<LeafcutterProvider>{children}</LeafcutterProvider>
|
||||
</I18n>
|
||||
</LocalizationProvider>
|
||||
</CookiesProvider>
|
||||
</SessionProvider>
|
||||
</SWRConfig>
|
||||
</NextAppDirEmotionCacheProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
import { Roboto, Playfair_Display, Poppins } from "next/font/google";
|
||||
|
||||
const roboto = Roboto({
|
||||
weight: ["400"],
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const playfair = Playfair_Display({
|
||||
weight: ["900"],
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const poppins = Poppins({
|
||||
weight: ["400", "700"],
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const fonts = {
|
||||
roboto,
|
||||
playfair,
|
||||
poppins,
|
||||
};
|
||||
|
||||
|
||||
export const colors: any = {
|
||||
lightGray: "#ededf0",
|
||||
mediumGray: "#e3e5e5",
|
||||
darkGray: "#33302f",
|
||||
mediumBlue: "#4285f4",
|
||||
green: "#349d7b",
|
||||
lavender: "#a5a6f6",
|
||||
darkLavender: "#5d5fef",
|
||||
pink: "#fcddec",
|
||||
cdrLinkOrange: "#ff7115",
|
||||
coreYellow: "#fac942",
|
||||
helpYellow: "#fff4d5",
|
||||
dwcDarkBlue: "#191847",
|
||||
hazyMint: "#ecf7f8",
|
||||
leafcutterElectricBlue: "#4d6aff",
|
||||
leafcutterLightBlue: "#fafbfd",
|
||||
waterbearElectricPurple: "#332c83",
|
||||
waterbearLightSmokePurple: "#eff3f8",
|
||||
bumpedPurple: "#212058",
|
||||
mutedPurple: "#373669",
|
||||
warningPink: "#ef5da8",
|
||||
lightPink: "#fff0f7",
|
||||
lightGreen: "#f0fff3",
|
||||
lightOrange: "#fff5f0",
|
||||
beige: "#f6f2f1",
|
||||
almostBlack: "#33302f",
|
||||
white: "#ffffff",
|
||||
};
|
||||
|
||||
export const typography: any = {
|
||||
h1: {
|
||||
fontFamily: playfair.style.fontFamily,
|
||||
fontSize: 45,
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.1,
|
||||
margin: 0,
|
||||
},
|
||||
h2: {
|
||||
fontFamily: poppins.style.fontFamily,
|
||||
fontSize: 35,
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.1,
|
||||
margin: 0,
|
||||
},
|
||||
h3: {
|
||||
fontFamily: poppins.style.fontFamily,
|
||||
fontWeight: 400,
|
||||
fontSize: 27,
|
||||
lineHeight: 1.1,
|
||||
margin: 0,
|
||||
},
|
||||
h4: {
|
||||
fontFamily: poppins.style.fontFamily,
|
||||
fontWeight: 700,
|
||||
fontSize: 18,
|
||||
},
|
||||
h5: {
|
||||
fontFamily: roboto.style.fontFamily,
|
||||
fontWeight: 700,
|
||||
fontSize: 16,
|
||||
lineHeight: "24px",
|
||||
textTransform: "uppercase",
|
||||
textAlign: "center",
|
||||
margin: 1,
|
||||
},
|
||||
h6: {
|
||||
fontFamily: roboto.style.fontFamily,
|
||||
fontWeight: 400,
|
||||
fontSize: 14,
|
||||
textAlign: "center",
|
||||
},
|
||||
p: {
|
||||
fontFamily: roboto.style.fontFamily,
|
||||
fontSize: 17,
|
||||
lineHeight: "26.35px",
|
||||
fontWeight: 400,
|
||||
margin: 0,
|
||||
},
|
||||
small: {
|
||||
fontFamily: roboto.style.fontFamily,
|
||||
fontSize: 13,
|
||||
lineHeight: "18px",
|
||||
fontWeight: 400,
|
||||
margin: 0,
|
||||
},
|
||||
};
|
||||
|
|
@ -20,11 +20,12 @@
|
|||
"@mui/material": "^5",
|
||||
"@mui/x-data-grid-pro": "^7.3.2",
|
||||
"@mui/x-date-pickers-pro": "^7.3.2",
|
||||
"bridge-common": "*",
|
||||
"bridge-ui": "*",
|
||||
"date-fns": "^3.6.0",
|
||||
"graphql-request": "^6.1.0",
|
||||
"leafcutter-ui": "*",
|
||||
"material-ui-popup-state": "^5.1.0",
|
||||
"bridge-ui": "*",
|
||||
"mui-chips-input": "^2.1.4",
|
||||
"next": "14.2.3",
|
||||
"next-auth": "^4.24.7",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue