diff --git a/.github/ISSUE_TEMPLATE/new-integration-request.md b/.github/ISSUE_TEMPLATE/new-integration-request.md deleted file mode 100644 index 03bfa1783..000000000 --- a/.github/ISSUE_TEMPLATE/new-integration-request.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: New Integration Request -about: Need an Integration that isn't in our catalog yet? Ask here! -title: Add XXXXX integration to Panora -labels: backend, embedded-catalog, good first issue -assignees: '' - ---- - -**What tools would you like to integrate with?** -*(example: Salesforce, Hubspot, Shopify)* - - -**What are the objects you'd like to interact with?** -*(example: Deals, Customers, Orders,)* - -** -**Do you anticipate your product will mostly read, or write data to this party party software?** -*(example: Our product only needs to watch new orders in Shopify, or Our product needs to create contacts in third-party CRMs...)* - -Please find details about how to build integrations [here](https://docs.panora.dev/open-source/contributors) . Our [discord](https://discord.gg/feRW57Ym3D) community is also a great place to get support! diff --git a/.github/ISSUE_TEMPLATE/new-integration-request.yml b/.github/ISSUE_TEMPLATE/new-integration-request.yml new file mode 100644 index 000000000..e30b73b2a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-integration-request.yml @@ -0,0 +1,49 @@ +name: New Integration Request +description: Need an Integration that isn't in our catalog yet? Ask here! +title: "feat: Add integration with: #SOFTWARE_NAME" +labels: ["backend", "embedded-catalog", "good first issue"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this! + - type: input + id: contact + attributes: + label: Contact Details + description: "How can we get in touch with you if we need more info? (We'll edit the issue to hide your email)" + placeholder: ex. email@example.com + validations: + required: true + - type: dropdown + id: software_category + attributes: + label: Category + multiple: true + description: "What type of software do you want to build integrations with?" + options: + - Accounting + - Calendar + - Cybersecurity + - CRM + - Data Warehouse + - Document Signature + - Ecommerce + - Email + - File Storage + - HRIS + - Logging + - Marketing Automation + - Payments + - Point of Sale + - Source Code Management + - Telematics and IOT + - Ticketing + - Other + validations: + required: true + - type: textarea + id: platforms + attributes: + label: Platforms + description: "Which specific platforms would you integrate with? (example: Salesforce, Hubspot, Zendesk, Jira...)" \ No newline at end of file diff --git a/apps/client-ts/package.json b/apps/client-ts/package.json index 09dcb1656..501a16ecd 100644 --- a/apps/client-ts/package.json +++ b/apps/client-ts/package.json @@ -20,13 +20,13 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", - "@radix-ui/react-scroll-area": "^1.0.5", "@tanstack/react-query": "^5.12.2", "@tanstack/react-query-devtools": "^5.25.0", "@tanstack/react-query-next-experimental": "^5.25.0", @@ -37,7 +37,10 @@ "clsx": "^2.1.0", "cmdk": "^0.2.1", "cookies": "^0.9.1", + "coolshapes-react": "^1.0.1", "date-fns": "^3.3.1", + "jotai": "^2.8.0", + "js-cookie": "^3.0.5", "lucide-react": "^0.344.0", "next": "14.1.2", "next-themes": "^0.2.1", @@ -45,14 +48,14 @@ "react": "^18", "react-day-picker": "^8.10.0", "react-dom": "^18", - "react-hook-form": "^7.51.0", + "react-hook-form": "^7.51.2", + "react-resizable-panels": "^2.0.19", "recharts": "^2.10.1", "sonner": "^1.4.3", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", "zod": "^3.22.4", - "zustand": "^4.4.7", - "js-cookie": "^3.0.5" + "zustand": "^4.4.7" }, "devDependencies": { "@types/cookies": "^0.9.0", diff --git a/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx b/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx index 6035e36d6..3b6613cdd 100644 --- a/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx +++ b/apps/client-ts/src/app/(Dashboard)/api-keys/page.tsx @@ -2,7 +2,6 @@ import { PlusCircledIcon } from "@radix-ui/react-icons"; import { Button } from "@/components/ui/button" -import { columns } from "@/components/ApiKeys/data/columns"; import { DataTable } from "@/components/shared/data-table"; import { Dialog, @@ -22,9 +21,9 @@ import { FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input"; -import useApiKeys from "@/hooks/useApiKeys"; +import useApiKeys from "@/hooks/get/useApiKeys"; import useProjectStore from "@/state/projectStore"; -import useApiKeyMutation from "@/hooks/mutations/useApiKeyMutation"; +import useCreateApiKey from "@/hooks/create/useCreateApiKey"; import useProfileStore from "@/state/profileStore"; import { Suspense, useEffect, useState } from "react"; import { cn } from "@/lib/utils"; @@ -35,6 +34,8 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import { DataTableLoading } from "@/components/shared/data-table-loading"; import {CustomHeading} from "@/components/shared/custom-heading"; +import { useColumns } from "@/components/ApiKeys/columns"; +import { PlusCircle } from "lucide-react"; const formSchema = z.object({ apiKeyIdentifier: z.string().min(2, { @@ -43,9 +44,10 @@ const formSchema = z.object({ }) interface TSApiKeys { - name : string, - token : string, - created : string + id_api_key: string; + name : string; + token : string; + created : string; } export default function Page() { @@ -56,21 +58,19 @@ export default function Page() { const {profile} = useProfileStore(); const { data: apiKeys, isLoading, error } = useApiKeys(); - const { mutate } = useApiKeyMutation(); + const { mutate } = useCreateApiKey(); + const columns = useColumns(); useEffect(() => { const temp_tsApiKeys = apiKeys?.map((key) => ({ + id_api_key: key.id_api_key, name: key.name || "", token: key.api_key_hash, created: new Date().toISOString() })) - setTSApiKeys(temp_tsApiKeys) - },[apiKeys]) - - const posthog = usePostHog() if(error){ @@ -96,10 +96,6 @@ export default function Page() { const onSubmit = (values: z.infer) => { - // e.preventDefault(); // Prevent default form submission - console.log("user data is => "+ JSON.stringify(profile)) - console.log("id_user is "+ profile!.id_user) - console.log("idProject is "+ idProject) mutate({ userId: profile!.id_user, projectId: idProject, @@ -112,7 +108,6 @@ export default function Page() { setOpen(!open) - }; return ( @@ -132,19 +127,19 @@ export default function Page() {
- @@ -192,8 +187,10 @@ export default function Page() {
- - + + diff --git a/apps/client-ts/src/app/(Dashboard)/b2c/profile/page.tsx b/apps/client-ts/src/app/(Dashboard)/b2c/profile/page.tsx index 1d73e54ad..e902452a0 100644 --- a/apps/client-ts/src/app/(Dashboard)/b2c/profile/page.tsx +++ b/apps/client-ts/src/app/(Dashboard)/b2c/profile/page.tsx @@ -15,29 +15,37 @@ import Cookies from 'js-cookie'; import useProfileStore from "@/state/profileStore"; import useProjectStore from "@/state/projectStore" import { useQueryClient } from '@tanstack/react-query'; +import { useState } from "react"; const Profile = () => { - + const [copied, setCopied] = useState(false); const { profile, setProfile } = useProfileStore(); const { setIdProject } = useProjectStore(); const queryClient = useQueryClient(); - - const router = useRouter(); + const handleCopy = async (email: string) => { + try { + await navigator.clipboard.writeText(email) + setCopied(true); + setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds + } catch (err) { + console.error('Failed to copy: ', err); + } + }; + const onLogout = () => { router.push('/b2c/login') Cookies.remove("access_token") setProfile(null) setIdProject("") queryClient.clear() - } return ( -
+
Profile @@ -52,13 +60,18 @@ const Profile = () => {
-
-
@@ -68,5 +81,4 @@ const Profile = () => { ); }; - export default Profile; \ No newline at end of file diff --git a/apps/client-ts/src/app/(Dashboard)/configuration/page.tsx b/apps/client-ts/src/app/(Dashboard)/configuration/page.tsx index ebed26bc0..17183a098 100644 --- a/apps/client-ts/src/app/(Dashboard)/configuration/page.tsx +++ b/apps/client-ts/src/app/(Dashboard)/configuration/page.tsx @@ -18,39 +18,36 @@ import { TabsList, TabsTrigger, } from "@/components/ui/tabs" -import { LinkedUsersPage } from "@/components/Configuration/LinkedUsersPage"; +import { LinkedUsersPage } from "@/components/Configuration/LinkedUsers/LinkedUsersPage"; import { Button } from "@/components/ui/button"; import { PlusCircledIcon } from "@radix-ui/react-icons"; -import { FModal } from "@/components/Configuration/FieldMappingModal" +import { FModal } from "@/components/Configuration/FieldMappings/FieldMappingModal" import { Separator } from "@/components/ui/separator"; -import FieldMappingsTable from "@/components/Configuration/FieldMappingsTable"; -import AddLinkedAccount from "@/components/Configuration/AddLinkedAccount"; -import useLinkedUsers from "@/hooks/useLinkedUsers"; -import useFieldMappings from "@/hooks/useFieldMappings"; +import FieldMappingsTable from "@/components/Configuration/FieldMappings/FieldMappingsTable"; +import AddLinkedAccount from "@/components/Configuration/LinkedUsers/AddLinkedAccount"; +import useLinkedUsers from "@/hooks/get/useLinkedUsers"; +import useFieldMappings from "@/hooks/get/useFieldMappings"; import { Skeleton } from "@/components/ui/skeleton"; import { useState } from "react"; -import { LoadingSpinner } from "@/components/Connection/LoadingSpinner"; -import AddWebhook from "@/components/Configuration/AddWebhook"; -import { cn } from "@/lib/utils"; -import { WebhooksPage } from "@/components/Configuration/WebhooksPage"; -import useWebhooks from "@/hooks/useWebhooks"; +import AddWebhook from "@/components/Configuration/Webhooks/AddWebhook"; +import { WebhooksPage } from "@/components/Configuration/Webhooks/WebhooksPage"; +import useWebhooks from "@/hooks/get/useWebhooks"; import { usePostHog } from 'posthog-js/react' import config from "@/lib/config"; import useProjectStore from "@/state/projectStore"; -import AddAuthCredentials from "@/components/Configuration/AddAuthCredentials"; -import AuthCredentialsTable from "@/components/Configuration/AuthCredentialsTable"; -import useConnectionStrategies from "@/hooks/useConnectionStrategies"; +import useConnectionStrategies from "@/hooks/get/useConnectionStrategies"; import { extractAuthMode,extractProvider,extractVertical} from '@panora/shared' import { Heading } from "@/components/ui/heading"; +import CustomConnectorPage from "@/components/Configuration/Connector/CustomConnectorPage"; export default function Page() { const {idProject} = useProjectStore(); const { data: linkedUsers, isLoading, error } = useLinkedUsers(); const { data: webhooks, isLoading: isWebhooksLoading, error: isWebhooksError } = useWebhooks(); - const {data: ConnectionStrategies, isLoading: isConnectionStrategiesLoading,error: isConnectionStategiesError} = useConnectionStrategies() - + const { data: connectionStrategies, isLoading: isConnectionStrategiesLoading, error: isConnectionStategiesError} = useConnectionStrategies() const { data: mappings, isLoading: isFieldMappingsLoading, error: isFieldMappingsError } = useFieldMappings(); + const [open, setOpen] = useState(false); const handleClose = () => { setOpen(false); @@ -78,13 +75,11 @@ export default function Page() { console.log("error fetching webhooks.."); } - if(isConnectionStrategiesLoading) - { + if(isConnectionStrategiesLoading){ console.log("loading Connection Strategies..."); } - if(isConnectionStategiesError) - { + if(isConnectionStategiesError){ console.log("error Fetching connection Strategies!") } @@ -97,20 +92,7 @@ export default function Page() { destination_field: mapping.slug, data_type: mapping.data_type, })) - - // console.log(ConnectionStrategies) - - const mappingConnectionStrategies = ConnectionStrategies?.map(cs => ({ - id_cs : cs.id_connection_strategy, - provider_name : extractProvider(cs.type), - auth_type: extractAuthMode(cs.type), - vertical: extractVertical(cs.type), - type: cs.type, - status: cs.status - })) - - console.log(mappingConnectionStrategies) - + return (
@@ -130,8 +112,8 @@ export default function Page() { Webhooks - - OAuth Credentials + + Manage Connectors @@ -156,19 +138,19 @@ export default function Page() {
- @@ -210,22 +192,8 @@ export default function Page() {
- -
- - - - Your Providers - - Use and setup the credentials of your providers. - - - - - - - -
+ +
diff --git a/apps/client-ts/src/app/(Dashboard)/layout.tsx b/apps/client-ts/src/app/(Dashboard)/layout.tsx index 195d4fc98..84449c320 100644 --- a/apps/client-ts/src/app/(Dashboard)/layout.tsx +++ b/apps/client-ts/src/app/(Dashboard)/layout.tsx @@ -1,13 +1,10 @@ 'use client' -import { Inter } from "next/font/google"; import "./../globals.css"; import { RootLayout } from "@/components/RootLayout"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import Cookies from 'js-cookie'; -import useFetchUserMutation from "@/hooks/mutations/useFetchUserMutation"; - -const inter = Inter({ subsets: ["latin"] }); +import useUser from "@/hooks/get/useUser"; export default function Layout({ children, @@ -17,27 +14,21 @@ export default function Layout({ const [userInitialized,setUserInitialized] = useState(false) const router = useRouter() - const {mutate: fetchUserMutate} = useFetchUserMutation() - - + const { mutate } = useUser() useEffect(() => { - if(!Cookies.get('access_token')) - { - router.replace("/b2c/login") - } - else - { - - fetchUserMutate(Cookies.get('access_token'),{ + if(!Cookies.get('access_token')) { + router.replace("/b2c/login") + }else { + mutate( + Cookies.get('access_token'), + { onError: () => router.replace("/b2c/login"), onSuccess: () => setUserInitialized(true) - }) + } + ) } },[]) - - - return ( <> {userInitialized ? ( diff --git a/apps/client-ts/src/app/b2c/login/page.tsx b/apps/client-ts/src/app/b2c/login/page.tsx index fe12e7715..5784cd977 100644 --- a/apps/client-ts/src/app/b2c/login/page.tsx +++ b/apps/client-ts/src/app/b2c/login/page.tsx @@ -11,17 +11,15 @@ import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import Cookies from 'js-cookie'; import useProfileStore from "@/state/profileStore"; -import useFetchUserMutation from "@/hooks/mutations/useFetchUserMutation"; +import useUser from "@/hooks/get/useUser"; export default function Page() { - const [userInitialized,setUserInitialized] = useState(true) - const {mutate : fetchUserMutate} = useFetchUserMutation() + const {mutate} = useUser() const router = useRouter() const {profile} = useProfileStore(); useEffect(() => { - if(profile) { router.replace('/connections'); @@ -38,7 +36,7 @@ export default function Page() { if(Cookies.get('access_token') && !profile) { - fetchUserMutate(Cookies.get('access_token'),{ + mutate(Cookies.get('access_token'),{ onError: () => setUserInitialized(false) }) } diff --git a/apps/client-ts/src/app/dashboard/page.tsx b/apps/client-ts/src/app/dashboard/page.tsx deleted file mode 100644 index a0a5ef377..000000000 --- a/apps/client-ts/src/app/dashboard/page.tsx +++ /dev/null @@ -1,91 +0,0 @@ -'use client'; - -import { Line, LineChart, ResponsiveContainer} from "recharts"; -import { Overview } from "@/components/Dashboard/overview"; -import { Button } from "@/components/ui/button"; -import { CalendarDateRangePicker } from "@/components/Dashboard/date-range-picker" -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card" -import { - Tabs, - TabsContent, -} from "@/components/ui/tabs" - -export default function Page() { - - const data = [ - { name: 'Jan', revenue: 15000 }, - { name: 'Feb', revenue: 21000 }, - { name: 'Mar', revenue: 18000 }, - { name: 'Apr', revenue: 25000 }, - { name: 'May', revenue: 27000 }, - ]; - - return ( -
-
-
-

Dashboard

-
- - -
-
- - -
- - - Total API Requests - - - - - -
- - - Total Connections - - +39 from last month - - - - - - - - - - - - - - Linked Accounts - - +78% from last month - - - - - - - - - - -
- -
-
-
-
- -
- ); - } diff --git a/apps/client-ts/src/components/ApiKeys/columns.tsx b/apps/client-ts/src/components/ApiKeys/columns.tsx new file mode 100644 index 000000000..cb399e906 --- /dev/null +++ b/apps/client-ts/src/components/ApiKeys/columns.tsx @@ -0,0 +1,100 @@ +"use client" + +import { ColumnDef } from "@tanstack/react-table" +import { Badge } from "@/components/ui/badge" +import { ApiKey } from "./schema" +import { DataTableColumnHeader } from "../shared/data-table-column-header" +import { DataTableRowActions } from "../shared/data-table-row-actions" +import { PasswordInput } from "../ui/password-input" +import { useState } from "react" +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip" +import { Button } from "../ui/button" + +export function useColumns() { + const [copiedState, setCopiedState] = useState<{ [key: string]: boolean }>({}); + + const handleCopy = (token: string) => { + navigator.clipboard.writeText(token); + setCopiedState((prevState) => ({ + ...prevState, + [token]: true, + })); + setTimeout(() => { + setCopiedState((prevState) => ({ + ...prevState, + [token]: false, + })); + }, 2000); // Reset copied state after 2 seconds + }; + + return [ + { + accessorKey: "name", + header: ({ column }) => ( + + ), + cell: ({ row }) =>
{row.getValue("name")}
, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "token", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const token: string = row.getValue("token"); + const copied = copiedState[token] || false; + + return ( +
+
+ +
+
handleCopy(token)} + > + + + + + + +

Copy Key

+
+
+
+ +
+
+ ); + }, + }, + { + accessorKey: "created", + header: ({ column }) => ( + + ), + cell: ({ row }) =>
{row.getValue("created")}
, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)) + }, + }, + { + id: "actions", + cell: ({ row }) => , + }, + ] as ColumnDef[]; +} diff --git a/apps/client-ts/src/components/ApiKeys/data/api-keys.ts b/apps/client-ts/src/components/ApiKeys/data/api-keys.ts deleted file mode 100644 index c31e53e38..000000000 --- a/apps/client-ts/src/components/ApiKeys/data/api-keys.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const API_KEYS = [ - { - "name": "API Key 1", - "token": "sk_test_e74394c2-f063-4693-af5f-84b7281de5e8", - "created": "Jul 31, 2019" - }, - { - "name": "API Key 1", - "token": "sk_test_e74394c2-f063-4693-af5f-84b7281de5e8", - "created": "Jul 31, 2019" - }, - { - "name": "API Key 1", - "token": "sk_test_e74394c2-f063-4693-af5f-84b7281de5e8", - "created": "Jul 31, 2019" - }, - { - "name": "API Key 1", - "token": "sk_test_e74394c2-f063-4693-af5f-84b7281de5e8", - "created": "Jul 31, 2019" - } -] \ No newline at end of file diff --git a/apps/client-ts/src/components/ApiKeys/data/columns.tsx b/apps/client-ts/src/components/ApiKeys/data/columns.tsx deleted file mode 100644 index c33d1577a..000000000 --- a/apps/client-ts/src/components/ApiKeys/data/columns.tsx +++ /dev/null @@ -1,63 +0,0 @@ -"use client" - -import { ColumnDef } from "@tanstack/react-table" -import { Badge } from "@/components/ui/badge" - - -import { ApiKey } from "./schema" -import { DataTableColumnHeader } from "../../shared/data-table-column-header" -import { DataTableRowActions } from "../../shared/data-table-row-actions" - -function insertDots(originalString: string): string { - if(!originalString) return ""; - if (originalString.length <= 50) { - return originalString; - } - return originalString.substring(0, 50 - 3) + '...'; -} - -export const columns: ColumnDef[] = [ - { - accessorKey: "name", - header: ({ column }) => ( - - ), - cell: ({ row }) =>
{row.getValue("name")}
, - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: "token", - header: ({ column }) => ( - - ), - cell: ({ row }) => -
-
- {insertDots(row.getValue("token"))} -
-
navigator.clipboard.writeText(row.getValue("token"))} - > - -
-
- , - - }, - { - accessorKey: "created", - header: ({ column }) => ( - - ), - cell: ({ row }) =>
{row.getValue("created")}
, - filterFn: (row, id, value) => { - return value.includes(row.getValue(id)) - }, - }, - { - id: "actions", - cell: ({ row }) => , - }, -] \ No newline at end of file diff --git a/apps/client-ts/src/components/ApiKeys/data/data.tsx b/apps/client-ts/src/components/ApiKeys/data/data.tsx deleted file mode 100644 index 03743ba86..000000000 --- a/apps/client-ts/src/components/ApiKeys/data/data.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { - ArrowDownIcon, - ArrowRightIcon, - ArrowUpIcon, - CheckCircledIcon, - CircleIcon, - CrossCircledIcon, - QuestionMarkCircledIcon, - StopwatchIcon, - } from "@radix-ui/react-icons" - - export const labels = [ - { - value: "bug", - label: "Bug", - }, - { - value: "feature", - label: "Feature", - }, - { - value: "documentation", - label: "Documentation", - }, - ] - - export const statuses = [ - { - value: "backlog", - label: "Backlog", - icon: QuestionMarkCircledIcon, - }, - { - value: "todo", - label: "Todo", - icon: CircleIcon, - }, - { - value: "in progress", - label: "In Progress", - icon: StopwatchIcon, - }, - { - value: "done", - label: "Done", - icon: CheckCircledIcon, - }, - { - value: "canceled", - label: "Canceled", - icon: CrossCircledIcon, - }, - ] - - export const priorities = [ - { - label: "Low", - value: "low", - icon: ArrowDownIcon, - }, - { - label: "Medium", - value: "medium", - icon: ArrowRightIcon, - }, - { - label: "High", - value: "high", - icon: ArrowUpIcon, - }, - ] \ No newline at end of file diff --git a/apps/client-ts/src/components/ApiKeys/data/seed.ts b/apps/client-ts/src/components/ApiKeys/data/seed.ts deleted file mode 100644 index b35a5ef17..000000000 --- a/apps/client-ts/src/components/ApiKeys/data/seed.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from "fs" -import path from "path" -import { faker } from "@faker-js/faker" - -import { labels, priorities, statuses } from "./data" - -const tasks = Array.from({ length: 100 }, () => ({ - id: `TASK-${faker.number.int({ min: 1000, max: 9999 })}`, - title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()), - status: faker.helpers.arrayElement(statuses).value, - label: faker.helpers.arrayElement(labels).value, - direction: faker.helpers.arrayElement(priorities).value, -})) - -fs.writeFileSync( - path.join(__dirname, "tasks.json"), - JSON.stringify(tasks, null, 2) -) - -console.log("✅ Tasks data generated.") \ No newline at end of file diff --git a/apps/client-ts/src/components/ApiKeys/data/schema.ts b/apps/client-ts/src/components/ApiKeys/schema.ts similarity index 62% rename from apps/client-ts/src/components/ApiKeys/data/schema.ts rename to apps/client-ts/src/components/ApiKeys/schema.ts index eb8015db0..b7fcd2442 100644 --- a/apps/client-ts/src/components/ApiKeys/data/schema.ts +++ b/apps/client-ts/src/components/ApiKeys/schema.ts @@ -1,8 +1,7 @@ import { z } from "zod" -// We're keeping a simple non-relational schema here. -// IRL, you will have a schema for your data models. export const apiKeySchema = z.object({ + id_api_key: z.string(), name: z.string(), token: z.string(), created: z.string(), diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx index 314114c9b..c8705d92a 100644 --- a/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx +++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/CreateUserForm.tsx @@ -15,7 +15,6 @@ import * as z from "zod" import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, @@ -24,7 +23,7 @@ import { import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import { PasswordInput } from '@/components/ui/password-input' -import useCreateUserMutation from '@/hooks/mutations/useCreateUserMutation' +import useCreateUser from '@/hooks/create/useCreateUser' const formSchema = z.object({ first_name: z.string().min(2,{ @@ -44,7 +43,7 @@ const formSchema = z.object({ const CreateUserForm = () => { - const {mutate : createUserMutate} = useCreateUserMutation(); + const {mutate : createUserMutate} = useCreateUser(); const form = useForm>({ resolver: zodResolver(formSchema), @@ -153,7 +152,7 @@ const CreateUserForm = () => {
- + diff --git a/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx index 5726493bc..076257337 100644 --- a/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx +++ b/apps/client-ts/src/components/Auth/CustomLoginComponent/LoginUserForm.tsx @@ -10,13 +10,11 @@ import { } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" -import { Label } from "@/components/ui/label" import { PasswordInput } from '@/components/ui/password-input' import * as z from "zod" import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, @@ -24,7 +22,7 @@ import { } from "@/components/ui/form" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" -import useLoginMutation from '@/hooks/mutations/useLoginMutation' +import useCreateLogin from '@/hooks/create/useCreateLogin' import { useRouter } from "next/navigation"; const formSchema = z.object({ @@ -40,7 +38,7 @@ const LoginUserForm = () => { const router = useRouter() - const {mutate : loginMutate} = useLoginMutation() + const {mutate : loginMutate} = useCreateLogin() const form = useForm>({ resolver: zodResolver(formSchema), @@ -51,7 +49,6 @@ const LoginUserForm = () => { }) const onSubmit = (values: z.infer) => { - loginMutate({ email:values.email, password_hash:values.password diff --git a/apps/client-ts/src/components/Configuration/AddAuthCredentials.tsx b/apps/client-ts/src/components/Configuration/AddAuthCredentials.tsx deleted file mode 100644 index 572aa41c2..000000000 --- a/apps/client-ts/src/components/Configuration/AddAuthCredentials.tsx +++ /dev/null @@ -1,57 +0,0 @@ -'use client' - -import { Button } from "@/components/ui/button" -import { - Dialog, - DialogContent, - DialogTrigger, -} from "@/components/ui/dialog" - -import { PlusCircledIcon } from "@radix-ui/react-icons" -import { useState } from "react" -import useProjectStore from "@/state/projectStore" - -import { cn } from "@/lib/utils" - -import { usePostHog } from 'posthog-js/react' -import config from "@/lib/config" - -import AddAuthCredentialsForm from "./AddAuthCredentialsForm" - -const AddAuthCredentials = () => { - const [open, setOpen] = useState(false); - const posthog = usePostHog() - const {idProject} = useProjectStore(); - - const handleOpenChange = (open: boolean) => { - setOpen(open) - // form.reset() - }; - - return ( - - - - - - setOpen(false)} /> - - - ) -} - -export default AddAuthCredentials; \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/AddAuthCredentialsForm.tsx b/apps/client-ts/src/components/Configuration/AddAuthCredentialsForm.tsx deleted file mode 100644 index 51355a356..000000000 --- a/apps/client-ts/src/components/Configuration/AddAuthCredentialsForm.tsx +++ /dev/null @@ -1,618 +0,0 @@ -import React,{useEffect, useState} from 'react' -import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons" -import {ScrollArea} from '@/components/ui/scrollbar' -import { Input } from "@/components/ui/input" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import { Button } from "@/components/ui/button" -import config from "@/lib/config" -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - } from "@/components/ui/command" -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, - } from "@/components/ui/select" -import { - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" -import { - Popover, - PopoverContent, - PopoverTrigger, - } from "@/components/ui/popover" -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" -import {PasswordInput} from '@/components/ui/password-input' -import { ALL_PROVIDERS,getLogoURL,providerToType,AuthStrategy } from "@panora/shared" -import * as z from "zod" -import { cn } from "@/lib/utils" -import useProjectStore from "@/state/projectStore" -import useConnectionStrategyMutation from '@/hooks/mutations/useConnectionStrategy' -import { usePostHog } from 'posthog-js/react' -import useConnectionStrategyAuthCredentialsMutation from '@/hooks/mutations/useConnectionStrategyAuthCredentials' -import useUpdateConnectionStrategyMutation from '@/hooks/mutations/useUpdateConnectionStrategy' - - - -const formSchema = z.object({ - provider_name: z.string().min(2, { - message: "Provider should be selected.", - }), - auth_type : z.string().min(2, { - message: "Authentication type should be selected.", - }), - client_id : z.string({ - required_error: "Please Enter a Client ID", - }), - client_secret : z.string({ - required_error: "Please Enter a Client Secret", - }), - scope : z.string({ - required_error: "Please Enter a scope", - }), - api_key: z.string({ - required_error: "Please Enter a API Key", - }), - username: z.string({ - required_error: "Please Enter Username", - }), - secret: z.string({ - required_error: "Please Enter Secret", - }), - -}) - - interface propType { - performUpdate: boolean, - closeDialog?: () => void, - data?:{ - provider_name: string, - auth_type: string, - status: boolean, - id_cs: string, - vertical: string, - type: string, - // client_id?:string, - // client_secret?:string, - // scope?:string, - // api_key?:string, - // username?:string, - // secret?:string, - } - - } - -const AddAuthCredentialsForm = (prop : propType) => { - const [copied, setCopied] = useState(false); - const [popoverOpen,setPopOverOpen] = useState(false); - const {idProject} = useProjectStore() - const {mutate : createCS} = useConnectionStrategyMutation(); - const {mutate :updateCS} = useUpdateConnectionStrategyMutation() - const {mutateAsync : fetchCredentials,data : fetchedData} = useConnectionStrategyAuthCredentialsMutation(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - provider_name: prop.data?.provider_name? `${prop.data?.provider_name.toLowerCase()}-${prop.data?.vertical.toUpperCase()}` : "", - auth_type: prop.data?.auth_type? prop.data?.auth_type : "", - client_id:"", - client_secret:"", - scope:"", - api_key:"", - username:"", - secret:"", - }, - - }) - - const posthog = usePostHog() - - useEffect(() => { - - if(prop.performUpdate) - { - - console.log(prop.data) - - fetchCredentials({ - projectId:idProject, - type: prop.data?.type, - attributes: prop.data?.auth_type===AuthStrategy.oauth2 ? ["client_id","client_secret","scope"] - : prop.data?.auth_type===AuthStrategy.api_key ? ["api_key"] : ["username","secret"] - }, - { - onSuccess(data, variables, context) { - - if(prop.data?.auth_type===AuthStrategy.oauth2) - { - form.setValue("client_id",data[0]) - form.setValue("client_secret",data[1]) - form.setValue("scope",data[2]) - - - } - else if(prop.data?.auth_type===AuthStrategy.api_key) - { - form.setValue("api_key",data[0]) - - } - else - { - form.setValue("username",data[0]) - form.setValue("secret",data[0]) - - } - - }, - } - ) - } - -},[]) - - const handlePopOverClose = () => { - setPopOverOpen(false); - } - - const handleCopy = async () => { - try { - await navigator.clipboard.writeText("localhost:3000/connections/oauth/callback") - setCopied(true); - setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds - } catch (err) { - console.error('Failed to copy: ', err); - } - }; - - - - const Watch = form.watch() - - function onSubmit(values: z.infer) { - - - const {client_id,client_secret,scope,provider_name,api_key,auth_type,secret,username} = values - - - switch(values.auth_type) - { - case AuthStrategy.oauth2: - if(client_id==="" || client_secret==="" || scope==="") - { - if(client_id==="") - { - form.setError("client_id",{"message":"Please Enter Client ID"}) - } - if(client_secret==="") - { - form.setError("client_secret",{"message":"Please Enter Client Secret"}) - } - if(scope==="") - { - form.setError("scope",{"message":"Please Enter the scope"}) - } - break; - } - if(prop.performUpdate) - { - updateCS({ - id_cs:prop.data?.id_cs, - ToUpdateToggle: false, - status:prop.data?.status, - attributes:["client_id","client_secret","scope"], - values:[client_id,client_secret,scope] - }) - posthog?.capture("Connection_strategy_OAuth2_updated", { - id_project: idProject, - mode: config.DISTRIBUTION - }); - } - else - { - createCS({ - projectId:idProject, - type: providerToType(provider_name.split("-")[0],provider_name.split("-")[1],AuthStrategy.oauth2), - attributes:["client_id","client_secret","scope"], - values:[client_id,client_secret,scope] - }); - posthog?.capture("Connection_strategy_OAuth2_created", { - id_project: idProject, - mode: config.DISTRIBUTION - }); - } - - form.reset(); - console.log(values) - if(prop.closeDialog!=undefined) - { - prop.closeDialog() - } - break; - - case AuthStrategy.api_key: - if(values.api_key==="") - { - form.setError("api_key",{"message":"Please Enter API Key"}); - break; - } - if(prop.performUpdate) - { - updateCS({ - id_cs:prop.data?.id_cs, - ToUpdateToggle: false, - status:prop.data?.status, - attributes:["api_key"], - values:[api_key] - }) - posthog?.capture("Connection_strategy_API_KEY_updated", { - id_project: idProject, - mode: config.DISTRIBUTION - }); - } - else - { - createCS({ - projectId:idProject, - type: providerToType(provider_name.split("-")[0],provider_name.split("-")[1],AuthStrategy.api_key), - attributes:["api_key"], - values:[api_key] - }); - posthog?.capture("Connection_strategy_API_KEY_created", { - id_project: idProject, - mode: config.DISTRIBUTION - }); - } - - form.reset(); - console.log(values) - if(prop.closeDialog!=undefined) - { - prop.closeDialog() - } - break; - - case AuthStrategy.basic: - if(values.username==="" || values.secret==="") - { - if(values.username==="") - { - form.setError("username",{"message":"Please Enter Username"}) - } - if(values.secret==="") - { - form.setError("secret",{"message":"Please Enter Secret"}) - } - break; - - } - if(prop.performUpdate) - { - updateCS({ - id_cs:prop.data?.id_cs, - ToUpdateToggle: false, - status:prop.data?.status, - attributes:["username","secret"], - values:[username,secret] - }) - posthog?.capture("Connection_strategy_BASIC_AUTH_updated", { - id_project: idProject, - mode: config.DISTRIBUTION - }); - } - else - { - createCS({ - projectId:idProject, - type: providerToType(provider_name.split("-")[0],provider_name.split("-")[1],AuthStrategy.basic), - attributes:["username","secret"], - values:[username,secret] - }); - posthog?.capture("Connection_strategy_BASIC_AUTH_created", { - id_project: idProject, - mode: config.DISTRIBUTION - }); - } - - form.reset(); - console.log(values) - if(prop.closeDialog!=undefined) - { - prop.closeDialog() - } - break; - } - - - } - - - - - return ( - <> -
- - - - Add OAuth Credentials - - In the event that you are using your own OAuth credentials, Panora gives you have the option to import them instead of using our pre-made OAuth apps. - - - -
- ( - - Provider - - - - - - - - - - No Provider found. - - - - {ALL_PROVIDERS.map((provider) => ( - { - form.setValue("provider_name", `${provider.value}-${provider.vertical}`) - form.clearErrors("provider_name") - handlePopOverClose(); - }} - className={field.value===`${provider.value}-${provider.vertical}` ? "bg-gray-200 w-full" : "w-full"} - - > -
handleWalletClick(provider.name)} - > -
- {provider.value} - {provider.value.charAt(0).toUpperCase() + provider.value.slice(1)} - {provider.vertical} - -
- {/* */} - - -
- -
- ))} -
-
-
-
-
- -
- )} - /> - - {/*
*/} -
-
- ( - - Authentication Method - - - - - - )} - /> - -
- - {/* If Authentication Method is OAuth2 */} - - {Watch.auth_type===AuthStrategy.oauth2 ? - <> -
- ( - - Client ID - - - - - - )} - /> -
-
- ( - - Client Secret - - - - - - )} - /> -
-
- ( - - Scope - - - - - - )} - /> -
-
- Redirect URI -
- {/*

localhost:3000/connections/oauth/callback

*/} - - - - - - - -
- -
- - : - <>} - - {Watch.auth_type===AuthStrategy.api_key ? - <> -
- ( - - API Key - - - - - - )} - /> -
- - : - <>} - - {Watch.auth_type===AuthStrategy.basic ? - <> -
- ( - - Username - - - - - - )} - /> -
-
- ( - - Secret - - - - - - )} - /> -
- - : - <>} - - - - - - - - ) -} - -export default AddAuthCredentialsForm \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/AddWebhook.tsx b/apps/client-ts/src/components/Configuration/AddWebhook.tsx deleted file mode 100644 index 9737bd9b0..000000000 --- a/apps/client-ts/src/components/Configuration/AddWebhook.tsx +++ /dev/null @@ -1,206 +0,0 @@ -'use client' - -import { Button } from "@/components/ui/button" -import { - Dialog, - DialogContent, - - DialogTrigger, -} from "@/components/ui/dialog" -import { Input } from "@/components/ui/input" -import { PlusCircledIcon } from "@radix-ui/react-icons" -import { useState } from "react" -import useProjectStore from "@/state/projectStore" -import useWebhookMutation from "@/hooks/mutations/useWebhookMutation" -import { - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { scopes } from "@panora/shared/src/webhookScopes" -import { cn } from "@/lib/utils" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -import * as z from "zod" -import { usePostHog } from 'posthog-js/react' -import config from "@/lib/config" - - -const formSchema = z.object({ - description: z.string().min(2, { - message: "description must be at least 2 characters.", - }), - url: z.string().min(2, { - message: "url must be at least 2 characters.", - }), - event: z.string(), -}) - -const AddWebhook = () => { - const [open, setOpen] = useState(false); - - const posthog = usePostHog() - - const {idProject} = useProjectStore(); - - const { mutate } = useWebhookMutation(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - description: "", - url: "", - event: scopes[0] - }, - }) - - const handleOpenChange = (openVal : boolean) => { - setOpen(openVal); - form.reset(); - }; - - function onSubmit(values: z.infer) { - console.log(values) - mutate({ - url: values.url, - description: values.description, - id_project: idProject, - scope: [values.event], - }); - handleOpenChange(false); - posthog?.capture("webhook_created", { - id_project: idProject, - mode: config.DISTRIBUTION - }) - } - - return ( - - - - - -
- - - - Create a webhook - - Set up your webhook endpoint to receive live events from Panora. - - - -
-
- ( - - Event - - - - - - )} - /> -
-
-
- ( - - Endpoint URL - - - - - - - - )} - /> -
-
- ( - - Description - - - - - - )} - /> -
-
- - - -
- -
-
- ) -} - -export default AddWebhook; \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/AuthCredentialsTable.tsx b/apps/client-ts/src/components/Configuration/AuthCredentialsTable.tsx deleted file mode 100644 index 248814990..000000000 --- a/apps/client-ts/src/components/Configuration/AuthCredentialsTable.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { - Card, - CardContent, - CardHeader, - CardTitle, - } from "@/components/ui/card" -import { DataTable } from "@/components/shared/data-table"; -import { authColumns } from "./authColumns"; -import {AuthStrategy} from '@panora/shared' -import { DataTableLoading } from "@/components/shared/data-table-loading"; -import { Suspense } from "react"; - -export interface authCredentialsMapping { - id_cs : string, - provider_name : string, - auth_type: AuthStrategy, - vertical: string, - type: string, - status: boolean, -} - -export default function AuthCredentialsTable({ - mappings, - isLoading -}: { mappings: authCredentialsMapping[] | undefined; isLoading: boolean }) { - - // const countDefined = mappings?.filter(mapping => mapping.status === "defined").length; - // const countMapped = mappings?.filter(mapping => mapping.status === "mapped").length; - if(isLoading){ - return ; - } - return ( - <> -
- - {mappings && } - -
- - ) -} \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx b/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx new file mode 100644 index 000000000..440497963 --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Connector/ConnectorDisplay.tsx @@ -0,0 +1,403 @@ +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { Button } from "@/components/ui/button" +import { Label } from "@/components/ui/label" +import { Separator } from "@/components/ui/separator" +import { Switch } from "@/components/ui/switch" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import { PasswordInput } from "@/components/ui/password-input" +import { z } from "zod" +import config from "@/lib/config" +import { AuthStrategy, providerToType, Provider, extractProvider, extractVertical } from "@panora/shared" +import { useEffect, useState } from "react" +import useProjectStore from "@/state/projectStore" +import { usePostHog } from 'posthog-js/react' +import { Input } from "@/components/ui/input" +import useConnectionStrategies from "@/hooks/get/useConnectionStrategies" +import { DataTableFacetedFilter } from "@/components/shared/data-table-faceted-filter" +import useCreateConnectionStrategy from "@/hooks/create/useCreateConnectionStrategy" +import useUpdateConnectionStrategy from "@/hooks/update/useUpdateConnectionStrategy" +import useConnectionStrategyAuthCredentials from "@/hooks/get/useConnectionStrategyAuthCredentials" + +interface ItemDisplayProps { + item?: Provider +} + +const formSchema = z.object({ + client_id : z.string({ + required_error: "Please Enter a Client ID", + }), + client_secret : z.string({ + required_error: "Please Enter a Client Secret", + }), + scope : z.string({ + required_error: "Please Enter a scope", + }), + api_key: z.string({ + required_error: "Please Enter a API Key", + }), + username: z.string({ + required_error: "Please Enter Username", + }), + secret: z.string({ + required_error: "Please Enter Secret", + }), +}) + +export function ConnectorDisplay({ item }: ItemDisplayProps) { + const [copied, setCopied] = useState(false); + const [switchEnabled, setSwitchEnabled] = useState(false); + const { idProject } = useProjectStore() + const { data: connectionStrategies, isLoading: isConnectionStrategiesLoading, error: isConnectionStategiesError } = useConnectionStrategies() + const { mutate: createCS } = useCreateConnectionStrategy(); + const { mutate: updateCS } = useUpdateConnectionStrategy() + const { mutateAsync: fetchCredentials, data: fetchedData } = useConnectionStrategyAuthCredentials(); + + const posthog = usePostHog() + + const mappingConnectionStrategies = connectionStrategies?.filter((cs) => extractVertical(cs.type).toLowerCase() == item?.vertical && extractProvider(cs.type).toLowerCase() == item?.name) + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + client_id: "", + client_secret: "", + scope: "", + api_key: "", + username: "", + secret: "", + }, + }) + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(`${config.API_URL}/connections/oauth/callback`) + setCopied(true); + setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds + } catch (err) { + console.error('Failed to copy: ', err); + } + }; + + function onSubmit(values: z.infer) { + const { client_id, client_secret, scope, api_key, secret, username } = values; + const performUpdate = mappingConnectionStrategies && mappingConnectionStrategies.length > 0; + switch (item?.authStrategy) { + case AuthStrategy.oauth2: + if (client_id === "" || client_secret === "" || scope === "") { + if (client_id === "") { + form.setError("client_id", { "message": "Please Enter Client ID" }); + } + if (client_secret === "") { + form.setError("client_secret", { "message": "Please Enter Client Secret" }); + } + if (scope === "") { + form.setError("scope", { "message": "Please Enter the scope" }); + } + break; + } + if (performUpdate) { + const dataToUpdate = mappingConnectionStrategies[0]; + updateCS({ + id_cs: dataToUpdate.id_connection_strategy, + updateToggle: false, + status: dataToUpdate.status, + attributes: ["client_id", "client_secret", "scope"], + values: [client_id, client_secret, scope] + }) + posthog?.capture("Connection_strategy_OAuth2_updated", { + id_project: idProject, + mode: config.DISTRIBUTION + }); + } else { + createCS({ + type: providerToType(item?.name, item?.vertical!, AuthStrategy.oauth2), + attributes: ["client_id", "client_secret", "scope"], + values: [client_id, client_secret, scope] + }); + posthog?.capture("Connection_strategy_OAuth2_created", { + id_project: idProject, + mode: config.DISTRIBUTION + }); + } + form.reset(); + console.log(values) + break; + + case AuthStrategy.api_key: + if (values.api_key === "") { + form.setError("api_key", { "message": "Please Enter API Key" }); + break; + } + if (performUpdate) { + const dataToUpdate = mappingConnectionStrategies[0]; + updateCS({ + id_cs: dataToUpdate.id_connection_strategy, + updateToggle: false, + status: dataToUpdate.status, + attributes: ["api_key"], + values: [api_key] + }) + posthog?.capture("Connection_strategy_API_KEY_updated", { + id_project: idProject, + mode: config.DISTRIBUTION + }); + } else { + createCS({ + type: providerToType(item?.name, item?.vertical!, AuthStrategy.api_key), + attributes: ["api_key"], + values: [api_key] + }); + posthog?.capture("Connection_strategy_API_KEY_created", { + id_project: idProject, + mode: config.DISTRIBUTION + }); + } + form.reset(); + console.log(values) + break; + + case AuthStrategy.basic: + if (values.username === "" || values.secret === "") { + if (values.username === "") { + form.setError("username", { "message": "Please Enter Username" }); + } + if (values.secret === "") { + form.setError("secret", { "message": "Please Enter Secret" }); + } + break; + } + if (performUpdate) { + const dataToUpdate = mappingConnectionStrategies[0]; + updateCS({ + id_cs: dataToUpdate.id_connection_strategy, + updateToggle: false, + status: dataToUpdate.status, + attributes: ["username", "secret"], + values: [username, secret] + }) + posthog?.capture("Connection_strategy_BASIC_AUTH_updated", { + id_project: idProject, + mode: config.DISTRIBUTION + }); + } else { + createCS({ + type: providerToType(item?.name, item?.vertical!, AuthStrategy.basic), + attributes: ["username", "secret"], + values: [username, secret] + }); + posthog?.capture("Connection_strategy_BASIC_AUTH_created", { + id_project: idProject, + mode: config.DISTRIBUTION + }); + } + form.reset(); + console.log(values) + break; + } + } + + useEffect(() => { + if (mappingConnectionStrategies && mappingConnectionStrategies.length > 0) { + fetchCredentials({ + type: mappingConnectionStrategies[0].type, + attributes: item?.authStrategy === AuthStrategy.oauth2 ? ["client_id", "client_secret", "scope"] + : item?.authStrategy === AuthStrategy.api_key ? ["api_key"] : ["username", "secret"] + }, { + onSuccess(data) { + if (item?.authStrategy === AuthStrategy.oauth2) { + form.setValue("client_id", data[0]); + form.setValue("client_secret", data[1]); + form.setValue("scope", data[2]); + } + if (item?.authStrategy === AuthStrategy.api_key) { + form.setValue("api_key", data[0]); + } + if (item?.authStrategy === AuthStrategy.basic) { + form.setValue("username", data[0]); + form.setValue("secret", data[1]); + } + setSwitchEnabled(mappingConnectionStrategies[0].status === true); + } + }); + } else { + form.reset(); + setSwitchEnabled(false); + } + }, [connectionStrategies, item]); + + const handleSwitchChange = (enabled: boolean) => { + if (mappingConnectionStrategies && mappingConnectionStrategies.length > 0) { + const dataToUpdate = mappingConnectionStrategies[0]; + updateCS({ + id_cs: dataToUpdate.id_connection_strategy, + updateToggle: true + }); + setSwitchEnabled(enabled); + } + }; + + return ( +
+ + {item ? ( +
+
+
+ +
+
{`${item.name.substring(0, 1).toUpperCase()}${item.name.substring(1)}`}
+
{item.description}
+ {mappingConnectionStrategies && mappingConnectionStrategies.length > 0 && ( +
+ +
+ )} +
+
+
+ +
+
+ + { item.authStrategy == AuthStrategy.oauth2 && + <> +
+ ( + + Client ID + + + + + + )} + /> +
+
+ ( + + Client Secret + + + + + + )} + /> +
+
+ ( + + Scopes + + + + + + )} + /> +
+
+ Redirect URI +
+ + +
+
+ + } + { + item.authStrategy == AuthStrategy.api_key && + <> +
+ ( + + API Key + + + + + + )} + /> +
+ + } + { + item.authStrategy == AuthStrategy.basic && + <> +
+ ( + + Username + + + + + + )} + /> +
+
+ ( + + Secret + + + + + + )} + /> +
+ + } + +
+ +
+ +
+ ) : ( +
+ No connector selected +
+ )} +
+ ) +} diff --git a/apps/client-ts/src/components/Configuration/Connector/ConnectorLayout.tsx b/apps/client-ts/src/components/Configuration/Connector/ConnectorLayout.tsx new file mode 100644 index 000000000..50dd7a475 --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Connector/ConnectorLayout.tsx @@ -0,0 +1,74 @@ +"use client" + +import * as React from "react" +import { Search } from "lucide-react" +import { Input } from "@/components/ui/input" +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable" +import { Separator } from "@/components/ui/separator" +import { Tabs } from "@/components/ui/tabs" +import { TooltipProvider } from "@/components/ui/tooltip" +import { ConnectorDisplay } from "./ConnectorDisplay" +import { ConnectorList } from "./ConnectorList" +import { VerticalSelector } from "./VerticalSelector" +import { Provider } from "@panora/shared" +import { useConnector } from "./useConnector" + +interface Props { + connectors: Provider[] + defaultLayout: number[] | undefined + onSearchChange: (query: string) => void +} + +export function ConnectorLayout({ + connectors, + defaultLayout = [265, 440, 655], + onSearchChange +}: Props) { + const [connector] = useConnector() + const [selectedVertical, setSelectedVertical] = React.useState("") + + const handleSearchChange = (event: React.ChangeEvent) => { + onSearchChange(event.target.value) + } + + return ( + + { + document.cookie = `react-resizable-panels:layout=${JSON.stringify( + sizes + )}` + }} + className="h-full max-h-[800px] items-stretch" + > + + +
+

Connectors

+

By default, all connectors use Panora managed credentials. You are free to edit them by creating your custom developer apps inside your favorite softwares !

+
+ +
+
+
+ + + +
+
+
+ +
+
+ + + `${item.vertical}-${item.name}` === connector.selected)} + /> + + +
+
+ ) +} diff --git a/apps/client-ts/src/components/Configuration/Connector/ConnectorList.tsx b/apps/client-ts/src/components/Configuration/Connector/ConnectorList.tsx new file mode 100644 index 000000000..8912954ce --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Connector/ConnectorList.tsx @@ -0,0 +1,79 @@ +import { ComponentProps } from "react" +import { cn } from "@/lib/utils" +import { Badge } from "@/components/ui/badge" +import { ScrollArea } from "@/components/ui/scroll-area" +import { useConnector } from "./useConnector" +import { AuthStrategy, Provider } from "@panora/shared" + +interface ConnectorListProps { + items: Provider[] +} + +export function ConnectorList({ items }: ConnectorListProps) { + const [connector, setConnector] = useConnector() + + return ( + +
+ {items.map((item) => ( + + ))} +
+
+ ) +} + +function getBadgeVariantFromLabel( + label?: string +): ComponentProps["variant"] { + if (label === AuthStrategy.oauth2) { + return "secondary" + } + return "default" +} diff --git a/apps/client-ts/src/components/Configuration/Connector/CustomConnectorPage.tsx b/apps/client-ts/src/components/Configuration/Connector/CustomConnectorPage.tsx new file mode 100644 index 000000000..74f81e840 --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Connector/CustomConnectorPage.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import useVerticalStore from "@/state/verticalStore"; +import { ConnectorLayout } from "./ConnectorLayout" +import { providersArray, Provider } from "@panora/shared" + +export default function CustomConnectorPage() { + const { vertical } = useVerticalStore(); + const [searchQuery, setSearchQuery] = React.useState("") + const defaultLayout = undefined + + const handleSearchChange = (query: string) => { + setSearchQuery(query) + } + + const filteredConnectors = vertical === "" + ? providersArray() + : providersArray(vertical); + + const connectors = filteredConnectors.filter((connector: Provider) => { + const matchesSearch = connector.name.toLowerCase().includes(searchQuery.toLowerCase()) || + connector.description?.toLowerCase().includes(searchQuery.toLowerCase()) + return matchesSearch + }) + + return ( + <> +
+ +
+ + ) +} diff --git a/apps/client-ts/src/components/Configuration/Connector/VerticalSelector.tsx b/apps/client-ts/src/components/Configuration/Connector/VerticalSelector.tsx new file mode 100644 index 000000000..9745d639a --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Connector/VerticalSelector.tsx @@ -0,0 +1,74 @@ +"use client" + +import * as React from "react" +import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import useVerticalStore from "@/state/verticalStore" +import { categoriesVerticals } from '@panora/shared'; + +export const verticals = categoriesVerticals as string[]; + +export function VerticalSelector({ onSelectVertical }: { onSelectVertical: (vertical: string) => void }) { + const [open, setOpen] = React.useState(false) + const [selectedVertical, setSelectedVertical] = React.useState("") + const { setVertical } = useVerticalStore(); + + return ( + + + + + + + + No verticals found. + + {verticals.map((vertical) => ( + { + setVertical(vertical) + setSelectedVertical(vertical) + setOpen(false) + onSelectVertical(vertical) + }} + > + {vertical} + + + ))} + + + + + ) +} diff --git a/apps/client-ts/src/components/Configuration/Connector/useConnector.tsx b/apps/client-ts/src/components/Configuration/Connector/useConnector.tsx new file mode 100644 index 000000000..cda095cae --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Connector/useConnector.tsx @@ -0,0 +1,15 @@ +import { atom, useAtom } from "jotai" +import { providersArray } from "@panora/shared" + + +type Config = { + selected: string +} + +const configAtom = atom({ + selected: `crm-${providersArray("crm")[0].name}`, +}) + +export function useConnector() { + return useAtom(configAtom) +} \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/FieldMappingModal.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/FieldMappingModal.tsx similarity index 97% rename from apps/client-ts/src/components/Configuration/FieldMappingModal.tsx rename to apps/client-ts/src/components/Configuration/FieldMappings/FieldMappingModal.tsx index bdbfca3ad..661a6aa3e 100644 --- a/apps/client-ts/src/components/Configuration/FieldMappingModal.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/FieldMappingModal.tsx @@ -34,20 +34,20 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select" -import useDefineFieldMutation from "@/hooks/mutations/useDefineFieldMutation" -import useMapFieldMutation from "@/hooks/mutations/useMapFieldMutation" +import useMapField from "@/hooks/create/useMapField" import { useEffect, useState } from "react" -import useFieldMappings from "@/hooks/useFieldMappings" -import useProviderProperties from "@/hooks/useProviderProperties" +import useFieldMappings from "@/hooks/get/useFieldMappings" +import useProviderProperties from "@/hooks/get/useProviderProperties" import { standardObjects } from "@panora/shared/src/standardObjects" import useProjectStore from "@/state/projectStore" -import useLinkedUsers from "@/hooks/useLinkedUsers" +import useLinkedUsers from "@/hooks/get/useLinkedUsers" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import * as z from "zod" import { usePostHog } from 'posthog-js/react' import config from "@/lib/config" import { CRM_PROVIDERS } from "@panora/shared" +import useDefineField from "@/hooks/create/useDefineField" const defineFormSchema = z.object({ @@ -108,8 +108,8 @@ export function FModal({ onClose }: {onClose: () => void}) { const {idProject} = useProjectStore(); const { data: mappings } = useFieldMappings(); - const { mutate: mutateDefineField } = useDefineFieldMutation(); - const { mutate: mutateMapField } = useMapFieldMutation(); + const { mutate: mutateDefineField } = useDefineField(); + const { mutate: mutateMapField } = useMapField(); const { data: linkedUsers } = useLinkedUsers(); // TODO: HANDLE VERTICAL AND PROVIDERS FOR CUSTOM MAPPINGS const { data: sourceCustomFields, error, isLoading } = useProviderProperties(linkedUserId,sourceProvider, "crm"); diff --git a/apps/client-ts/src/components/Configuration/FieldMappingsTable.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/FieldMappingsTable.tsx similarity index 100% rename from apps/client-ts/src/components/Configuration/FieldMappingsTable.tsx rename to apps/client-ts/src/components/Configuration/FieldMappings/FieldMappingsTable.tsx diff --git a/apps/client-ts/src/components/Configuration/columns.tsx b/apps/client-ts/src/components/Configuration/FieldMappings/columns.tsx similarity index 92% rename from apps/client-ts/src/components/Configuration/columns.tsx rename to apps/client-ts/src/components/Configuration/FieldMappings/columns.tsx index 0034a183f..a2b432687 100644 --- a/apps/client-ts/src/components/Configuration/columns.tsx +++ b/apps/client-ts/src/components/Configuration/FieldMappings/columns.tsx @@ -5,9 +5,9 @@ import { ColumnDef } from "@tanstack/react-table" import { Badge } from "@/components/ui/badge" import { Checkbox } from "@/components/ui/checkbox" -import { DataTableColumnHeader } from "../shared/data-table-column-header" -import { DataTableRowActions } from "../shared/data-table-row-actions" -import { Mapping } from "./data/schema" +import { DataTableColumnHeader } from "../../shared/data-table-column-header" +import { DataTableRowActions } from "../../shared/data-table-row-actions" +import { Mapping } from "./schema" export const columns: ColumnDef[] = [ { @@ -141,6 +141,6 @@ export const columns: ColumnDef[] = [ }, { id: "actions", - cell: ({ row }) => , + cell: ({ row }) => , }, ] \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/data/schema.ts b/apps/client-ts/src/components/Configuration/FieldMappings/schema.ts similarity index 61% rename from apps/client-ts/src/components/Configuration/data/schema.ts rename to apps/client-ts/src/components/Configuration/FieldMappings/schema.ts index ff760c476..c8baec250 100644 --- a/apps/client-ts/src/components/Configuration/data/schema.ts +++ b/apps/client-ts/src/components/Configuration/FieldMappings/schema.ts @@ -1,7 +1,5 @@ import { z } from "zod" -// We're keeping a simple non-relational schema here. -// IRL, you will have a schema for your data models. export const mappingSchema = z.object({ standard_object: z.string(), source_app: z.string(), @@ -12,4 +10,4 @@ export const mappingSchema = z.object({ data_type: z.string() }) -export type Mapping = z.infer \ No newline at end of file +export type Mapping = z.infer diff --git a/apps/client-ts/src/components/Configuration/AddLinkedAccount.tsx b/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx similarity index 91% rename from apps/client-ts/src/components/Configuration/AddLinkedAccount.tsx rename to apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx index c3c510fee..e2772bb28 100644 --- a/apps/client-ts/src/components/Configuration/AddLinkedAccount.tsx +++ b/apps/client-ts/src/components/Configuration/LinkedUsers/AddLinkedAccount.tsx @@ -35,7 +35,7 @@ import { import { PlusCircledIcon } from "@radix-ui/react-icons" import { cn } from "@/lib/utils" import { useState } from "react" -import useLinkedUserMutation from "@/hooks/mutations/useLinkedUserMutation" +import useCreateLinkedUser from "@/hooks/create/useCreateLinkedUser" import useOrganisationStore from "@/state/organisationStore" import useProjectStore from "@/state/projectStore" import { zodResolver } from "@hookform/resolvers/zod" @@ -43,6 +43,7 @@ import { useForm } from "react-hook-form" import * as z from "zod" import { usePostHog } from 'posthog-js/react' import config from "@/lib/config" +import { PlusCircle } from "lucide-react" interface LinkedUserModalObj { open: boolean; @@ -61,7 +62,7 @@ const AddLinkedAccount = () => { import: false }) - const { mutate } = useLinkedUserMutation(); + const { mutate } = useCreateLinkedUser(); const handleOpenChange = (open: boolean) => { setShowNewLinkedUserDialog(prevState => ({ ...prevState, open })); @@ -70,16 +71,13 @@ const AddLinkedAccount = () => { const posthog = usePostHog() - const {idOrg} = useOrganisationStore(); const {idProject} = useProjectStore(); - const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { linkedUserIdentifier: "", }, - }) function onSubmit(values: z.infer) { @@ -103,16 +101,15 @@ const AddLinkedAccount = () => { - + @@ -134,6 +131,7 @@ const AddLinkedAccount = () => { >

Add New Linked User

+ @@ -205,10 +203,10 @@ const AddLinkedAccount = () => { - - + diff --git a/apps/client-ts/src/components/Configuration/LinkedUsers/LinkedUsersPage.tsx b/apps/client-ts/src/components/Configuration/LinkedUsers/LinkedUsersPage.tsx new file mode 100644 index 000000000..7b70e6390 --- /dev/null +++ b/apps/client-ts/src/components/Configuration/LinkedUsers/LinkedUsersPage.tsx @@ -0,0 +1,20 @@ +import { DataTable } from "../../shared/data-table"; +import { columns } from "./columns"; + + +export function LinkedUsersPage({ + linkedUsers, + isLoading +}: { linkedUsers: Record[] | undefined; isLoading: boolean }) { + + const lusers = linkedUsers?.map(lu => ({ + linked_user_id: lu.id_linked_user, + remote_user_id: lu.linked_user_origin_id, + })) + + return ( + <> + {linkedUsers && } + + ) +} \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/LinkedUsers/columns.tsx b/apps/client-ts/src/components/Configuration/LinkedUsers/columns.tsx new file mode 100644 index 000000000..4af3a2ec7 --- /dev/null +++ b/apps/client-ts/src/components/Configuration/LinkedUsers/columns.tsx @@ -0,0 +1,63 @@ +import { ColumnDef } from "@tanstack/react-table"; +import { ColumnLU } from "./schema"; +import { DataTableColumnHeader } from "@/components/shared/data-table-column-header"; +import { Badge } from "@/components/ui/badge"; +import { Checkbox } from "@/components/ui/checkbox"; + +export const columns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + className="translate-y-[2px]" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + className="translate-y-[2px]" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "linked_user_id", + header: ({ column }) => ( + + ), + cell: ({ row }) =>{ + return ( +
+ {row.getValue("linked_user_id")} +
+ ) + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "remote_user_id", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
+ {row.getValue("remote_user_id")} +
+ ) + }, + enableSorting: false, + enableHiding: false, + }, + + ] \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/LinkedUsers/schema.ts b/apps/client-ts/src/components/Configuration/LinkedUsers/schema.ts new file mode 100644 index 000000000..71eb68289 --- /dev/null +++ b/apps/client-ts/src/components/Configuration/LinkedUsers/schema.ts @@ -0,0 +1,7 @@ +import { z } from "zod" + +export const linkedUsersSchema = z.object({ + linked_user_id: z.string(), + remote_user_id: z.string(), +}) +export type ColumnLU = z.infer \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/LinkedUsersPage.tsx b/apps/client-ts/src/components/Configuration/LinkedUsersPage.tsx deleted file mode 100644 index ea7b11fa0..000000000 --- a/apps/client-ts/src/components/Configuration/LinkedUsersPage.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable react/jsx-key */ -import { - Avatar, - AvatarFallback, - AvatarImage, - } from "@/components/ui/avatar" -import { Badge } from "@/components/ui/badge" -import { Skeleton } from "@/components/ui/skeleton"; - -export function LinkedUsersPage({ - linkedUsers, - isLoading -}: { linkedUsers: Record[] | undefined; isLoading: boolean }) { - - return ( -
- {linkedUsers && linkedUsers.map((linkedUser)=>{ - return ( -
- - - OM - -
-

- {isLoading ? : linkedUser.id_linked_user} -

-

- {isLoading ? : linkedUser.alias} -

-
-
- {isLoading ? : linkedUser.linked_user_origin_id} -
- -
- ) - })} - -
- ) -} \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx b/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx new file mode 100644 index 000000000..4b4cbc9ff --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Webhooks/AddWebhook.tsx @@ -0,0 +1,186 @@ +'use client' + +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogTrigger, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { PlusCircledIcon } from "@radix-ui/react-icons" +import { useState } from "react" +import useProjectStore from "@/state/projectStore" +import { + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { cn } from "@/lib/utils" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import * as z from "zod" +import { usePostHog } from 'posthog-js/react' +import config from "@/lib/config" +import { DataTableFacetedFilterWebhook } from "../../shared/data-table-webhook-scopes" +import useCreateWebhook from "@/hooks/create/useCreateWebhook" + + +const formSchema = z.object({ + description: z.string().min(2, { + message: "description must be at least 2 characters.", + }), + url: z.string().min(2, { + message: "url must be at least 2 characters.", + }), + scopes: z.string(), +}) + +const AddWebhook = () => { + const [open, setOpen] = useState(false); + + const posthog = usePostHog() + + const {idProject} = useProjectStore(); + + const { mutate } = useCreateWebhook(); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + description: "", + url: "", + scopes: "", + }, + }) + + + const handleOpenChange = (openVal : boolean) => { + setOpen(openVal); + form.reset(); + }; + + function onSubmit(values: z.infer) { + const selectedScopes = values.scopes ? values.scopes.split(' ') : []; + console.log({ ...values, scopes: selectedScopes }); + mutate({ + url: values.url, + description: values.description, + id_project: idProject, + scope: selectedScopes, + }); + handleOpenChange(false); + posthog?.capture("webhook_created", { + id_project: idProject, + mode: config.DISTRIBUTION + }) + } + + return ( + + + + + +
+ + + Create a webhook + + Set up your webhook endpoint to receive live events from Panora. + + + +
+
+ ( + + Scopes + +
+ +
+
+ +
+ )} + /> +
+
+
+ ( + + Endpoint URL + + + + + + + + )} + /> +
+
+ ( + + Description + + + + + + )} + /> +
+
+ + + +
+ +
+
+ ) +} + +export default AddWebhook; \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/Webhooks/WebhooksPage.tsx b/apps/client-ts/src/components/Configuration/Webhooks/WebhooksPage.tsx new file mode 100644 index 000000000..ca4540ebc --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Webhooks/WebhooksPage.tsx @@ -0,0 +1,43 @@ +/* eslint-disable react/jsx-key */ +'use client' + +import { DataTable } from "@/components/shared/data-table"; +import { DataTableLoading } from "@/components/shared/data-table-loading"; +import { useEffect, useState } from "react"; +import { useColumns } from "./columns"; + +export interface Webhook { + scope: string[]; + url: string; + endpoint_description: string | null; + secret: string; + id_webhook_endpoint: string; + active: boolean; +} +export function WebhooksPage({ + webhooks: initialWebhooks, + isLoading +}: { webhooks: Webhook[] | undefined; isLoading: boolean }) { + const [webhooks, setWebhooks] = useState(initialWebhooks); + + const columns = useColumns(webhooks, setWebhooks); + + useEffect(() => { + const whs = initialWebhooks?.map((wh) => ({ + scope: wh.scope, + url: wh.url, + endpoint_description: wh.endpoint_description, + secret: wh.secret, + id_webhook_endpoint: wh.id_webhook_endpoint, + active: wh.active + })); + setWebhooks(whs) + }, [initialWebhooks]); + + return ( +
+ {isLoading && } + {webhooks && } +
+ ) +} \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx b/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx new file mode 100644 index 000000000..f343ccaf3 --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Webhooks/columns.tsx @@ -0,0 +1,179 @@ +/* eslint-disable react/jsx-key */ +'use client' + +import { ColumnDef } from "@tanstack/react-table" +import { Badge } from "@/components/ui/badge" +import { PasswordInput } from "@/components/ui/password-input" +import { useState } from "react" +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" +import { Button } from "@/components/ui/button" +import { DataTableColumnHeader } from "@/components/shared/data-table-column-header" +import { DataTableRowActions } from "@/components/shared/data-table-row-actions" +import { Switch } from "@/components/ui/switch" +import useUpdateWebhookStatus from "@/hooks/update/useUpdateWebhookStatus" +import { Webhook } from "./WebhooksPage" + + +export function useColumns(webhooks: Webhook[] | undefined, setWebhooks: React.Dispatch>) { + const [copiedState, setCopiedState] = useState<{ [key: string]: boolean }>({}); + const { mutate } = useUpdateWebhookStatus(); + + const disableWebhook = (webhook_id: string, status: boolean) => { + mutate({ + id: webhook_id, + active: status, + }, { + onSuccess: () => { + const index = webhooks!.findIndex(webhook => webhook.id_webhook_endpoint === webhook_id); + if (index !== -1) { + const updatedWebhooks = [...webhooks!]; + updatedWebhooks[index].active = status; + setWebhooks(updatedWebhooks); + } + } + }); + } + + const handleCopy = (token: string) => { + navigator.clipboard.writeText(token); + setCopiedState((prevState) => ({ + ...prevState, + [token]: true, + })); + setTimeout(() => { + setCopiedState((prevState) => ({ + ...prevState, + [token]: false, + })); + }, 2000); // Reset copied state after 2 seconds + }; + + return [ + { + accessorKey: "url", + header: ({ column }) => ( + + ), + cell: ({ row }) => + + {row.getValue("url")} + , + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "secret", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const token: string = row.getValue("secret"); + const copied = copiedState[token] || false; + + return ( +
+
+ +
+
handleCopy(token)} + > + + + + + + +

Copy Secret

+
+
+
+
+
+ ); + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "endpoint_description", + header: ({ column }) => ( + + ), + cell: ({ row }) => + + {row.getValue("endpoint_description")} + , + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)) + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "scope", + header: ({ column }) => ( + + ), + cell: ({ row }) =>
+ {(row.getValue("scope") as string[]).map((scope) => { + return ( + {scope} + ) + })} +
, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)) + }, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "active", + header: ({ column }) => ( + + ), + cell: ({ row }) =>
+ disableWebhook(row.original.id_webhook_endpoint, !row.getValue('active')) } + /> +
, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)) + }, + enableSorting: false, + enableHiding: false, + }, + { + id: "actions", + cell: ({ row }) => , + }, + ] as ColumnDef[]; +} diff --git a/apps/client-ts/src/components/Configuration/Webhooks/schema.ts b/apps/client-ts/src/components/Configuration/Webhooks/schema.ts new file mode 100644 index 000000000..af05e863a --- /dev/null +++ b/apps/client-ts/src/components/Configuration/Webhooks/schema.ts @@ -0,0 +1,12 @@ +import { z } from "zod" + +export const webhookSchema = z.object({ + scope: z.string(), + url: z.string(), + endpoint_description: z.string(), + secret: z.string(), + id_webhook_endpoint: z.string(), + active: z.boolean() +}) + +export type Webhook = z.infer \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/WebhooksPage.tsx b/apps/client-ts/src/components/Configuration/WebhooksPage.tsx deleted file mode 100644 index b008145db..000000000 --- a/apps/client-ts/src/components/Configuration/WebhooksPage.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* eslint-disable react/jsx-key */ -'use client' - -import { Badge } from "@/components/ui/badge" -import { Skeleton } from "@/components/ui/skeleton"; -import { Switch } from "@/components/ui/switch"; -import useWebhookStatusMutation from "@/hooks/mutations/useWebhookUpdate"; -import { useEffect, useState } from "react"; - -export function WebhooksPage({ - webhooks: initialWebhooks, - isLoading -}: { webhooks: Record[] | undefined; isLoading: boolean }) { - const [webhooks, setWebhooks] = useState(initialWebhooks); - const [showSecret, setShowSecret] = useState>({}); - - const {mutate} = useWebhookStatusMutation(); - const disableWebhook = (webhook_id: string, status: boolean) => { - mutate({ - id: webhook_id, - active: status, - }, { - onSuccess: () => { - // Find the index of the webhook to update - const index = webhooks!.findIndex(webhook => webhook.id_webhook_endpoint === webhook_id); - if (index !== -1) { - // Create a new array with all previous webhooks - const updatedWebhooks = [...webhooks!]; - // Update the specific webhook's active status - updatedWebhooks[index].active = status; - // Set the updated webhooks array to state - setWebhooks(updatedWebhooks); - } - } - }); - } - - useEffect(() => { - setWebhooks(initialWebhooks); - }, [initialWebhooks]); - - return ( -
- {webhooks && webhooks.map((webhook)=>{ - return ( -
- -
-

- {isLoading ? : webhook.scope} -

-

- {isLoading ? : webhook.url} -

-

- {isLoading ? : - webhook.endpoint_description - } -

-

setShowSecret({...showSecret, [webhook.id_webhook_endpoint]: true})}> - - {isLoading ? : - showSecret[webhook.id_webhook_endpoint] ? -

navigator.clipboard.writeText(webhook.secret)}> - -

{webhook.secret}

-
: - <> - -

Reveal Key

- - } - -

-
-
- {isLoading ? : - disableWebhook(webhook.id_webhook_endpoint, !webhook.active)} - /> - } -
-
- ) - })} - -
- ) -} \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/authColumns.tsx b/apps/client-ts/src/components/Configuration/authColumns.tsx deleted file mode 100644 index 4ce2bd1e0..000000000 --- a/apps/client-ts/src/components/Configuration/authColumns.tsx +++ /dev/null @@ -1,180 +0,0 @@ -"use client" - -import { ColumnDef } from "@tanstack/react-table" -import Link from 'next/link'; -import { Badge,badgeVariants } from "@/components/ui/badge" -import { Checkbox } from "@/components/ui/checkbox" -import {Pencil2Icon} from '@radix-ui/react-icons' -import { DataTableColumnHeader } from "../shared/data-table-column-header" -import { DataTableRowActions } from "../shared/data-table-row-actions" -import { Mapping } from "./data/authCredentialsSchema" -import { Button } from "@/components/ui/button" -import {Switch} from '@/components/ui/switch' -import { useState } from "react"; -import { - Dialog, - DialogContent, - DialogTrigger, -} from "@/components/ui/dialog" -import { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { - DotsHorizontalIcon, -} from "@radix-ui/react-icons" -import AddAuthCredentialsForm from "./AddAuthCredentialsForm"; -import {getLogoURL,AuthStrategy} from '@panora/shared' -import useUpdateConnectionStrategyMutation from "@/hooks/mutations/useUpdateConnectionStrategy"; -import useDeleteConnectionStrategyMutation from "@/hooks/mutations/useDeleteConnectionStrategy"; - -const UpdateStatusCellComponent = ({row}:{row:any}) => { - const [isDisable,setDisable] = useState(false) - const {mutate} = useUpdateConnectionStrategyMutation() - const onSwitchChange = () => { - setDisable(true) - mutate( - { - id_cs:row.original.id_cs, - ToUpdateToggle:true - }, { - onSuccess : () => setDisable(false), - onError : () => setDisable(false) - }) - // console.log("Changed switch") - } - - return ( -
- {/* {row.getValue("activate")==false ? "Deactivated" : "Activated"} */} - onSwitchChange()} disabled={isDisable} value={row.getValue("status")} defaultChecked={row.getValue("status")}/> -
- ) -} - -const ActionCellComponent = ({row}:{row:any}) => { - - const [open,setOpen] = useState(false) - - const handleOpenChange = (open: boolean) => { - setOpen(open) - // form.reset() - }; - - const {mutate} = useDeleteConnectionStrategyMutation() - - const deleteConnectionStrategy = () => { - mutate( - { - id_cs:row.original.id_cs - } - ) - } - - return ( - - - - - - - - Edit - - - deleteConnectionStrategy()}>Delete - - - - - setOpen(false)} performUpdate={true} /> - - - - ) - -} - -export const authColumns: ColumnDef[] = [ - { - // acc: 'provider_name', - // accessorFn: row => `${row.provider_name} ${row.logoPath}`, - accessorKey:'provider_name', - // accessorKey: 'mergeD', - header: ({ column }) => ( - - ), - cell: ({ row }) =>{ - - - return ( -
- - {row.original.provider_name}/ - {row.original.provider_name} - -
- ) - }, - - }, - { - accessorKey: "auth_type", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - - const Auth_Type = AuthStrategy.oauth2===row.getValue("auth_type") ? "OAuth2" : AuthStrategy.api_key===row.getValue("auth_type") ? "API Key" : "Basic Auth" - - return ( -
- {Auth_Type} -
- ) - }, - }, - { - accessorKey: "vertical", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - - - return ( -
- {row.getValue("vertical")} -
- ) - }, - }, - { - accessorKey: "status", - header: ({ column }) => ( - - ), - cell: UpdateStatusCellComponent - }, - - - - { - accessorKey: "action", - header: ({ column }) => ( - - ), - cell: ActionCellComponent - - }, - -] - diff --git a/apps/client-ts/src/components/Configuration/data/authCredentialsSchema.ts b/apps/client-ts/src/components/Configuration/data/authCredentialsSchema.ts deleted file mode 100644 index 489bb1045..000000000 --- a/apps/client-ts/src/components/Configuration/data/authCredentialsSchema.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { AuthStrategy } from "@panora/shared" -import { z } from "zod" - -// We're keeping a simple non-relational schema here. -// IRL, you will have a schema for your data models. -export const mappingSchema = z.object({ - provider_name: z.string(), - auth_type: z.string(), - status: z.boolean(), - id_cs: z.string(), - vertical: z.string(), - type: z.string() - // activate: z.boolean(), - // credentials: z.object({ - // clientID: z.string().optional(), - // clientSecret: z.string().optional(), - // scope: z.string().optional(), - // apiKey: z.string().optional(), - // username: z.string().optional(), - // secret: z.string().optional() - - // }), - // action: z.string(), - // logoPath: z.string(), - // // destination_field: z.string(), - // // data_type: z.string() -}) - -export type Mapping = z.infer \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/data/data.tsx b/apps/client-ts/src/components/Configuration/data/data.tsx deleted file mode 100644 index 670860193..000000000 --- a/apps/client-ts/src/components/Configuration/data/data.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { - ArrowDownIcon, - ArrowRightIcon, - ArrowUpIcon, - CheckCircledIcon, - CircleIcon, - CrossCircledIcon, - QuestionMarkCircledIcon, - StopwatchIcon, - } from "@radix-ui/react-icons" - - export const labels = [ - { - value: "/crm/tasks", - label: "/crm/tasks", - }, - { - value: "/crm/notess", - label: "/crm/notess", - }, - { - value: "/crm/contacts", - label: "/crm/contacts", - }, - ] - - export const labels2 = [ - { - value: "GET", - label: "GET", - }, - { - value: "POST", - label: "POST", - }, - { - value: "GET", - label: "GET", - }, - ] - - export const statuses = [ - { - value: "backlog", - label: "Backlog", - icon: QuestionMarkCircledIcon, - }, - { - value: "todo", - label: "Todo", - icon: CircleIcon, - }, - { - value: "in progress", - label: "In Progress", - icon: StopwatchIcon, - }, - { - value: "done", - label: "Done", - icon: CheckCircledIcon, - }, - { - value: "canceled", - label: "Canceled", - icon: CrossCircledIcon, - }, - ] - - export const priorities = [ - { - label: "Low", - value: "low", - icon: ArrowDownIcon, - }, - { - label: "Medium", - value: "medium", - icon: ArrowRightIcon, - }, - { - label: "High", - value: "high", - icon: ArrowUpIcon, - }, - ] \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/data/mappings.ts b/apps/client-ts/src/components/Configuration/data/mappings.ts deleted file mode 100644 index eba75bb9c..000000000 --- a/apps/client-ts/src/components/Configuration/data/mappings.ts +++ /dev/null @@ -1,62 +0,0 @@ -export const MAPPINGS = [ - { - "standard_object": "Contact", - "source_app": "Hubspot", - "status": "defined", - "category": "CRM", - "source_field": "favorite_color", - "destination_field": "", - "data_type": "string", - "date": "Jul 31 2019" - }, - { - "standard_object": "Contact", - "source_app": "Zoho", - "status": "defined", - "category": "CRM", - "source_field": "favorite_team", - "destination_field": "fav_team", - "data_type": "string", - "date": "Jul 31 2019" - }, - { - "standard_object": "Company", - "source_app": "Hubspot", - "status": "defined", - "category": "CRM", - "source_field": "favorite_color", - "destination_field": "fav_color", - "data_type": "string", - "date": "Jul 31 2019" - }, - { - "standard_object": "Ticket", - "source_app": "Slack", - "status": "mapped", - "category": "TICKETING", - "source_field": "favorite_color", - "destination_field": "fav_color", - "data_type": "string", - "date": "Jul 31 2019" - }, - { - "standard_object": "Contact", - "source_app": "Hubspot", - "status": "defined", - "category": "CRM", - "source_field": "favorite_color", - "destination_field": "fav_color", - "data_type": "string", - "date": "Jul 31 2019" - }, - { - "standard_object": "Contact", - "source_app": "Hubspot", - "status": "defined", - "category": "CRM", - "source_field": "favorite_color", - "destination_field": "fav_color", - "data_type": "string", - "date": "Jul 31 2019" - }, -] \ No newline at end of file diff --git a/apps/client-ts/src/components/Configuration/data/seed.ts b/apps/client-ts/src/components/Configuration/data/seed.ts deleted file mode 100644 index b35a5ef17..000000000 --- a/apps/client-ts/src/components/Configuration/data/seed.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from "fs" -import path from "path" -import { faker } from "@faker-js/faker" - -import { labels, priorities, statuses } from "./data" - -const tasks = Array.from({ length: 100 }, () => ({ - id: `TASK-${faker.number.int({ min: 1000, max: 9999 })}`, - title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()), - status: faker.helpers.arrayElement(statuses).value, - label: faker.helpers.arrayElement(labels).value, - direction: faker.helpers.arrayElement(priorities).value, -})) - -fs.writeFileSync( - path.join(__dirname, "tasks.json"), - JSON.stringify(tasks, null, 2) -) - -console.log("✅ Tasks data generated.") \ No newline at end of file diff --git a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx index 341b18e51..835d876f5 100644 --- a/apps/client-ts/src/components/Connection/AddConnectionButton.tsx +++ b/apps/client-ts/src/components/Connection/AddConnectionButton.tsx @@ -34,11 +34,10 @@ import { PopoverTrigger, } from "@/components/ui/popover" import { PlusCircledIcon } from "@radix-ui/react-icons" -import { cn } from "@/lib/utils" import { useState } from "react" import useOrganisationStore from "@/state/organisationStore" import useProjectStore from "@/state/projectStore" -import useMagicLinkMutation from "@/hooks/mutations/useMagicLinkMutation" +import useCreateMagicLink from "@/hooks/create/useCreateMagicLink" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import * as z from "zod" @@ -53,7 +52,6 @@ const formSchema = z.object({ message: "linkedUserMail must be at least 2 characters.", }), }) - interface LinkedUserModalObj { open: boolean; import?: boolean; @@ -74,7 +72,7 @@ const AddConnectionButton = ({ const posthog = usePostHog() - const { mutate, isError, error } = useMagicLinkMutation(); + const { mutate, isError, error } = useCreateMagicLink(); const {nameOrg} = useOrganisationStore(); const {idProject} = useProjectStore(); @@ -119,13 +117,10 @@ const AddConnectionButton = ({ @@ -260,7 +255,7 @@ const AddConnectionButton = ({ Cancel - + diff --git a/apps/client-ts/src/components/Connection/ConnectionTable.tsx b/apps/client-ts/src/components/Connection/ConnectionTable.tsx index a271e762e..220130552 100644 --- a/apps/client-ts/src/components/Connection/ConnectionTable.tsx +++ b/apps/client-ts/src/components/Connection/ConnectionTable.tsx @@ -12,7 +12,7 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D import { Button } from "../ui/button"; import { PlusCircledIcon } from "@radix-ui/react-icons"; import CopyLinkInput from "./CopyLinkInput"; -import useConnections from "@/hooks/useConnections"; +import useConnections from "@/hooks/get/useConnections"; import { DataTableLoading } from "../shared/data-table-loading"; import { Suspense, useState } from "react"; import AddConnectionButton from "./AddConnectionButton"; @@ -21,7 +21,6 @@ import useMagicLinkStore from "@/state/magicLinkStore"; import useOrganisationStore from "@/state/organisationStore"; import { usePostHog } from 'posthog-js/react' import useProjectStore from "@/state/projectStore"; -import Cookies from 'js-cookie'; export default function ConnectionTable() { diff --git a/apps/client-ts/src/components/Connection/CopyLinkInput.tsx b/apps/client-ts/src/components/Connection/CopyLinkInput.tsx index a3aa77767..3bb7f773b 100644 --- a/apps/client-ts/src/components/Connection/CopyLinkInput.tsx +++ b/apps/client-ts/src/components/Connection/CopyLinkInput.tsx @@ -12,7 +12,6 @@ const CopyLinkInput = () => { const {uniqueLink} = useMagicLinkStore(); - const handleCopy = async () => { try { await navigator.clipboard.writeText(`${config.MAGIC_LINK_DOMAIN}/?uniqueLink=${uniqueLink}`); diff --git a/apps/client-ts/src/components/Connection/columns.tsx b/apps/client-ts/src/components/Connection/columns.tsx index 23fad47b4..a5d6f9fdb 100644 --- a/apps/client-ts/src/components/Connection/columns.tsx +++ b/apps/client-ts/src/components/Connection/columns.tsx @@ -5,9 +5,9 @@ import { ColumnDef } from "@tanstack/react-table" import { Badge } from "@/components/ui/badge" import { Checkbox } from "@/components/ui/checkbox" -import { Connection } from "./data/schema" +import { Connection } from "./schema" import { DataTableColumnHeader } from "./../shared/data-table-column-header" -import React,{ useState } from "react" +import React from "react" import { ClipboardIcon } from '@radix-ui/react-icons' import { toast } from "sonner" import { getLogoURL } from "@panora/shared" diff --git a/apps/client-ts/src/components/Connection/data/connection.ts b/apps/client-ts/src/components/Connection/data/connection.ts deleted file mode 100644 index df86f4189..000000000 --- a/apps/client-ts/src/components/Connection/data/connection.ts +++ /dev/null @@ -1,132 +0,0 @@ -export const CONNECTIONS = [ - { - "linkedUser": "TASK-8782", - "status": "in progress", - "app": "documentation", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - - }, - { - "linkedUser": "TASK-7878", - "integration": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!", - "status": "backlog", - "app": "documentation", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-7839", - "integration": "We need to bypass the neural TCP card!", - "status": "todo", - "app": "bug", - "category": "high", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-5562", - "integration": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwlinkedUserth!", - "status": "backlog", - "app": "feature", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-8686", - "integration": "I'll parse the wireless SSL protocol, that should driver the API panel!", - "status": "canceled", - "app": "feature", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-1280", - "integration": "Use the digital TLS panel, then you can transmit the haptic system!", - "status": "done", - "app": "bug", - "category": "high", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-7262", - "integration": "The UTF8 application is down, parse the neural bandwlinkedUserth so we can back up the PNG firewall!", - "status": "done", - "app": "feature", - "category": "high", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-1138", - "integration": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwlinkedUserth!", - "status": "in progress", - "app": "feature", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-7184", - "integration": "We need to program the back-end THX pixel!", - "status": "todo", - "app": "feature", - "category": "low", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-5160", - "integration": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!", - "status": "in progress", - "app": "documentation", - "category": "high", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-5618", - "integration": "Generating the driver won't do anything, we need to index the online SSL application!", - "status": "done", - "app": "documentation", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-6699", - "integration": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!", - "status": "backlog", - "app": "documentation", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - }, - { - "linkedUser": "TASK-2858", - "integration": "We need to overrlinkedUsere the online UDP bus!", - "status": "backlog", - "app": "bug", - "category": "medium", - "date": "", - "organisation": "", - "connectionToken": "", - }, -] \ No newline at end of file diff --git a/apps/client-ts/src/components/Connection/data/data.tsx b/apps/client-ts/src/components/Connection/data/data.tsx deleted file mode 100644 index 03743ba86..000000000 --- a/apps/client-ts/src/components/Connection/data/data.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { - ArrowDownIcon, - ArrowRightIcon, - ArrowUpIcon, - CheckCircledIcon, - CircleIcon, - CrossCircledIcon, - QuestionMarkCircledIcon, - StopwatchIcon, - } from "@radix-ui/react-icons" - - export const labels = [ - { - value: "bug", - label: "Bug", - }, - { - value: "feature", - label: "Feature", - }, - { - value: "documentation", - label: "Documentation", - }, - ] - - export const statuses = [ - { - value: "backlog", - label: "Backlog", - icon: QuestionMarkCircledIcon, - }, - { - value: "todo", - label: "Todo", - icon: CircleIcon, - }, - { - value: "in progress", - label: "In Progress", - icon: StopwatchIcon, - }, - { - value: "done", - label: "Done", - icon: CheckCircledIcon, - }, - { - value: "canceled", - label: "Canceled", - icon: CrossCircledIcon, - }, - ] - - export const priorities = [ - { - label: "Low", - value: "low", - icon: ArrowDownIcon, - }, - { - label: "Medium", - value: "medium", - icon: ArrowRightIcon, - }, - { - label: "High", - value: "high", - icon: ArrowUpIcon, - }, - ] \ No newline at end of file diff --git a/apps/client-ts/src/components/Connection/data/seed.ts b/apps/client-ts/src/components/Connection/data/seed.ts deleted file mode 100644 index b35a5ef17..000000000 --- a/apps/client-ts/src/components/Connection/data/seed.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from "fs" -import path from "path" -import { faker } from "@faker-js/faker" - -import { labels, priorities, statuses } from "./data" - -const tasks = Array.from({ length: 100 }, () => ({ - id: `TASK-${faker.number.int({ min: 1000, max: 9999 })}`, - title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()), - status: faker.helpers.arrayElement(statuses).value, - label: faker.helpers.arrayElement(labels).value, - direction: faker.helpers.arrayElement(priorities).value, -})) - -fs.writeFileSync( - path.join(__dirname, "tasks.json"), - JSON.stringify(tasks, null, 2) -) - -console.log("✅ Tasks data generated.") \ No newline at end of file diff --git a/apps/client-ts/src/components/Connection/data/schema.ts b/apps/client-ts/src/components/Connection/schema.ts similarity index 100% rename from apps/client-ts/src/components/Connection/data/schema.ts rename to apps/client-ts/src/components/Connection/schema.ts diff --git a/apps/client-ts/src/components/Dashboard/date-range-picker.tsx b/apps/client-ts/src/components/Dashboard/date-range-picker.tsx deleted file mode 100644 index 67b3b4595..000000000 --- a/apps/client-ts/src/components/Dashboard/date-range-picker.tsx +++ /dev/null @@ -1,65 +0,0 @@ -"use client" - -import * as React from "react" -import { CalendarIcon } from "@radix-ui/react-icons" -import { addDays, format } from "date-fns" -import { DateRange } from "react-day-picker" - -import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" -import { Calendar } from "@/components/ui/calendar" -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover" - -export function CalendarDateRangePicker({ - className, -}: React.HTMLAttributes) { - const [date, setDate] = React.useState({ - from: new Date(2023, 0, 20), - to: addDays(new Date(2023, 0, 20), 20), - }) - - return ( -
- - - - - - - - -
- ) -} \ No newline at end of file diff --git a/apps/client-ts/src/components/Dashboard/overview.tsx b/apps/client-ts/src/components/Dashboard/overview.tsx deleted file mode 100644 index 980131dc0..000000000 --- a/apps/client-ts/src/components/Dashboard/overview.tsx +++ /dev/null @@ -1,78 +0,0 @@ -"use client" - -import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts" - -const data = [ - { - name: "Jan", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Feb", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Mar", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Apr", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "May", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Jun", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Jul", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Aug", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Sep", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Oct", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Nov", - total: Math.floor(Math.random() * 5000) + 1000, - }, - { - name: "Dec", - total: Math.floor(Math.random() * 5000) + 1000, - }, -] - -export function Overview() { - return ( - - - - `$${value}`} - /> - - - - ) -} \ No newline at end of file diff --git a/apps/client-ts/src/components/Events/EventsTable.tsx b/apps/client-ts/src/components/Events/EventsTable.tsx index b60993520..920897cdd 100644 --- a/apps/client-ts/src/components/Events/EventsTable.tsx +++ b/apps/client-ts/src/components/Events/EventsTable.tsx @@ -1,17 +1,16 @@ import { columns } from "./columns" import { ApiDataTable } from '../shared/api-data-table'; -import useEvents from "@/hooks/useEvents"; +import useEvents from "@/hooks/get/useEvents"; import { DataTableLoading } from "../shared/data-table-loading"; import { events as Event } from "api"; -import { useEventsCount } from '@/hooks/useEventsCount'; -import { useQueryPagination } from '@/hooks/useQueryPagination'; +import { useEventsCount } from '@/hooks/get/useEventsCount'; +import { useQueryPagination } from '@/hooks/get/useQueryPagination'; import useProjectStore from "@/state/projectStore"; export default function EventsTable() { const { data: eventsCount } = useEventsCount(); const pagination = useQueryPagination({ totalItems: eventsCount }); - const {idProject} = useProjectStore(); const { data: events, @@ -33,9 +32,6 @@ export default function EventsTable() { date: event.timestamp.toLocaleString(), // convert Date to string })); - // Already did it at api level - // const sortedTransformedEvents = transformedEvents?.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); - if(isLoading){ return ( @@ -46,7 +42,6 @@ export default function EventsTable() { console.log("error events.."); } - return ( <> {transformedEvents && ( diff --git a/apps/client-ts/src/components/Events/columns.tsx b/apps/client-ts/src/components/Events/columns.tsx index 5f5bfd020..27b8f8d06 100644 --- a/apps/client-ts/src/components/Events/columns.tsx +++ b/apps/client-ts/src/components/Events/columns.tsx @@ -6,7 +6,7 @@ import { Badge } from "@/components/ui/badge" import { Checkbox } from "@/components/ui/checkbox" import { DataTableColumnHeader } from "../shared/data-table-column-header" -import { Event } from "./data/schema" +import { Event } from "./schema" import { getLogoURL } from "@panora/shared" export const columns: ColumnDef[] = [ diff --git a/apps/client-ts/src/components/Events/data/data.tsx b/apps/client-ts/src/components/Events/data/data.tsx deleted file mode 100644 index 670860193..000000000 --- a/apps/client-ts/src/components/Events/data/data.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { - ArrowDownIcon, - ArrowRightIcon, - ArrowUpIcon, - CheckCircledIcon, - CircleIcon, - CrossCircledIcon, - QuestionMarkCircledIcon, - StopwatchIcon, - } from "@radix-ui/react-icons" - - export const labels = [ - { - value: "/crm/tasks", - label: "/crm/tasks", - }, - { - value: "/crm/notess", - label: "/crm/notess", - }, - { - value: "/crm/contacts", - label: "/crm/contacts", - }, - ] - - export const labels2 = [ - { - value: "GET", - label: "GET", - }, - { - value: "POST", - label: "POST", - }, - { - value: "GET", - label: "GET", - }, - ] - - export const statuses = [ - { - value: "backlog", - label: "Backlog", - icon: QuestionMarkCircledIcon, - }, - { - value: "todo", - label: "Todo", - icon: CircleIcon, - }, - { - value: "in progress", - label: "In Progress", - icon: StopwatchIcon, - }, - { - value: "done", - label: "Done", - icon: CheckCircledIcon, - }, - { - value: "canceled", - label: "Canceled", - icon: CrossCircledIcon, - }, - ] - - export const priorities = [ - { - label: "Low", - value: "low", - icon: ArrowDownIcon, - }, - { - label: "Medium", - value: "medium", - icon: ArrowRightIcon, - }, - { - label: "High", - value: "high", - icon: ArrowUpIcon, - }, - ] \ No newline at end of file diff --git a/apps/client-ts/src/components/Events/data/jobsConst.ts b/apps/client-ts/src/components/Events/data/jobsConst.ts deleted file mode 100644 index bbee2b0f8..000000000 --- a/apps/client-ts/src/components/Events/data/jobsConst.ts +++ /dev/null @@ -1,120 +0,0 @@ -export const JOBS = [ - { - "method": "GET", - "integration": "You can't compress the program without quantifying the open-source SSD pixel!", - "status": "in progress", - "url": "/crm/contacts", - "direction": "medium", - "date": "", - "organisation": "", - - }, - { - "method": "GET", - "integration": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!", - "status": "backlog", - "url": "/crm/contacts", - "direction": "medium", - "date": "", - "organisation": "", - }, - { - "method": "GET", - "integration": "We need to bypass the neural TCP card!", - "status": "todo", - "url": "/crm/tasks", - "direction": "high", - "date": "", - "organisation": "", - }, - { - "method": "GET", - "integration": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwmethodth!", - "status": "backlog", - "url": "/crm/notess", - "direction": "medium", - "date": "", - "organisation": "", - }, - { - "method": "GET", - "integration": "I'll parse the wireless SSL protocol, that should driver the API panel!", - "status": "canceled", - "url": "/crm/notess", - "direction": "medium", - "date": "", - "organisation": "", - }, - { - "method": "POST", - "integration": "Use the digital TLS panel, then you can transmit the haptic system!", - "status": "done", - "url": "/crm/tasks", - "direction": "high", - "date": "", - "organisation": "", - }, - { - "method": "POST", - "integration": "The UTF8 application is down, parse the neural bandwmethodth so we can back up the PNG firewall!", - "status": "done", - "url": "/crm/notess", - "direction": "high", - "date": "", - "organisation": "", - }, - { - "method": "GET", - "integration": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwmethodth!", - "status": "in progress", - "url": "/crm/notess", - "direction": "medium", - "date": "", - "organisation": "", - }, - { - "method": "GET", - "integration": "We need to program the back-end THX pixel!", - "status": "todo", - "url": "/crm/notess", - "direction": "low", - "date": "", - "organisation": "", - }, - { - "method": "POST", - "integration": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!", - "status": "in progress", - "url": "/crm/contacts", - "direction": "high", - "date": "", - "organisation": "", - }, - { - "method": "POST", - "integration": "Generating the driver won't do anything, we need to index the online SSL application!", - "status": "done", - "url": "/crm/contacts", - "direction": "medium", - "date": "", - "organisation": "", - }, - { - "method": "GET", - "integration": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!", - "status": "backlog", - "url": "/crm/contacts", - "direction": "medium", - "date": "", - "organisation": "", - }, - { - "method": "POST", - "integration": "We need to overrmethode the online UDP bus!", - "status": "backlog", - "url": "/crm/tasks", - "direction": "medium", - "date": "", - "organisation": "", - }, -] \ No newline at end of file diff --git a/apps/client-ts/src/components/Events/data/seed.ts b/apps/client-ts/src/components/Events/data/seed.ts deleted file mode 100644 index b35a5ef17..000000000 --- a/apps/client-ts/src/components/Events/data/seed.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from "fs" -import path from "path" -import { faker } from "@faker-js/faker" - -import { labels, priorities, statuses } from "./data" - -const tasks = Array.from({ length: 100 }, () => ({ - id: `TASK-${faker.number.int({ min: 1000, max: 9999 })}`, - title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()), - status: faker.helpers.arrayElement(statuses).value, - label: faker.helpers.arrayElement(labels).value, - direction: faker.helpers.arrayElement(priorities).value, -})) - -fs.writeFileSync( - path.join(__dirname, "tasks.json"), - JSON.stringify(tasks, null, 2) -) - -console.log("✅ Tasks data generated.") \ No newline at end of file diff --git a/apps/client-ts/src/components/Events/data/schema.ts b/apps/client-ts/src/components/Events/schema.ts similarity index 100% rename from apps/client-ts/src/components/Events/data/schema.ts rename to apps/client-ts/src/components/Events/schema.ts diff --git a/apps/client-ts/src/components/Nav/main-nav-sm.tsx b/apps/client-ts/src/components/Nav/main-nav-sm.tsx index 84c22e3bf..05e097ba6 100644 --- a/apps/client-ts/src/components/Nav/main-nav-sm.tsx +++ b/apps/client-ts/src/components/Nav/main-nav-sm.tsx @@ -22,14 +22,14 @@ export function SmallNav({ const [open, setOpen] = useState(false); const navItemClassName = (itemName: string) => `text-sm border-b font-medium w-full text-left mx-0 py-2 dark:hover:bg-zinc-900 hover:bg-zinc-200 cursor-pointer ${ - selectedItem === itemName ? "dark:bg-zinc-800 bg-zinc-200" : "text-muted-foreground" - } transition-colors`; - - function click(name: string) { - setSelectedItem(name); - onLinkClick(name); - setOpen(false); - } + selectedItem === itemName ? "dark:bg-zinc-800 bg-zinc-200" : "text-muted-foreground" + } transition-colors`; + + function click(name: string) { + setSelectedItem(name); + onLinkClick(name); + setOpen(false); + } return (
diff --git a/apps/client-ts/src/components/Nav/main-nav.tsx b/apps/client-ts/src/components/Nav/main-nav.tsx index e26ed860c..0c477c8c7 100644 --- a/apps/client-ts/src/components/Nav/main-nav.tsx +++ b/apps/client-ts/src/components/Nav/main-nav.tsx @@ -13,14 +13,15 @@ export function MainNav({ }) { const [selectedItem, setSelectedItem] = useState(""); const pathname = usePathname(); + useEffect(() => { setSelectedItem(pathname.substring(1)) }, [pathname]) const navItemClassName = (itemName: string) => `group flex items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground cursor-pointer ${ - selectedItem === itemName ? 'bg-accent' : 'transparent' - } transition-colors`; + selectedItem === itemName ? 'bg-accent' : 'transparent' + } transition-colors`; function click(e: MouseEvent, name: string) { e.preventDefault(); diff --git a/apps/client-ts/src/components/Nav/theme-toggle.tsx b/apps/client-ts/src/components/Nav/theme-toggle.tsx index 69d0e75ff..a9d885e8e 100644 --- a/apps/client-ts/src/components/Nav/theme-toggle.tsx +++ b/apps/client-ts/src/components/Nav/theme-toggle.tsx @@ -31,9 +31,6 @@ export function ThemeToggle({} : ToggleProps) { setTheme("dark")}> Dark - setTheme("system")}> - System - ) diff --git a/apps/client-ts/src/components/Nav/user-nav.tsx b/apps/client-ts/src/components/Nav/user-nav.tsx index 996d0a269..e42590d71 100644 --- a/apps/client-ts/src/components/Nav/user-nav.tsx +++ b/apps/client-ts/src/components/Nav/user-nav.tsx @@ -20,11 +20,10 @@ import Cookies from 'js-cookie'; import useProjectStore from "@/state/projectStore" import { useQueryClient } from '@tanstack/react-query'; - export function UserNav() { const router = useRouter(); const { profile, setProfile } = useProfileStore(); - const { idProject, setIdProject } = useProjectStore(); + const { setIdProject } = useProjectStore(); const queryClient = useQueryClient(); const onLogout = () => { @@ -33,7 +32,6 @@ export function UserNav() { setProfile(null) setIdProject("") queryClient.clear() - } return ( diff --git a/apps/client-ts/src/components/Provider/provider.tsx b/apps/client-ts/src/components/Provider/provider.tsx index bf488b3aa..e9e423564 100644 --- a/apps/client-ts/src/components/Provider/provider.tsx +++ b/apps/client-ts/src/components/Provider/provider.tsx @@ -1,18 +1,16 @@ "use client" -import React, { ReactNode, useState } from "react" +import React from "react" import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental" import { QueryClientProvider, QueryClient } from "@tanstack/react-query" import { ReactQueryDevtools } from "@tanstack/react-query-devtools" -import { useRouter } from "next/router" - function Provider({ children }: any) { const client = new QueryClient(); return ( <> - {children} + {children} diff --git a/apps/client-ts/src/components/RootLayout/index.tsx b/apps/client-ts/src/components/RootLayout/index.tsx index 7e69f5f35..a54968a2b 100644 --- a/apps/client-ts/src/components/RootLayout/index.tsx +++ b/apps/client-ts/src/components/RootLayout/index.tsx @@ -8,11 +8,11 @@ import TeamSwitcher from '@/components/shared/team-switcher'; import Link from 'next/link' import { useRouter } from 'next/navigation'; import { cn } from "@/lib/utils"; -import useProfileStore from '@/state/profileStore'; import useProjectStore from '@/state/projectStore'; import { ThemeToggle } from '@/components/Nav/theme-toggle'; -import useProjects from '@/hooks/useProjects'; -import useRefreshAccessTokenMutation from '@/hooks/mutations/useRefreshAccessTokenMutation'; +import useProjects from '@/hooks/get/useProjects'; +import useRefreshAccessTokenMutation from '@/hooks/create/useRefreshAccessToken'; +import { useTheme } from 'next-themes'; export const RootLayout = ({children}:{children:React.ReactNode}) => { const router = useRouter() @@ -20,6 +20,7 @@ export const RootLayout = ({children}:{children:React.ReactNode}) => { const {data : projectsData} = useProjects(); const { idProject, setIdProject } = useProjectStore(); const {mutate : refreshAccessToken} = useRefreshAccessTokenMutation() + const { theme } = useTheme() useEffect(() => { if(projectsData) @@ -47,7 +48,7 @@ export const RootLayout = ({children}:{children:React.ReactNode}) => {