Metamigo -> Bridge
This commit is contained in:
parent
242f3cf6b8
commit
a445762a37
145 changed files with 396 additions and 16668 deletions
3
apps/bridge-frontend/.eslintrc.json
Normal file
3
apps/bridge-frontend/.eslintrc.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
36
apps/bridge-frontend/.gitignore
vendored
Normal file
36
apps/bridge-frontend/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
36
apps/bridge-frontend/README.md
Normal file
36
apps/bridge-frontend/README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
0
apps/bridge-frontend/app/_actions/bots.ts
Normal file
0
apps/bridge-frontend/app/_actions/bots.ts
Normal file
31
apps/bridge-frontend/app/_components/DeleteDialog.tsx
Normal file
31
apps/bridge-frontend/app/_components/DeleteDialog.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { Grid, Box } from "@mui/material";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { typography } from "@/app/_styles/theme";
|
||||
|
||||
interface DeleteDialogProps {
|
||||
title: string;
|
||||
entity: string;
|
||||
children: any;
|
||||
}
|
||||
|
||||
export const DeleteDialog: FC<DeleteDialogProps> = ({ title, entity, children }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { h3 } = typography;
|
||||
|
||||
return (
|
||||
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
|
||||
<Grid container direction="column">
|
||||
<Grid item>
|
||||
<Box sx={h3}>{title}</Box>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{children}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
30
apps/bridge-frontend/app/_components/Detail.tsx
Normal file
30
apps/bridge-frontend/app/_components/Detail.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { Grid, Box } from "@mui/material";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { typography } from "@/app/_styles/theme";
|
||||
|
||||
interface DetailProps {
|
||||
title: string;
|
||||
entity: string;
|
||||
children: any;
|
||||
}
|
||||
|
||||
export const Detail: FC<DetailProps> = ({ title, entity, children }) => {
|
||||
const router = useRouter();
|
||||
const { h3 } = typography;
|
||||
|
||||
return (
|
||||
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
|
||||
<Grid container direction="column">
|
||||
<Grid item>
|
||||
<Box sx={h3}>{title}</Box>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{children}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
30
apps/bridge-frontend/app/_components/Edit.tsx
Normal file
30
apps/bridge-frontend/app/_components/Edit.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { Grid, Box } from "@mui/material";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { typography } from "@/app/_styles/theme";
|
||||
|
||||
interface EditProps {
|
||||
title: string;
|
||||
entity: string;
|
||||
children: any;
|
||||
}
|
||||
|
||||
export const Edit: FC<EditProps> = ({ title, entity, children }) => {
|
||||
const router = useRouter();
|
||||
const { h3 } = typography;
|
||||
|
||||
return (
|
||||
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
|
||||
<Grid container direction="column">
|
||||
<Grid item>
|
||||
<Box sx={h3}>{title}</Box>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{children}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
38
apps/bridge-frontend/app/_components/InternalLayout.tsx
Normal file
38
apps/bridge-frontend/app/_components/InternalLayout.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
"use client";
|
||||
|
||||
import { FC, PropsWithChildren, useState } from "react";
|
||||
import { Grid } from "@mui/material";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
import { CssBaseline } from "@mui/material";
|
||||
import { css, Global } from "@emotion/react";
|
||||
import { fonts } from "@/app/_styles/theme";
|
||||
|
||||
type LayoutProps = PropsWithChildren<{
|
||||
docs?: any;
|
||||
}>;
|
||||
|
||||
export const InternalLayout: FC<PropsWithChildren> = ({ children }) => {
|
||||
const [open, setOpen] = useState(true);
|
||||
const { roboto } = fonts;
|
||||
const globalCSS = css`
|
||||
* {
|
||||
font-family: ${roboto.style.fontFamily};
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Global styles={globalCSS} />
|
||||
<CssBaseline />
|
||||
<Grid container direction="row">
|
||||
<Sidebar open={open} setOpen={setOpen} />
|
||||
<Grid
|
||||
item
|
||||
sx={{ ml: open ? "270px" : "100px", width: "100%", height: "100vh" }}
|
||||
>
|
||||
{children as any}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
86
apps/bridge-frontend/app/_components/List.tsx
Normal file
86
apps/bridge-frontend/app/_components/List.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import { Grid, Box } from "@mui/material";
|
||||
import { DataGridPro, GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { typography } from "@/app/_styles/theme";
|
||||
|
||||
interface ListProps {
|
||||
title: string;
|
||||
entity: string;
|
||||
rows: any;
|
||||
columns: GridColDef<any>[];
|
||||
}
|
||||
|
||||
export const List: FC<ListProps> = ({ title, entity, rows, columns }) => {
|
||||
const router = useRouter();
|
||||
const { h3 } = typography;
|
||||
|
||||
const onRowClick = (id: string) => {
|
||||
router.push(`/${entity}/${id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
|
||||
<Grid container direction="column">
|
||||
<Grid item>
|
||||
<Box sx={h3}>{title}</Box>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: "#ddd",
|
||||
border: 0,
|
||||
width: "100%",
|
||||
height: "100vh",
|
||||
".MuiDataGrid-row": {
|
||||
cursor: "pointer",
|
||||
"&:hover": {
|
||||
backgroundColor: "#1982fc33 !important",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
".MuiDataGrid-row:nth-of-type(1n)": {
|
||||
backgroundColor: "#f3f3f3",
|
||||
},
|
||||
".MuiDataGrid-row:nth-of-type(2n)": {
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
".MuiDataGrid-columnHeaderTitle": {
|
||||
color: "#333",
|
||||
fontWeight: "bold",
|
||||
fontSize: 11,
|
||||
margin: "0 auto",
|
||||
},
|
||||
".MuiDataGrid-columnHeader": {
|
||||
backgroundColor: "#ccc",
|
||||
border: 0,
|
||||
borderBottom: "3px solid #ddd",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DataGridPro
|
||||
rows={rows}
|
||||
columns={columns}
|
||||
density="compact"
|
||||
pagination
|
||||
initialState={{
|
||||
pagination: { paginationModel: { pageSize: 25 } },
|
||||
}}
|
||||
pageSizeOptions={[5, 10, 25]}
|
||||
paginationMode="client"
|
||||
sx={{ height: "100vh" }}
|
||||
// rowBuffer={30}
|
||||
rowHeight={46}
|
||||
scrollbarSize={0}
|
||||
disableVirtualization
|
||||
disableColumnMenu
|
||||
onRowClick={(row: any) => onRowClick(row.id)}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
350
apps/bridge-frontend/app/_components/Sidebar.tsx
Normal file
350
apps/bridge-frontend/app/_components/Sidebar.tsx
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
"use client";
|
||||
|
||||
import { FC } from "react";
|
||||
import {
|
||||
Box,
|
||||
Grid,
|
||||
Typography,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
Drawer,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
ExpandCircleDown as ExpandCircleDownIcon,
|
||||
AccountCircle as AccountCircleIcon,
|
||||
Chat as ChatIcon,
|
||||
PermPhoneMsg as PhoneIcon,
|
||||
WhatsApp as WhatsAppIcon,
|
||||
Facebook as FacebookIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { usePathname } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { fonts } from "@/app/_styles/theme";
|
||||
import LinkLogo from "@/public/link-logo-small.png";
|
||||
// import { useSession, signOut } from "next-auth/react";
|
||||
|
||||
const openWidth = 270;
|
||||
const closedWidth = 100;
|
||||
|
||||
const MenuItem = ({
|
||||
name,
|
||||
href,
|
||||
Icon,
|
||||
iconSize,
|
||||
inset = false,
|
||||
selected = false,
|
||||
open = true,
|
||||
badge,
|
||||
target = "_self",
|
||||
}: any) => (
|
||||
<Link href={href} target={target}>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
p: 0,
|
||||
mb: 1,
|
||||
bl: iconSize === 0 ? "1px solid white" : "inherit",
|
||||
}}
|
||||
selected={selected}
|
||||
>
|
||||
{iconSize > 0 ? (
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
color: `white`,
|
||||
minWidth: 0,
|
||||
mr: 2,
|
||||
textAlign: "center",
|
||||
margin: open ? "0 8 0 0" : "0 auto",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
mr: 0.5,
|
||||
mt: "-4px",
|
||||
}}
|
||||
>
|
||||
<Icon />
|
||||
</Box>
|
||||
</ListItemIcon>
|
||||
) : (
|
||||
<Box
|
||||
sx={{
|
||||
width: 30,
|
||||
height: "28px",
|
||||
position: "relative",
|
||||
ml: "9px",
|
||||
mr: "1px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "1px",
|
||||
height: "56px",
|
||||
backgroundColor: "white",
|
||||
position: "absolute",
|
||||
left: "3px",
|
||||
top: "-10px",
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
width: "42px",
|
||||
height: "42px",
|
||||
position: "absolute",
|
||||
top: "-27px",
|
||||
left: "3px",
|
||||
border: "solid 1px #fff",
|
||||
borderColor: "transparent transparent transparent #fff",
|
||||
borderRadius: "60px",
|
||||
rotate: "-35deg",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{open && (
|
||||
<ListItemText
|
||||
inset={inset}
|
||||
primary={
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontSize: 16,
|
||||
fontWeight: "bold",
|
||||
border: 0,
|
||||
textAlign: "left",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{badge && badge > 0 ? (
|
||||
<ListItemSecondaryAction>
|
||||
<Typography
|
||||
color="textSecondary"
|
||||
variant="body1"
|
||||
className="badge"
|
||||
sx={{
|
||||
backgroundColor: "#FFB620",
|
||||
color: "black !important",
|
||||
borderRadius: 10,
|
||||
px: 1,
|
||||
fontSize: 12,
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{badge}
|
||||
</Typography>
|
||||
</ListItemSecondaryAction>
|
||||
) : null}
|
||||
</ListItemButton>
|
||||
</Link>
|
||||
);
|
||||
|
||||
interface SidebarProps {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||
const pathname = usePathname();
|
||||
const { poppins } = fonts;
|
||||
// const { data: session } = useSession();
|
||||
// const username = session?.user?.name || "User";
|
||||
|
||||
// const logout = () => {
|
||||
// signOut({ callbackUrl: "/login" });
|
||||
// };
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
sx={{ width: open ? openWidth : closedWidth, flexShrink: 0 }}
|
||||
variant="permanent"
|
||||
anchor="left"
|
||||
open={open}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
width: open ? openWidth : closedWidth,
|
||||
border: 0,
|
||||
overflow: "visible",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 20,
|
||||
right: open ? -8 : -16,
|
||||
color: "#1C75FD",
|
||||
rotate: open ? "90deg" : "-90deg",
|
||||
}}
|
||||
onClick={() => {
|
||||
setOpen!(!open);
|
||||
}}
|
||||
>
|
||||
<ExpandCircleDownIcon
|
||||
sx={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
background: "white",
|
||||
borderRadius: 500,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Grid
|
||||
container
|
||||
direction="column"
|
||||
justifyContent="space-between"
|
||||
wrap="nowrap"
|
||||
spacing={0}
|
||||
sx={{ backgroundColor: "#25272A", height: "100%", p: 2 }}
|
||||
>
|
||||
<Grid item container>
|
||||
<Grid item sx={{ width: open ? "40px" : "100%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "40px",
|
||||
height: "40px",
|
||||
margin: open ? "0" : "0 auto",
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={LinkLogo}
|
||||
alt="Link logo"
|
||||
width={40}
|
||||
height={40}
|
||||
style={{
|
||||
objectFit: "cover",
|
||||
filter: "grayscale(100) brightness(100)",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
.
|
||||
</Grid>
|
||||
{open && (
|
||||
<Grid item>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
fontSize: 26,
|
||||
color: "white",
|
||||
fontWeight: 700,
|
||||
mt: 1,
|
||||
ml: 0.5,
|
||||
fontFamily: poppins.style.fontFamily,
|
||||
}}
|
||||
>
|
||||
CDR Bridge
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Box
|
||||
sx={{
|
||||
height: "0.5px",
|
||||
width: "100%",
|
||||
backgroundColor: "#666",
|
||||
mb: 1,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
container
|
||||
direction="column"
|
||||
sx={{
|
||||
mt: "6px",
|
||||
overflow: "scroll",
|
||||
scrollbarWidth: "none",
|
||||
msOverflowStyle: "none",
|
||||
"&::-webkit-scrollbar": { display: "none" },
|
||||
}}
|
||||
flexGrow={1}
|
||||
>
|
||||
<List
|
||||
component="nav"
|
||||
sx={{
|
||||
a: {
|
||||
textDecoration: "none",
|
||||
|
||||
".MuiListItemButton-root": {
|
||||
p: 1,
|
||||
borderRadius: 2,
|
||||
"&:hover": {
|
||||
background: "#555",
|
||||
},
|
||||
".MuiTypography-root": {
|
||||
p: {
|
||||
color: "#999 !important",
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
".badge": {
|
||||
p: { fontSize: 12, color: "black !important" },
|
||||
},
|
||||
},
|
||||
".Mui-selected": {
|
||||
background: "#444",
|
||||
color: "#fff !important",
|
||||
".MuiTypography-root": {
|
||||
p: {
|
||||
color: "#fff !important",
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
".badge": {
|
||||
p: { fontSize: 12, color: "black !important" },
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
name="Whatsapp"
|
||||
href="/whatsapp"
|
||||
selected={pathname.endsWith("/whatsapp")}
|
||||
Icon={WhatsAppIcon}
|
||||
iconSize={20}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Signal"
|
||||
href="/signal"
|
||||
selected={pathname.startsWith("/signal")}
|
||||
Icon={ChatIcon}
|
||||
iconSize={20}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Facebook"
|
||||
href="/facebook"
|
||||
selected={pathname.startsWith("/facebook")}
|
||||
Icon={FacebookIcon}
|
||||
iconSize={20}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Voice"
|
||||
href="/voice"
|
||||
selected={pathname.startsWith("/voice")}
|
||||
Icon={PhoneIcon}
|
||||
iconSize={20}
|
||||
/>
|
||||
<MenuItem
|
||||
name="Users"
|
||||
href="/users"
|
||||
selected={pathname.startsWith("/users")}
|
||||
Icon={AccountCircleIcon}
|
||||
iconSize={20}
|
||||
/>
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
15
apps/bridge-frontend/app/_lib/authentication.ts
Normal file
15
apps/bridge-frontend/app/_lib/authentication.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import NextAuth from "next-auth"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import { KyselyAdapter } from "@auth/kysely-adapter"
|
||||
import { db } from "./database";
|
||||
|
||||
export const authOptions = NextAuth({
|
||||
// @ts-ignore
|
||||
adapter: KyselyAdapter(db),
|
||||
providers: [
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
}),
|
||||
],
|
||||
})
|
||||
89
apps/bridge-frontend/app/_lib/database.ts
Normal file
89
apps/bridge-frontend/app/_lib/database.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { PostgresDialect } from "kysely";
|
||||
import { Pool } from "pg";
|
||||
import { KyselyAuth } from "@auth/kysely-adapter";
|
||||
import { CamelCasePlugin } from "kysely";
|
||||
import type { GeneratedAlways } from "kysely";
|
||||
|
||||
interface Database {
|
||||
User: {
|
||||
id: string;
|
||||
name: string | null;
|
||||
email: string;
|
||||
emailVerified: Date | null;
|
||||
image: string | null;
|
||||
};
|
||||
|
||||
Account: {
|
||||
id: GeneratedAlways<string>;
|
||||
userId: string;
|
||||
type: "oidc" | "oauth" | "email" | "webauthn";
|
||||
provider: string;
|
||||
providerAccountId: string;
|
||||
refresh_token: string | undefined;
|
||||
access_token: string | undefined;
|
||||
expires_at: number | undefined;
|
||||
token_type: Lowercase<string> | undefined;
|
||||
scope: string | undefined;
|
||||
id_token: string | undefined;
|
||||
session_state: string | undefined;
|
||||
};
|
||||
|
||||
Session: {
|
||||
id: GeneratedAlways<string>;
|
||||
userId: string;
|
||||
sessionToken: string;
|
||||
expires: Date;
|
||||
};
|
||||
|
||||
VerificationToken: {
|
||||
identifier: string;
|
||||
token: string;
|
||||
expires: Date;
|
||||
};
|
||||
|
||||
WhatsAppBot: {
|
||||
id: GeneratedAlways<string>;
|
||||
name: string;
|
||||
phoneNumber: string;
|
||||
createdBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
FacebookBot: {
|
||||
id: GeneratedAlways<string>;
|
||||
name: string;
|
||||
createdBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
VoiceBot: {
|
||||
id: GeneratedAlways<string>;
|
||||
name: string;
|
||||
createdBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
SignalBot: {
|
||||
id: GeneratedAlways<string>;
|
||||
name: string;
|
||||
phoneNumber: string;
|
||||
createdBy: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
}
|
||||
|
||||
export const db = new KyselyAuth<Database>({
|
||||
dialect: new PostgresDialect({
|
||||
pool: new Pool({
|
||||
host: process.env.DATABASE_HOST,
|
||||
database: process.env.DATABASE_NAME,
|
||||
user: process.env.DATABASE_USER,
|
||||
password: process.env.DATABASE_PASSWORD,
|
||||
}),
|
||||
}),
|
||||
plugins: [new CamelCasePlugin()],
|
||||
});
|
||||
104
apps/bridge-frontend/app/_lib/facebook.ts
Normal file
104
apps/bridge-frontend/app/_lib/facebook.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { Service } from "./service";
|
||||
|
||||
const getAllBots = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const getOneBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const sendMessage = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const receiveMessages = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const registerBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const resetBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const requestCode = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const unverifyBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const refreshBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const createBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const deleteBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const handleWebhook = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
const { searchParams } = req.nextUrl;
|
||||
const token = searchParams.get("hub.verify_token");
|
||||
|
||||
if (token !== process.env.FB_VERIFY_TOKEN) {
|
||||
// return NextResponse.error("Invalid token", { status: 403 });
|
||||
}
|
||||
|
||||
if (searchParams.get("hub.mode") === "subscribe") {
|
||||
const challenge = searchParams.get("hub.challenge");
|
||||
console.log(token);
|
||||
console.log(challenge);
|
||||
|
||||
return new Response(challenge, { status: 200 }) as NextResponse;
|
||||
}
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
export const Facebook: Service = {
|
||||
getAllBots,
|
||||
getOneBot,
|
||||
sendMessage,
|
||||
receiveMessages,
|
||||
registerBot,
|
||||
resetBot,
|
||||
requestCode,
|
||||
unverifyBot,
|
||||
refreshBot,
|
||||
createBot,
|
||||
deleteBot,
|
||||
handleWebhook,
|
||||
};
|
||||
50
apps/bridge-frontend/app/_lib/routing.ts
Normal file
50
apps/bridge-frontend/app/_lib/routing.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { Service } from "./service";
|
||||
import { Facebook } from "./facebook";
|
||||
|
||||
const services: Record<string, Service> = {
|
||||
facebook: Facebook,
|
||||
none: NextResponse.error() as any,
|
||||
};
|
||||
|
||||
const getService = (req: NextRequest): Service => {
|
||||
const service = req.nextUrl.searchParams.get("service") ?? "none";
|
||||
return services[service];
|
||||
};
|
||||
|
||||
export const getAllBots = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.getAllBots(req);
|
||||
|
||||
export const getOneBot = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.getOneBot(req);
|
||||
|
||||
export const sendMessage = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.sendMessage(req);
|
||||
|
||||
export const receiveMessages = async (
|
||||
req: NextRequest,
|
||||
): Promise<NextResponse> => getService(req)?.receiveMessages(req);
|
||||
|
||||
export const registerBot = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.registerBot(req);
|
||||
|
||||
export const resetBot = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.resetBot(req);
|
||||
|
||||
export const requestCode = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.requestCode(req);
|
||||
|
||||
export const unverifyBot = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.unverifyBot(req);
|
||||
|
||||
export const refreshBot = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.refreshBot(req);
|
||||
|
||||
export const createBot = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.createBot(req);
|
||||
|
||||
export const deleteBot = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.deleteBot(req);
|
||||
|
||||
export const handleWebhook = async (req: NextRequest): Promise<NextResponse> =>
|
||||
getService(req)?.handleWebhook(req);
|
||||
16
apps/bridge-frontend/app/_lib/service.ts
Normal file
16
apps/bridge-frontend/app/_lib/service.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export interface Service {
|
||||
getAllBots: (req: NextRequest) => Promise<NextResponse>;
|
||||
getOneBot: (req: NextRequest) => Promise<NextResponse>;
|
||||
sendMessage: (req: NextRequest) => Promise<NextResponse>;
|
||||
receiveMessages: (req: NextRequest) => Promise<NextResponse>;
|
||||
registerBot: (req: NextRequest) => Promise<NextResponse>;
|
||||
resetBot: (req: NextRequest) => Promise<NextResponse>;
|
||||
requestCode: (req: NextRequest) => Promise<NextResponse>;
|
||||
unverifyBot: (req: NextRequest) => Promise<NextResponse>;
|
||||
refreshBot: (req: NextRequest) => Promise<NextResponse>;
|
||||
createBot: (req: NextRequest) => Promise<NextResponse>;
|
||||
deleteBot: (req: NextRequest) => Promise<NextResponse>;
|
||||
handleWebhook: (req: NextRequest) => Promise<NextResponse>;
|
||||
}
|
||||
82
apps/bridge-frontend/app/_lib/signal.ts
Normal file
82
apps/bridge-frontend/app/_lib/signal.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { Service } from "./service";
|
||||
|
||||
const getAllBots = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const getOneBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const sendMessage = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const receiveMessages = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const registerBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const resetBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const requestCode = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const unverifyBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const refreshBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const createBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const deleteBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
export const Signal: Service = {
|
||||
getAllBots,
|
||||
getOneBot,
|
||||
sendMessage,
|
||||
receiveMessages,
|
||||
registerBot,
|
||||
resetBot,
|
||||
requestCode,
|
||||
unverifyBot,
|
||||
refreshBot,
|
||||
createBot,
|
||||
deleteBot,
|
||||
};
|
||||
82
apps/bridge-frontend/app/_lib/voice.ts
Normal file
82
apps/bridge-frontend/app/_lib/voice.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { Service } from "./service";
|
||||
|
||||
const getAllBots = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const getOneBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const sendMessage = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const receiveMessages = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const registerBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const resetBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const requestCode = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const unverifyBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const refreshBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const createBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const deleteBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
export const Voice: Service = {
|
||||
getAllBots,
|
||||
getOneBot,
|
||||
sendMessage,
|
||||
receiveMessages,
|
||||
registerBot,
|
||||
resetBot,
|
||||
requestCode,
|
||||
unverifyBot,
|
||||
refreshBot,
|
||||
createBot,
|
||||
deleteBot,
|
||||
};
|
||||
82
apps/bridge-frontend/app/_lib/whatsapp.ts
Normal file
82
apps/bridge-frontend/app/_lib/whatsapp.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { Service } from "./service";
|
||||
|
||||
const getAllBots = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const getOneBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const sendMessage = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const receiveMessages = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const registerBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const resetBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const requestCode = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const unverifyBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const refreshBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const createBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
const deleteBot = async (req: NextRequest) => {
|
||||
console.log({ req });
|
||||
|
||||
return NextResponse.json({ response: "ok" });
|
||||
};
|
||||
|
||||
export const Whatsapp: Service = {
|
||||
getAllBots,
|
||||
getOneBot,
|
||||
sendMessage,
|
||||
receiveMessages,
|
||||
registerBot,
|
||||
resetBot,
|
||||
requestCode,
|
||||
unverifyBot,
|
||||
refreshBot,
|
||||
createBot,
|
||||
deleteBot,
|
||||
};
|
||||
112
apps/bridge-frontend/app/_styles/theme.ts
Normal file
112
apps/bridge-frontend/app/_styles/theme.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
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,
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { receiveMessages as GET } from "@/app/_lib/routing";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { registerBot as POST } from "@/app/_lib/routing";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { requestCode as POST } from "@/app/_lib/routing";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { resetBot as POST } from "@/app/_lib/routing";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { getOneBot as GET } from "@/app/_lib/routing";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { sendMessage as POST } from "@/app/_lib/routing";
|
||||
1
apps/bridge-frontend/app/api/[service]/bots/route.ts
Normal file
1
apps/bridge-frontend/app/api/[service]/bots/route.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { getAllBots as GET } from "@/app/_lib/routing";
|
||||
8
apps/bridge-frontend/app/api/[service]/webhooks/route.ts
Normal file
8
apps/bridge-frontend/app/api/[service]/webhooks/route.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
const handleRequest = async (req: NextRequest, res: NextResponse) => {
|
||||
|
||||
return NextResponse.json({ message: "ok" });
|
||||
};
|
||||
|
||||
export { handleRequest as GET, handleRequest as POST };
|
||||
6
apps/bridge-frontend/app/api/auth/[...nextauth]/route.ts
Normal file
6
apps/bridge-frontend/app/api/auth/[...nextauth]/route.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import NextAuth from "next-auth";
|
||||
import { authOptions } from "@/app/_lib/authentication";
|
||||
|
||||
const handler = NextAuth(authOptions);
|
||||
|
||||
export { handler as GET, handler as POST };
|
||||
3
apps/bridge-frontend/app/facebook/[id]/edit/page.tsx
Normal file
3
apps/bridge-frontend/app/facebook/[id]/edit/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Facebook view</h1>;
|
||||
}
|
||||
3
apps/bridge-frontend/app/facebook/[id]/page.tsx
Normal file
3
apps/bridge-frontend/app/facebook/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Facebook view</h1>;
|
||||
}
|
||||
3
apps/bridge-frontend/app/facebook/new/page.tsx
Normal file
3
apps/bridge-frontend/app/facebook/new/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Facebook Home</h1>;
|
||||
}
|
||||
46
apps/bridge-frontend/app/facebook/page.tsx
Normal file
46
apps/bridge-frontend/app/facebook/page.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"use client";
|
||||
|
||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { List } from "ui";
|
||||
|
||||
export default function Page() {
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "phoneNumber",
|
||||
headerName: "Phone Number",
|
||||
flex: 2,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "Created At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.createdAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "updatedAt",
|
||||
headerName: "Updated At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.updatedAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: 10,
|
||||
phoneNumber: "1234567890",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<List title="Facebook" entity="facebook" rows={rows} columns={columns} />
|
||||
);
|
||||
}
|
||||
BIN
apps/bridge-frontend/app/favicon.ico
Normal file
BIN
apps/bridge-frontend/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
26
apps/bridge-frontend/app/layout.tsx
Normal file
26
apps/bridge-frontend/app/layout.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import type { Metadata } from "next";
|
||||
import { InternalLayout } from "./_components/InternalLayout";
|
||||
import { LicenseInfo } from "@mui/x-license";
|
||||
|
||||
LicenseInfo.setLicenseKey(
|
||||
"7c9bf25d9e240f76e77cbf7d2ba58a23Tz02NjU4OCxFPTE3MTU4NjIzMzQ2ODgsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=",
|
||||
);
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "CDR Bridge",
|
||||
description: "",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<InternalLayout>{children}</InternalLayout>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
230
apps/bridge-frontend/app/page.module.css
Normal file
230
apps/bridge-frontend/app/page.module.css
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.description {
|
||||
display: inherit;
|
||||
justify-content: inherit;
|
||||
align-items: inherit;
|
||||
font-size: 0.85rem;
|
||||
max-width: var(--max-width);
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.description a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.description p {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
background-color: rgba(var(--callout-rgb), 0.5);
|
||||
border: 1px solid rgba(var(--callout-border-rgb), 0.3);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.code {
|
||||
font-weight: 700;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(25%, auto));
|
||||
max-width: 100%;
|
||||
width: var(--max-width);
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 1rem 1.2rem;
|
||||
border-radius: var(--border-radius);
|
||||
background: rgba(var(--card-rgb), 0);
|
||||
border: 1px solid rgba(var(--card-border-rgb), 0);
|
||||
transition: background 200ms, border 200ms;
|
||||
}
|
||||
|
||||
.card span {
|
||||
display: inline-block;
|
||||
transition: transform 200ms;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
opacity: 0.6;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
max-width: 30ch;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.center::before {
|
||||
background: var(--secondary-glow);
|
||||
border-radius: 50%;
|
||||
width: 480px;
|
||||
height: 360px;
|
||||
margin-left: -400px;
|
||||
}
|
||||
|
||||
.center::after {
|
||||
background: var(--primary-glow);
|
||||
width: 240px;
|
||||
height: 180px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.center::before,
|
||||
.center::after {
|
||||
content: "";
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
filter: blur(45px);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: relative;
|
||||
}
|
||||
/* Enable hover only on non-touch devices */
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.card:hover {
|
||||
background: rgba(var(--card-rgb), 0.1);
|
||||
border: 1px solid rgba(var(--card-border-rgb), 0.15);
|
||||
}
|
||||
|
||||
.card:hover span {
|
||||
transform: translateX(4px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
.card:hover span {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 700px) {
|
||||
.content {
|
||||
padding: 4rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
margin-bottom: 120px;
|
||||
max-width: 320px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 1rem 2.5rem;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.center {
|
||||
padding: 8rem 0 6rem;
|
||||
}
|
||||
|
||||
.center::before {
|
||||
transform: none;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.description a {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.description p,
|
||||
.description div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.description p {
|
||||
align-items: center;
|
||||
inset: 0 0 auto;
|
||||
padding: 2rem 1rem 1.4rem;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(var(--background-start-rgb), 1),
|
||||
rgba(var(--callout-rgb), 0.5)
|
||||
);
|
||||
background-clip: padding-box;
|
||||
backdrop-filter: blur(24px);
|
||||
}
|
||||
|
||||
.description div {
|
||||
align-items: flex-end;
|
||||
pointer-events: none;
|
||||
inset: auto 0 0;
|
||||
padding: 2rem;
|
||||
height: 200px;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
rgb(var(--background-end-rgb)) 40%
|
||||
);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet and Smaller Desktop */
|
||||
@media (min-width: 701px) and (max-width: 1120px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.vercelLogo {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.logo {
|
||||
filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
4
apps/bridge-frontend/app/page.tsx
Normal file
4
apps/bridge-frontend/app/page.tsx
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export default function Page() {
|
||||
return <h1>Home</h1>;
|
||||
}
|
||||
|
||||
9
apps/bridge-frontend/app/signal/[id]/page.tsx
Normal file
9
apps/bridge-frontend/app/signal/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { Detail } from "@/app/_components/Detail";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Detail title="Signal Detail" entity="signal">
|
||||
<p>Cool</p>
|
||||
</Detail>
|
||||
);
|
||||
}
|
||||
3
apps/bridge-frontend/app/signal/new/page.tsx
Normal file
3
apps/bridge-frontend/app/signal/new/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Signal new</h1>;
|
||||
}
|
||||
46
apps/bridge-frontend/app/signal/page.tsx
Normal file
46
apps/bridge-frontend/app/signal/page.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"use client";
|
||||
|
||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { List } from "ui";
|
||||
|
||||
export default function Page() {
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "phoneNumber",
|
||||
headerName: "Phone Number",
|
||||
flex: 2,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "Created At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.createdAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "updatedAt",
|
||||
headerName: "Updated At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.updatedAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: 10,
|
||||
phoneNumber: "1234567890",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<List title="Signal Bots" entity="signal" rows={rows} columns={columns} />
|
||||
);
|
||||
}
|
||||
44
apps/bridge-frontend/app/users/page.tsx
Normal file
44
apps/bridge-frontend/app/users/page.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"use client";
|
||||
|
||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { List } from "ui";
|
||||
|
||||
export default function Page() {
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "phoneNumber",
|
||||
headerName: "Phone Number",
|
||||
flex: 2,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "Created At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.createdAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "updatedAt",
|
||||
headerName: "Updated At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.updatedAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: 10,
|
||||
phoneNumber: "1234567890",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
];
|
||||
|
||||
return <List title="Users" entity="users" rows={rows} columns={columns} />;
|
||||
}
|
||||
3
apps/bridge-frontend/app/voice/[id]/edit/page.tsx
Normal file
3
apps/bridge-frontend/app/voice/[id]/edit/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Voice Edit</h1>;
|
||||
}
|
||||
3
apps/bridge-frontend/app/voice/[id]/page.tsx
Normal file
3
apps/bridge-frontend/app/voice/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Voice detail</h1>;
|
||||
}
|
||||
3
apps/bridge-frontend/app/voice/new/page.tsx
Normal file
3
apps/bridge-frontend/app/voice/new/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Voice Home</h1>;
|
||||
}
|
||||
46
apps/bridge-frontend/app/voice/page.tsx
Normal file
46
apps/bridge-frontend/app/voice/page.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"use client";
|
||||
|
||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { List } from "ui";
|
||||
|
||||
export default function Page() {
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "phoneNumber",
|
||||
headerName: "Phone Number",
|
||||
flex: 2,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "Created At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.createdAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "updatedAt",
|
||||
headerName: "Updated At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.updatedAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const rows = [
|
||||
{
|
||||
id: 10,
|
||||
phoneNumber: "1234567890",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<List title="Voice bots" entity="users" rows={rows} columns={columns} />
|
||||
);
|
||||
}
|
||||
3
apps/bridge-frontend/app/whatsapp/[id]/edit/page.tsx
Normal file
3
apps/bridge-frontend/app/whatsapp/[id]/edit/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Whatsapp edit</h1>;
|
||||
}
|
||||
6
apps/bridge-frontend/app/whatsapp/[id]/page.tsx
Normal file
6
apps/bridge-frontend/app/whatsapp/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { db } from "@/app/_lib/database";
|
||||
|
||||
export default async function Page() {
|
||||
const rows = await db.selectFrom("WhatsAppBot").
|
||||
return <h1>Whatsapp View</h1>;
|
||||
}
|
||||
3
apps/bridge-frontend/app/whatsapp/new/page.tsx
Normal file
3
apps/bridge-frontend/app/whatsapp/new/page.tsx
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function Page() {
|
||||
return <h1>Whatsapp new</h1>;
|
||||
}
|
||||
45
apps/bridge-frontend/app/whatsapp/page.tsx
Normal file
45
apps/bridge-frontend/app/whatsapp/page.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
"use client";
|
||||
|
||||
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||
import { List } from "ui";
|
||||
import { db } from "@/app/_lib/database";
|
||||
|
||||
export default async function Page() {
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
field: "id",
|
||||
headerName: "ID",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "phoneNumber",
|
||||
headerName: "Phone Number",
|
||||
flex: 2,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "Created At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.createdAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "updatedAt",
|
||||
headerName: "Updated At",
|
||||
valueGetter: (params: any) =>
|
||||
new Date(params.row?.updatedAt).toLocaleString(),
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const rows = await db.selectFrom("WhatsAppBot").selectAll().execute();
|
||||
|
||||
return (
|
||||
<List
|
||||
title="Whatsapp Bots"
|
||||
rows={rows}
|
||||
columns={columns}
|
||||
onRowClick={() => {}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
6
apps/bridge-frontend/next.config.mjs
Normal file
6
apps/bridge-frontend/next.config.mjs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
transpilePackages: ["ui"]
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
49
apps/bridge-frontend/package.json
Normal file
49
apps/bridge-frontend/package.json
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"name": "bridge-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/kysely-adapter": "^1.0.0",
|
||||
"@emotion/cache": "^11.11.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@mui/icons-material": "^5",
|
||||
"@mui/lab": "^5.0.0-alpha.170",
|
||||
"@mui/material": "^5",
|
||||
"@mui/material-nextjs": "^5.15.11",
|
||||
"@mui/x-data-grid-pro": "^7.3.0",
|
||||
"@mui/x-date-pickers-pro": "^7.2.0",
|
||||
"@mui/x-license": "^7.2.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"kysely": "^0.26.1",
|
||||
"material-ui-popup-state": "^5.1.0",
|
||||
"mui-chips-input": "^2.1.4",
|
||||
"next": "14.2.2",
|
||||
"next-auth": "^4.24.7",
|
||||
"pg": "^8.11.5",
|
||||
"react": "18.2.0",
|
||||
"react-cookie": "^7.1.4",
|
||||
"react-digit-input": "^2.1.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-qr-code": "^2.0.12",
|
||||
"react-timer-hook": "^3.0.7",
|
||||
"tss-react": "^4.9.6",
|
||||
"ui": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/pg": "^8.11.5",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.2",
|
||||
"ts-config": "*",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
BIN
apps/bridge-frontend/public/link-logo-small.png
Normal file
BIN
apps/bridge-frontend/public/link-logo-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
26
apps/bridge-frontend/tsconfig.json
Normal file
26
apps/bridge-frontend/tsconfig.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue