diff --git a/apps/web/app/(webapp)/(components)/CodeEditor.tsx b/apps/web/app/(webapp)/(components)/CodeEditor.tsx new file mode 100644 index 00000000..f6837247 --- /dev/null +++ b/apps/web/app/(webapp)/(components)/CodeEditor.tsx @@ -0,0 +1,56 @@ +'use client' + +import React from 'react'; +// import dynamic from 'next/dynamic'; +import MonacoEditor from 'react-monaco-editor'; +import { CodeEditorProps } from '@/types/webappTypes/componentsTypes'; +// const MonacoEditor = dynamic(import('react-monaco-editor'), { ssr: false }); + +const CodeEditor = ({ + code, + setCode +}: CodeEditorProps) => { + + const handleEditorChange = (newCode: string, event: any) => { + setCode(newCode); + }; + + const options = { + autoIndent: 'full', + contextmenu: true, + fontFamily: 'monospace', + fontSize: 13, + lineHeight: 24, + hideCursorInOverviewRuler: true, + matchBrackets: 'always', + minimap: { + enabled: true, + }, + scrollbar: { + horizontalSliderSize: 4, + verticalSliderSize: 18, + }, + selectOnLineNumbers: true, + roundedSelection: false, + readOnly: false, + cursorStyle: 'line', + automaticLayout: true, + }; + + return ( +
+ +
+ ) +} + +export default CodeEditor \ No newline at end of file diff --git a/apps/web/app/(webapp)/(components)/EmptyState.tsx b/apps/web/app/(webapp)/(components)/EmptyState.tsx index 45fc61ca..e170d990 100644 --- a/apps/web/app/(webapp)/(components)/EmptyState.tsx +++ b/apps/web/app/(webapp)/(components)/EmptyState.tsx @@ -1,6 +1,6 @@ import { EmptyStateProps } from '@/types/webappTypes/componentsTypes'; import React from 'react'; -import { EmptyStateButton } from '.'; +import { AddPermissionButton } from '../app/api-management/consumers/(components)'; const EmptyState = ({ type, @@ -8,6 +8,8 @@ const EmptyState = ({ body, parentStyle, titleStyle, + searchQuery, + altData, bodyStyle, iconStyle, containerStyle, @@ -77,8 +79,14 @@ const EmptyState = ({ { - button && - + button && ( + buttonType == 'ADD_PERMISSIONS' ? + + : null + ) } diff --git a/apps/web/app/(webapp)/(components)/EmptyStateButton.tsx b/apps/web/app/(webapp)/(components)/EmptyStateButton.tsx deleted file mode 100644 index 7a66e4e3..00000000 --- a/apps/web/app/(webapp)/(components)/EmptyStateButton.tsx +++ /dev/null @@ -1,27 +0,0 @@ -'use client' - -import { Button } from '@/components/globalComponents' -import React from 'react' - -const EmptyStateButton = ({ type }: { type?: string}) => { - - const handleAddPermission = () => null; - - return ( -
- { - type == 'ADD_PERMISSIONS' ? -
- ) -} - -export default EmptyStateButton \ No newline at end of file diff --git a/apps/web/app/(webapp)/(components)/ListPanel.tsx b/apps/web/app/(webapp)/(components)/ListPanel.tsx new file mode 100644 index 00000000..cfc853b8 --- /dev/null +++ b/apps/web/app/(webapp)/(components)/ListPanel.tsx @@ -0,0 +1,88 @@ +'use client' + +import { ListPanelContainerProps } from '@/types/webappTypes/componentsTypes' +import React, { useEffect, useState } from 'react'; +import { motion } from 'framer-motion'; +import { getObjectsNotInArrayB } from '@/utils/getObjectNotInArray'; + +const ListPanel = ({ + panel, + currentValue, + setCurrentValue, + containerStyle +}: ListPanelContainerProps) => { + + const [panelOut, setPanelOut] = useState([]); + const panelRemaining = getObjectsNotInArrayB(panel, panelOut); + + useEffect(() => { + setPanelOut([panel[0]]); + }, []); + + const handlePanelList = (data: any) => { + if (panelOut?.find((item: any) => item?.id == data?.id)) { + const filteredItem = panelOut?.filter(item => item?.id != data?.id); + setPanelOut(filteredItem); + } else { + setPanelOut((prev: any) => [...prev, data]); + } + } + + return ( +
+ { + panelOut?.map((data) => ( +
setCurrentValue(data?.value)} + > +
+ {data?.label} +
+ + { + currentValue == data?.value && + + } +
+ )) + } + +
+ + +
+ { + panelRemaining?.map((data) => ( + + )) + } +
+
+
+ ) +} + +export default ListPanel \ No newline at end of file diff --git a/apps/web/app/(webapp)/(components)/index.ts b/apps/web/app/(webapp)/(components)/index.ts index 6c2843d4..c29b7342 100644 --- a/apps/web/app/(webapp)/(components)/index.ts +++ b/apps/web/app/(webapp)/(components)/index.ts @@ -27,7 +27,8 @@ import TierBox from "./TierBox"; import DownloadButton from "./DownloadButton"; import DragAndUploadFile from "./DragAndUploadFile"; import KybBanner from "./KybBanner"; -import EmptyStateButton from "./EmptyStateButton"; +import ListPanel from "./ListPanel"; +import CodeEditor from "./CodeEditor"; export { DatePicker, @@ -59,5 +60,6 @@ export { TierBox, DownloadButton, DragAndUploadFile, - EmptyStateButton + ListPanel, + CodeEditor } \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/collections/(components)/CollectionSection.tsx b/apps/web/app/(webapp)/app/api-management/collections/(components)/CollectionSection.tsx index e9597f5f..a6ae24f9 100644 --- a/apps/web/app/(webapp)/app/api-management/collections/(components)/CollectionSection.tsx +++ b/apps/web/app/(webapp)/app/api-management/collections/(components)/CollectionSection.tsx @@ -1,14 +1,12 @@ 'use client' -import { AppCenterModal, AppRightModal, ConfigurationBox, EmptyState, TableElement, TwoFactorAuthModal } from '@/app/(webapp)/(components)' +import { AppCenterModal, ConfigurationBox, EmptyState, TableElement, TwoFactorAuthModal } from '@/app/(webapp)/(components)' import { SearchBar, SelectElement } from '@/components/forms' import { COLLECTION_ACTIONS_DATA } from '@/data/collectionDatas' import { HeadersProps, HostsProps, SectionsProps, SnisProps } from '@/types/webappTypes/appTypes' import { createColumnHelper } from '@tanstack/react-table' import { useRouter } from 'next/navigation' import React, { FormEvent, useEffect, useState } from 'react' -import ApiConfiguration from './ApiConfiguration' -import ModifyApiConfiguration from './ModifyApiConfiguration' import { updateSearchParams } from '@/utils/searchParams' import clientAxiosRequest from '@/hooks/clientAxiosRequest' import * as API from '@/config/endpoints'; @@ -40,50 +38,50 @@ const CollectionSection = ({ const [api, setApi] = useState(null); const profile = altData; const userType = profile?.user?.role?.parent?.slug; - const [api_endpoint, setApiEndpoint] = useState(null); + // const [api_endpoint, setApiEndpoint] = useState(null); const environment = 'development'; // console.log(rawData); - const [endpoint_url, setEndpointUrl] = useState(''); - const [parameters, setParameters] = useState(''); - const [snis, setSnis] = useState([]); - const [hosts, setHost] = useState([]); - const [headers, setHeaders] = useState([]); + // const [endpoint_url, setEndpointUrl] = useState(''); + // const [parameters, setParameters] = useState(''); + // const [snis, setSnis] = useState([]); + // const [hosts, setHost] = useState([]); + // const [headers, setHeaders] = useState([]); // const getUserProfile = getJsCookies('aperta-user-profile'); // const userProfile = getUserProfile ? JSON.parse(getUserProfile) : null; // const userType = userProfile?.userType; - const refreshData = () => { - setEndpointUrl(''); - setParameters(''); - setSnis([]); - setHost([]); - setHeaders([]); - } - - const updateFields = (value: any) => { - setEndpointUrl(value?.endpoint_url); - setParameters(value?.parameters); - } - - async function FetchData() { - const result = await clientAxiosRequest({ - headers: {}, - apiEndpoint: API.getAPI({ - environment, - id: api?.id - }), - method: 'GET', - data: null, - noToast: true, - }); - setApiEndpoint(result?.data); - } - - useEffect(() => { - api?.id && FetchData(); - }, [api?.id]); + // const refreshData = () => { + // setEndpointUrl(''); + // setParameters(''); + // setSnis([]); + // setHost([]); + // setHeaders([]); + // } + + // const updateFields = (value: any) => { + // setEndpointUrl(value?.endpoint_url); + // setParameters(value?.parameters); + // } + + // async function FetchData() { + // const result = await clientAxiosRequest({ + // headers: {}, + // apiEndpoint: API.getAPI({ + // environment, + // id: api?.id + // }), + // method: 'GET', + // data: null, + // noToast: true, + // }); + // setApiEndpoint(result?.data); + // } + + // useEffect(() => { + // api?.id && FetchData(); + // }, [api?.id]); useEffect(() => { const slug = updateSearchParams('slug', details?.name); @@ -134,8 +132,8 @@ const CollectionSection = ({ data: { "name": api?.name, "enabled": Boolean(openModal == 'activate'), - "url": api?.url, - "route": api?.route + "upstream": api?.upstream, + "downstream": api?.downstream } }); @@ -147,59 +145,59 @@ const CollectionSection = ({ } } - const handleApiConfiguration = (code: string, e?: FormEvent) => { - e && e.preventDefault(); - - if (profile?.user?.twofaEnabled && !code) { - setOpen2FA(true); - } else { - setLoading(true); - // TODO: GET CONFIGURATION ENDPOINT - } - } - - const handleApiModification = async (code: string, e?: FormEvent) => { - e && e.preventDefault(); - - if (profile?.user?.twofaEnabled && !code) { - setOpen2FA(true); - } else { - setLoading(true); - const result: any = await clientAxiosRequest({ - headers: code ? { 'X-TwoFA-Code' : code, } : {}, - apiEndpoint: API.updateAPI({ - environment, - id: api?.id - }), - method: 'PATCH', - data: { - "name": api?.name, - "enabled": true, - "url": endpoint_url, - "route": { - "paths": [ - parameters - ], - "methods": api?.route?.methods - } - } - }); - - setLoading(false); - if (result?.status == 200) { - close2FAModal(); - refreshData(); - router.refresh(); - } - } - } + // const handleApiConfiguration = (code: string, e?: FormEvent) => { + // e && e.preventDefault(); + + // if (profile?.user?.twofaEnabled && !code) { + // setOpen2FA(true); + // } else { + // setLoading(true); + // // TODO: GET CONFIGURATION ENDPOINT + // } + // } + + // const handleApiModification = async (code: string, e?: FormEvent) => { + // e && e.preventDefault(); + + // if (profile?.user?.twofaEnabled && !code) { + // setOpen2FA(true); + // } else { + // setLoading(true); + // const result: any = await clientAxiosRequest({ + // headers: code ? { 'X-TwoFA-Code' : code, } : {}, + // apiEndpoint: API.updateAPI({ + // environment, + // id: api?.id + // }), + // method: 'PATCH', + // data: { + // "name": api?.name, + // "enabled": true, + // "url": endpoint_url, + // "route": { + // "paths": [ + // parameters + // ], + // "methods": api?.route?.methods + // } + // } + // }); + + // setLoading(false); + // if (result?.status == 200) { + // close2FAModal(); + // refreshData(); + // router.refresh(); + // } + // } + // } const handle2FA = (value: string) => { - openModal == 'configure' ? - handleApiConfiguration(value, undefined) : - openModal == 'modify' ? - handleApiModification(value, undefined) : - handleActivateDeactivateDeleteApi(value); + // openModal == 'configure' ? + // handleApiConfiguration(value, undefined) : + // openModal == 'modify' ? + // handleApiModification(value, undefined) : + handleActivateDeactivateDeleteApi(value); }; const actionColumn = columnHelper.accessor('actions', { @@ -223,11 +221,13 @@ const CollectionSection = ({ onClick={() => { const api = rawData?.find(data => data?.id == row.original.id); setApi(api) - updateFields({ - endpoint_url: api?.url, - parameters: api?.route?.paths - }) - setOpenModal(action.name); + // updateFields({ + // endpoint_url: api?.url, + // parameters: api?.route?.paths + // }) + action.name == 'configure' ? + router.push(`/app/api-management/collections/${details?.id}/api-configuration?api_id=${row.original.id}`) : + setOpenModal(action.name); }} > {action.icon} @@ -245,7 +245,7 @@ const CollectionSection = ({ return ( <> - { + {/* { (openModal == 'configure' || openModal == 'modify') && } - } + } */} { ( diff --git a/apps/web/app/(webapp)/app/api-management/collections/(components)/DownStreamForm.tsx b/apps/web/app/(webapp)/app/api-management/collections/(components)/DownStreamForm.tsx new file mode 100644 index 00000000..5ca4b39e --- /dev/null +++ b/apps/web/app/(webapp)/app/api-management/collections/(components)/DownStreamForm.tsx @@ -0,0 +1,77 @@ +'use client' + +import { InputElement } from '@/components/forms' +import { APIConfigurationProps } from '@/types/webappTypes/appTypes'; +import React, { ChangeEvent, useEffect, useState } from 'react' + +const DownStreamForm = ({ + rawData +}: APIConfigurationProps) => { + + const [api_name, setApiName] = useState(''); + const [request_method, setRequestMethod] = useState(''); + const [tier, setTier] = useState(''); + const [paths, setPaths] = useState(''); + + console.log(rawData); + + useEffect(() => { + setApiName(rawData?.name || ''); + setRequestMethod(rawData?.downstream?.methods?.toString()); + setTier(''); + setPaths(rawData?.downstream?.paths?.toString()); + }, []); + + return ( +
+
+
+ Downstream Service +
+ +
+ + + + +
+
+
+ ) +} + +export default DownStreamForm \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/collections/(components)/TransformationForm.tsx b/apps/web/app/(webapp)/app/api-management/collections/(components)/TransformationForm.tsx new file mode 100644 index 00000000..1a63af58 --- /dev/null +++ b/apps/web/app/(webapp)/app/api-management/collections/(components)/TransformationForm.tsx @@ -0,0 +1,180 @@ +'use client' + +import { AppCenterModal, CodeEditor, ListPanel, TwoFactorAuthModal } from '@/app/(webapp)/(components)' +import { Button } from '@/components/globalComponents'; +import { API_CONFIGURATION_PANEL } from '@/data/collectionDatas'; +import clientAxiosRequest from '@/hooks/clientAxiosRequest'; +import { APIConfigurationProps } from '@/types/webappTypes/appTypes'; +import React, { useEffect, useState } from 'react'; +import * as API from '@/config/endpoints'; + +const TransformationForm = ({ + rawData, + profileData +}: APIConfigurationProps) => { + const paths = API_CONFIGURATION_PANEL; + const [request_body, setRequestBody] = useState(''); + const [response_body, setResponseBody] = useState(''); + const [request_header, setRequestHeader] = useState(''); + const [response_header, setResponseHeader] = useState(''); + + const [open2FA, setOpen2FA] = useState(false); + const [loading, setLoading] = useState(false); + const environment = 'development'; + const [path, setPath] = useState('request_body'); + + async function fetchAPITransformation() { + const result: any = await clientAxiosRequest({ + headers: {}, + apiEndpoint: API.getAPITransformation({ + environment, + id: rawData?.id + }), + method: 'GET', + data: null, + noToast: true + }); + + setResponseBody(result?.data?.downstream); + setRequestBody(result?.data?.upstream) + } + + useEffect(() => { + fetchAPITransformation(); + }, []); + + const incorrect = ( + !request_body || + !response_body + ) + + const close2FAModal = () => { + setOpen2FA(false); + } + + const handleSubmit = async (e: any, code: string,) => { + e && e.preventDefault(); + if (profileData?.user?.twofaEnabled && !code) { + setOpen2FA(true); + } else { + setLoading(true); + const result: any = await clientAxiosRequest({ + headers: code ? { 'X-TwoFA-Code' : code, } : {}, + apiEndpoint: API.updateAPITransformation({ + environment, + id: rawData?.id + }), + method: 'PUT', + data: { + "downstream": response_body, + "upstream": request_body, + } + }); + + if (result?.message) { + close2FAModal(); + setLoading(false); + // router.refresh(); + } + } + } + + // console.log( + // `request_body >>>> ${request_body}, `, + // `response_body >>>> ${response_body}, `, + // `request_header >>>> ${request_header}, `, + // `response_header >>>> ${response_header}, `, + // `path >>>> ${path}`) + + const handle2FA = (value: string) => { + handleSubmit('', value); + }; + + return ( + <> + { + open2FA && + + handle2FA(value)} + /> + + } + +
handleSubmit(e, '')} className='w-full'> +
+
+ Transformation +
+ +
+
+ +
+ + { + path == 'request_body' && +
+ +
+ } + + { + path == 'request_header' && +
+ +
+ } + + { + path == 'response_body' && +
+ +
+ } + + { + path == 'response_header' && +
+ +
+ } + +
+
+
+
+
+ + ) +} + +export default TransformationForm \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/collections/(components)/UpstreamForm.tsx b/apps/web/app/(webapp)/app/api-management/collections/(components)/UpstreamForm.tsx new file mode 100644 index 00000000..7925939d --- /dev/null +++ b/apps/web/app/(webapp)/app/api-management/collections/(components)/UpstreamForm.tsx @@ -0,0 +1,168 @@ +'use client' + +import { InputElement, ToggleSwitch } from '@/components/forms' +import { Button } from '@/components/globalComponents'; +import clientAxiosRequest from '@/hooks/clientAxiosRequest'; +import { APIConfigurationProps } from '@/types/webappTypes/appTypes'; +import React, { useState } from 'react' +import * as API from '@/config/endpoints'; +import { useRouter } from 'next/navigation'; +import { AppCenterModal, TwoFactorAuthModal } from '@/app/(webapp)/(components)'; + +const UpStreamForm = ({ + rawData, + profileData +}: APIConfigurationProps) => { + const [enable, setEnable] = useState(rawData?.enabled || false); + const [host, setHost] = useState(rawData?.upstream?.host || ''); + const [headerValue, setHeaderValue] = useState(''); + const [headerName, setHeaderName] = useState(''); + const [endpointUrl, setEndpointUrl] = useState(rawData?.upstream?.url || ''); + + const [open2FA, setOpen2FA] = useState(false); + const [loading, setLoading] = useState(false); + const environment = 'development'; + const router = useRouter(); + + const incorrect = ( + !host || + // !headerName || + // !headerValue || + !endpointUrl + ); + + const close2FAModal = () => { + setOpen2FA(false); + } + + const handleSubmit = async (e: any, code: string,) => { + e && e.preventDefault(); + if (profileData?.user?.twofaEnabled && !code) { + setOpen2FA(true); + } else { + setLoading(true); + const result: any = await clientAxiosRequest({ + headers: code ? { 'X-TwoFA-Code' : code, } : {}, + apiEndpoint: API.updateAPI({ + environment, + id: rawData?.id + }), + method: 'PATCH', + data: { + "name": rawData?.name, + "enabled": enable, + "upstream": { + ...rawData?.upstream, + host, + url: endpointUrl + }, + "downstream": rawData?.downstream + } + }); + + if (result?.message) { + close2FAModal(); + setLoading(false); + // router.refresh(); + } + } + } + + const handle2FA = (value: string) => { + handleSubmit('', value); + }; + + return ( + <> + { + open2FA && + + handle2FA(value)} + /> + + } + +
handleSubmit(e, '')} className='w-full'> +
+
+ Upstream Service +
+ +
+
+
+ Enable +
+ + +
+ + + + +
+ + + +
+ +
+
+
+
+
+ + ) +} + +export default UpStreamForm \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/collections/(components)/index.ts b/apps/web/app/(webapp)/app/api-management/collections/(components)/index.ts index 3b394708..03840501 100644 --- a/apps/web/app/(webapp)/app/api-management/collections/(components)/index.ts +++ b/apps/web/app/(webapp)/app/api-management/collections/(components)/index.ts @@ -6,6 +6,9 @@ import HostsContainer from "./HostsContainer"; import SnisContainer from "./SnisContainer"; import HeadersContainer from "./HeadersContainer"; import ActivateDeactivateDeleteApi from "./ActivateDeactivateDeleteApi"; +import DownStreamForm from "./DownStreamForm"; +import TransformationForm from "./TransformationForm"; +import UpstreamForm from "./UpstreamForm"; export { CollectionsTable, @@ -15,5 +18,8 @@ export { HostsContainer, SnisContainer, HeadersContainer, - ActivateDeactivateDeleteApi + ActivateDeactivateDeleteApi, + DownStreamForm, + TransformationForm, + UpstreamForm } \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/collections/[id]/api-configuration/page.tsx b/apps/web/app/(webapp)/app/api-management/collections/[id]/api-configuration/page.tsx new file mode 100644 index 00000000..616de3c5 --- /dev/null +++ b/apps/web/app/(webapp)/app/api-management/collections/[id]/api-configuration/page.tsx @@ -0,0 +1,56 @@ +import { UrlParamsProps } from '@/types/webappTypes/appTypes' +import React from 'react' +import { DownStreamForm, TransformationForm, UpstreamForm } from '../../(components)'; +import { applyAxiosRequest } from '@/hooks'; +import * as API from '@/config/endpoints'; +import Logout from '@/components/globalComponents/Logout'; + +const APIConfigurationPage = async({ params, searchParams }: UrlParamsProps) => { + const api_id = searchParams?.api_id || ''; + const environment = 'development'; + + const fetchedAPI: any = await applyAxiosRequest({ + headers: {}, + apiEndpoint: API.getAPI({ + environment, + id: api_id + }), + method: 'GET', + data: null + }) + + const fetchedProfile: any = await applyAxiosRequest({ + headers: {}, + apiEndpoint: API.getProfile(), + method: 'GET', + data: null + }); + + if (fetchedAPI?.status == 401) { + return + } + + let apiDetails = fetchedAPI?.data; + let profile = fetchedProfile?.data; + + return ( +
+

+ API Configuration +

+ + + + +
+ ) +} + +export default APIConfigurationPage \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/collections/[id]/page.tsx b/apps/web/app/(webapp)/app/api-management/collections/[id]/page.tsx index 6aef3505..252ecbae 100644 --- a/apps/web/app/(webapp)/app/api-management/collections/[id]/page.tsx +++ b/apps/web/app/(webapp)/app/api-management/collections/[id]/page.tsx @@ -55,11 +55,11 @@ const CollectionPage = async ({ params, searchParams }: UrlParamsProps) => { return({ ...endpoint, api_name: endpoint?.name, - request_method: endpoint?.route?.methods?.toString(), + request_method: endpoint?.downstream?.methods?.toString(), configured: endpoint?.enabled, - endpoint_url: endpoint?.url, + endpoint_url: endpoint?.upstream?.url, tier: '', - parameters: endpoint?.route?.paths?.toString(), + parameters: endpoint?.downstream?.paths?.toString(), }); }) diff --git a/apps/web/app/(webapp)/app/api-management/consumers/(components)/AddPermissionButton.tsx b/apps/web/app/(webapp)/app/api-management/consumers/(components)/AddPermissionButton.tsx new file mode 100644 index 00000000..52812d4c --- /dev/null +++ b/apps/web/app/(webapp)/app/api-management/consumers/(components)/AddPermissionButton.tsx @@ -0,0 +1,139 @@ +'use client' + +import { Button } from '@/components/globalComponents' +import clientAxiosRequest from '@/hooks/clientAxiosRequest'; +import { useRouter } from 'next/navigation'; +import React, { FormEvent, useEffect, useState } from 'react'; +import * as API from '@/config/endpoints'; +import { AppCenterModal, AppRightModal, TwoFactorAuthModal } from '@/app/(webapp)/(components)'; +import { AddAPIPermissions } from '.'; + +const AddPermissionButton = ({ + searchQuery, + customerId +}: { + searchQuery: string, + customerId: string +}) => { + const [openModal, setOpenModal] = useState(false); + const [open2FA, setOpen2FA] = useState(false); + const [loading, setLoading] = useState(false); + const [profile, setProfile] = useState(null); + const router = useRouter(); + const [collections, setCollections] = useState([]); + const [apiIds, setApiIds] = useState([]); + const environment = 'development'; + + const fetchAPICollections = async () => { + const result: any = await clientAxiosRequest({ + headers: {}, + apiEndpoint: API.getCollections(), + method: 'GET', + data: null, + noToast: true + }); + setCollections(result?.data); + } + + const fetchProfile = async () => { + const result: any = await clientAxiosRequest({ + headers: {}, + apiEndpoint: API.getProfile(), + method: 'GET', + data: null, + noToast: true + }); + setProfile(result?.data); + } + + useEffect(() => { + fetchProfile(); + fetchAPICollections(); + }, []); + + const close2FAModal = () => { + setOpen2FA(false); + setOpenModal(false); + }; + + const refreshData = () => { + setApiIds([]); + } + + const handleAddPermission = async (code: string, e?: FormEvent) => { + e && e.preventDefault(); + + if (profile?.user?.twofaEnabled && !code) { + setOpen2FA(true); + } else { + setLoading(true); + + let sanitizedApiIds = apiIds?.map((item: any) => item.id); + + const result: any = await clientAxiosRequest({ + headers: code ? { 'X-TwoFA-Code' : code, } : {}, + apiEndpoint: API.postAssignAPIs({ + environment, + id: customerId + }), + method: 'POST', + data: { apiIds: sanitizedApiIds } + }); + + setLoading(false); + if (result?.status == 201) { + close2FAModal(); + refreshData(); + router.refresh(); + } + } + } + + return ( + <> + { + openModal && + setOpenModal(false)} + childrenStyle='!px-0 !py-0 !pt-[20px]' + > + setOpenModal(false)} + data={collections} + next={handleAddPermission} + searchQuery={searchQuery} + loading={loading} + api_ids={apiIds} + setApiIds={setApiIds} + /> + + } + + { + open2FA && + + handleAddPermission(value, undefined)} + /> + + } + +
+
+ + ) +} + +export default AddPermissionButton \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/consumers/(components)/ConsumerSections.tsx b/apps/web/app/(webapp)/app/api-management/consumers/(components)/ConsumerSections.tsx index c662c3ba..c1f3d9da 100644 --- a/apps/web/app/(webapp)/app/api-management/consumers/(components)/ConsumerSections.tsx +++ b/apps/web/app/(webapp)/app/api-management/consumers/(components)/ConsumerSections.tsx @@ -304,8 +304,10 @@ const ConsumerSections = ({ title='Nothing to show' type='DEFAULT' parentStyle='!h-[calc(100vh-600px)]' + altData={altData?.id} body='There’s no information to show for this user yet.' button={path == ''} + searchQuery={filters[0]} buttonType='ADD_PERMISSIONS' /> } diff --git a/apps/web/app/(webapp)/app/api-management/consumers/(components)/index.ts b/apps/web/app/(webapp)/app/api-management/consumers/(components)/index.ts index 64df0f35..ffe78b29 100644 --- a/apps/web/app/(webapp)/app/api-management/consumers/(components)/index.ts +++ b/apps/web/app/(webapp)/app/api-management/consumers/(components)/index.ts @@ -11,6 +11,7 @@ import EditPermissionButton from "./EditPermissionButton"; import AddAPIPermissions from "./AddAPIPermissions"; import ApiPermissionCard from "./ApiPermissionCard"; import ApiPermissionSelector from "./ApiPermissionSelector"; +import AddPermissionButton from "./AddPermissionButton"; export { ConsumersTable, @@ -25,5 +26,6 @@ export { EditPermissionButton, AddAPIPermissions, ApiPermissionCard, - ApiPermissionSelector + ApiPermissionSelector, + AddPermissionButton } \ No newline at end of file diff --git a/apps/web/app/(webapp)/app/api-management/consumers/[id]/page.tsx b/apps/web/app/(webapp)/app/api-management/consumers/[id]/page.tsx index 8ff647df..f314d007 100644 --- a/apps/web/app/(webapp)/app/api-management/consumers/[id]/page.tsx +++ b/apps/web/app/(webapp)/app/api-management/consumers/[id]/page.tsx @@ -84,15 +84,15 @@ const ConsumerPage = async ({ params, searchParams }: UrlParamsProps) => { }); const consents = CONSUMER_CONSENTS; - const collections = CONSUMER_COLLECTIONS_FULLDATA; - // const collections = fetchedCollection?.data?.map((collection: any) => { - // return({ - // ...collection, - // collection_name: collection?.name, - // endpoints: collection?.endpoints, - // categories: collection?.categories - // }) - // }); + // const collections = CONSUMER_COLLECTIONS_FULLDATA; + const collections = fetchedCollection?.data?.map((collection: any) => { + return({ + ...collection, + collection_name: collection?.name, + endpoints: collection?.endpoints, + categories: collection?.categories + }) + }); const filters = [search_query, status, date_sent] diff --git a/apps/web/config/endpoints.ts b/apps/web/config/endpoints.ts index b35bdf09..0c46435d 100644 --- a/apps/web/config/endpoints.ts +++ b/apps/web/config/endpoints.ts @@ -86,6 +86,10 @@ export const postUnassignAPIs = ({ environment, id }: GetSingleEnvironmentProps) `${BASE_URL}/apis/${environment}/company/${id}/unassign`; export const getCompanyAPIs = ({ page, limit, environment, companyId }: GetEnvironmentProps) => `${BASE_URL}/apis/${environment}/company/${companyId}?page=${page}${limit ? `&limit=${limit}`: ''}`; +export const updateAPITransformation = ({ environment, id }: GetSingleEnvironmentProps) => + `${BASE_URL}/apis/${environment}/${id}/transformation`; +export const getAPITransformation = ({ environment, id }: GetSingleEnvironmentProps) => + `${BASE_URL}/apis/${environment}/${id}/transformation`; // COMPANY export const getCompanies = ({ page, limit, name, createdAt_gt, createdAt_l, status }: GetListProps) => diff --git a/apps/web/data/collectionDatas.tsx b/apps/web/data/collectionDatas.tsx index 7627dd79..5762424e 100644 --- a/apps/web/data/collectionDatas.tsx +++ b/apps/web/data/collectionDatas.tsx @@ -17,6 +17,29 @@ export const COLLECTIONS_TABLE_HEADERS = [ } ]; +export const API_CONFIGURATION_PANEL = [ + { + id: 1, + label: 'Request Body', + value: 'request_body' + }, + { + id: 2, + label: 'Response Body', + value: 'response_body' + }, + { + id: 3, + label: 'Request Header', + value: 'request_header' + }, + { + id: 4, + label: 'Response Header', + value: 'response_header' + } +]; + export const DASHBOARD_API_HEADERS = [ { header: 'API Name', @@ -74,31 +97,31 @@ export const COLLECTION_ACTIONS_DATA = [ }, - // { - // id: 2, - // label: 'Configure', - // name: 'configure', - // type: 'not_configured', - // icon: - // - // - // }, { - id: 3, - label: 'Modify', - name: 'modify', + id: 2, + label: 'Configure', + name: 'configure', type: 'all', icon: - - - - - - - - + }, + // { + // id: 3, + // label: 'Modify', + // name: 'modify', + // type: 'all', + // icon: + // + // + // + // + // + // + // + // + // + // }, { id: 4, label: 'Deactivate', diff --git a/apps/web/package.json b/apps/web/package.json index cd909233..eac8adf4 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -24,6 +24,7 @@ "react-chartjs-2": "^5.2.0", "react-dom": "^18", "react-icons": "^4.11.0", + "react-monaco-editor": "^0.55.0", "react-tailwindcss-datepicker": "^1.6.6", "react-toastify": "^9.1.3", "xlsx": "^0.18.5", diff --git a/apps/web/types/webappTypes/appTypes.ts b/apps/web/types/webappTypes/appTypes.ts index e32d3c21..a1e64045 100644 --- a/apps/web/types/webappTypes/appTypes.ts +++ b/apps/web/types/webappTypes/appTypes.ts @@ -27,6 +27,7 @@ export interface searchParamsProps { two_fa?: string; token?: string; alt_data?: any; + api_id?: string; } export interface ParamsProps { @@ -262,6 +263,11 @@ export interface APIConfigurationContainerProps { handleAdd: (type: string) => void; } +export interface APIConfigurationProps { + rawData: any; + profileData?: any; +} + export interface CreateRoleButtonProps { permissions_list: any; } diff --git a/apps/web/types/webappTypes/componentsTypes.ts b/apps/web/types/webappTypes/componentsTypes.ts index 5674398e..9a893c41 100644 --- a/apps/web/types/webappTypes/componentsTypes.ts +++ b/apps/web/types/webappTypes/componentsTypes.ts @@ -53,8 +53,10 @@ export interface EmptyStateProps { title: string; body: string; parentStyle?: string; + altData?: any; titleStyle?: string; bodyStyle?: string; + searchQuery?: string; iconStyle?: string; containerStyle?: string; button?: boolean; @@ -82,6 +84,10 @@ export interface PanelContainerProps { status?: string; } +export interface ListPanelContainerProps extends PanelContainerProps{ + setCurrentValue: Dispatch>; +} + export interface TopPanelContainerProps extends PanelContainerProps{ name: string; } @@ -142,6 +148,11 @@ export interface CodeSnippetProps { codeContainerStyle?: string; } +export interface CodeEditorProps { + code: any; + setCode: Dispatch>; +} + export interface RequestMethodTextProps { method: any; styles?: any; diff --git a/apps/web/utils/getObjectNotInArray.ts b/apps/web/utils/getObjectNotInArray.ts new file mode 100644 index 00000000..e107fb90 --- /dev/null +++ b/apps/web/utils/getObjectNotInArray.ts @@ -0,0 +1,7 @@ +export const getObjectsNotInArrayB = (arrayA: any[], arrayB: any[]) => { + const result = arrayA.filter((objA) => { + return !arrayB.some((objB) => objB.id === objA.id); + }); + + return result; +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91d71e3b..96c45686 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -215,6 +215,9 @@ importers: react-icons: specifier: ^4.11.0 version: 4.11.0(react@18.2.0) + react-monaco-editor: + specifier: ^0.55.0 + version: 0.55.0(@types/react@18.2.28)(monaco-editor@0.44.0)(react@18.2.0) react-tailwindcss-datepicker: specifier: ^1.6.6 version: 1.6.6(dayjs@1.11.10)(react@18.2.0) @@ -1704,7 +1707,6 @@ packages: /@types/prop-types@15.7.8: resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==} - dev: true /@types/qs@6.9.8: resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==} @@ -1726,11 +1728,9 @@ packages: '@types/prop-types': 15.7.8 '@types/scheduler': 0.16.4 csstype: 3.1.2 - dev: true /@types/scheduler@0.16.4: resolution: {integrity: sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==} - dev: true /@types/semver@7.5.3: resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} @@ -2957,7 +2957,6 @@ packages: /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} - dev: true /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -5600,6 +5599,10 @@ packages: resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} dev: false + /monaco-editor@0.44.0: + resolution: {integrity: sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q==} + dev: false + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -6269,7 +6272,6 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: true /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} @@ -6384,12 +6386,24 @@ packages: /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true + /react-monaco-editor@0.55.0(@types/react@18.2.28)(monaco-editor@0.44.0)(react@18.2.0): + resolution: {integrity: sha512-GdEP0Q3Rn1dczfKEEyY08Nes5plWwIYU4sWRBQO0+jsQWQsKMHKCC6+hPRwR7G/4aA3V/iU9jSmWPzVJYMVFSQ==} + peerDependencies: + '@types/react': '>=16 <= 18' + monaco-editor: ^0.44.0 + react: '>=16 <= 18' + dependencies: + '@types/react': 18.2.28 + monaco-editor: 0.44.0 + prop-types: 15.8.1 + react: 18.2.0 + dev: false + /react-tailwindcss-datepicker@1.6.6(dayjs@1.11.10)(react@18.2.0): resolution: {integrity: sha512-kHSUonRO86PoYQQWPqpaSw2JeEn9OafdLsLBG85zO5Sfs23Ku8Ixt/YO+Is3TCBcFeOKnZgzhGLmP3NAXVlFkA==} peerDependencies: