diff --git a/package.json b/package.json index 9841f293..327ae914 100644 --- a/package.json +++ b/package.json @@ -33,14 +33,15 @@ "js-cookie": "^3.0.5", "just-compare": "^2.3.0", "long": "^5.2.3", - "nice-grpc-common": "^2.0.2", "ms": "^2.1.3", + "nice-grpc-common": "^2.0.2", "nice-grpc-web": "^3.3.2", "protobufjs": "^7.2.5", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.10", + "react-query": "^3.39.3", "react-resizable-panels": "^0.0.53", "react-router-dom": "^6.14.0", "react-window": "^1.8.9" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89bcca13..a1eced9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ dependencies: react-error-boundary: specifier: ^4.0.10 version: 4.0.10(react@18.2.0) + react-query: + specifier: ^3.39.3 + version: 3.39.3(react-dom@18.2.0)(react@18.2.0) react-resizable-panels: specifier: ^0.0.53 version: 0.0.53(react-dom@18.2.0)(react@18.2.0) @@ -1459,14 +1462,17 @@ packages: /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + + /big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + dev: false /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -1475,6 +1481,19 @@ packages: fill-range: 7.0.1 dev: true + /broadcast-channel@3.7.0: + resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} + dependencies: + '@babel/runtime': 7.21.5 + detect-node: 2.1.0 + js-sha3: 0.8.0 + microseconds: 0.2.0 + nano-time: 1.0.0 + oblivious-set: 1.0.0 + rimraf: 3.0.2 + unload: 2.2.0 + dev: false + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -1570,7 +1589,6 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -1657,6 +1675,10 @@ packages: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false + /detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + dev: false + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2190,7 +2212,6 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} @@ -2269,7 +2290,6 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true /globals@13.20.0: resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} @@ -2394,11 +2414,9 @@ packages: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} @@ -2563,6 +2581,10 @@ packages: engines: {node: '>=14'} dev: false + /js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2669,6 +2691,13 @@ packages: yallist: 4.0.0 dev: true + /match-sorter@6.3.1: + resolution: {integrity: sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==} + dependencies: + '@babel/runtime': 7.21.5 + remove-accents: 0.4.2 + dev: false + /memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} dev: false @@ -2690,6 +2719,10 @@ packages: picomatch: 2.3.1 dev: true + /microseconds@0.2.0: + resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} + dev: false + /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2711,7 +2744,6 @@ packages: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 - dev: true /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -2744,6 +2776,12 @@ packages: minimatch: 3.1.2 dev: true + /nano-time@1.0.0: + resolution: {integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==} + dependencies: + big-integer: 1.6.52 + dev: false + /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2839,11 +2877,14 @@ packages: es-abstract: 1.21.2 dev: true + /oblivious-set@1.0.0: + resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: true /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -2928,7 +2969,6 @@ packages: /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - dev: true /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} @@ -3097,6 +3137,25 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: false + /react-query@3.39.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.21.5 + broadcast-channel: 3.7.0 + match-sorter: 6.3.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react-redux@7.2.9(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} peerDependencies: @@ -3271,6 +3330,10 @@ packages: functions-have-names: 1.2.3 dev: true + /remove-accents@0.4.2: + resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==} + dev: false + /repeat-string@1.6.1: resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} @@ -3307,7 +3370,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: true /rollup@3.23.0: resolution: {integrity: sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==} @@ -3597,6 +3659,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unload@2.2.0: + resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} + dependencies: + '@babel/runtime': 7.21.5 + detect-node: 2.1.0 + dev: false + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -3752,7 +3821,6 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true /ws@8.16.0: resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} diff --git a/src/api/axios.ts b/src/api/axios.ts index 2d630c2a..5a911213 100644 --- a/src/api/axios.ts +++ b/src/api/axios.ts @@ -2,7 +2,7 @@ import axios from 'axios'; const baseURL = import.meta.env.VITE_PARSEABLE_URL ?? '/'; -const instance = axios.create({ baseURL, validateStatus: () => true, withCredentials: true }); +const instance = axios.create({ baseURL, withCredentials: true }); instance.interceptors.request.use( (request) => { diff --git a/src/api/logStream.ts b/src/api/logStream.ts index 12ad545e..f2f9910d 100644 --- a/src/api/logStream.ts +++ b/src/api/logStream.ts @@ -22,7 +22,7 @@ export const getLogStreamAlerts = (streamName: string) => { }; export const putLogStreamAlerts = (streamName: string, data: any) => { - return Axios().put(LOG_STREAMS_ALERTS_URL(streamName), data); + return Axios().put(LOG_STREAMS_ALERTS_URL(streamName), JSON.parse(data)); }; export const getLogStreamRetention = (streamName: string) => { @@ -30,7 +30,7 @@ export const getLogStreamRetention = (streamName: string) => { }; export const putLogStreamRetention = (streamName: string, data: any) => { - return Axios().put(LOG_STREAMS_RETRNTION_URL(streamName), data); + return Axios().put(LOG_STREAMS_RETRNTION_URL(streamName), JSON.parse(data)); }; export const getLogStreamStats = (streamName: string) => { @@ -39,4 +39,4 @@ export const getLogStreamStats = (streamName: string) => { export const deleteLogStream = (streamName: string) => { return Axios().delete(DELETE_STREAMS_URL(streamName)); -} +}; diff --git a/src/components/Mantine/theme.tsx b/src/components/Mantine/theme.tsx index 1f29a6cb..18cc3bb1 100644 --- a/src/components/Mantine/theme.tsx +++ b/src/components/Mantine/theme.tsx @@ -224,19 +224,30 @@ export const theme: MantineThemeOverride = { color: colors.white[0], }, }; - } + }, }, - PasswordInput: { styles: ({ colors }) => { return { input: { border: `${sizing.px} ${colors.gray[2]} solid`, borderRadius: 'md', - }, }; - } - } + }, + }, + Switch: { + styles: () => { + return { + track: { + border: 'none', + cursor: 'pointer', + }, + thumb: { + border: 'none', + }, + }; + }, + }, }, }; diff --git a/src/hooks/useAlertsEditor.tsx b/src/hooks/useAlertsEditor.tsx new file mode 100644 index 00000000..87a53eb1 --- /dev/null +++ b/src/hooks/useAlertsEditor.tsx @@ -0,0 +1,90 @@ +import { useMutation, useQuery } from 'react-query'; +import { getLogStreamAlerts, putLogStreamAlerts } from '@/api/logStream'; +import { IconCheck, IconFileAlert } from '@tabler/icons-react'; +import { notifyApi } from '@/utils/notification'; +import { notifications } from '@mantine/notifications'; +import useMountedState from './useMountedState'; +import { isAxiosError, AxiosError } from 'axios'; + +export const useAlertsEditor = (streamName: string) => { + const [alertQuery, setAlertQuery] = useMountedState(''); + + const { + mutate: updateLogStreamAlerts, + data: alertQueryData, + isSuccess: updateLogStreamIsSuccess, + } = useMutation((data: string) => putLogStreamAlerts(streamName, data), { + onSuccess: () => { + notifyApi({ + color: 'green', + message: 'Alert Added.', + icon: , + }); + }, + onError: (data: AxiosError) => { + if (isAxiosError(data) && data.response) { + const error = data.response.data; + notifyApi( + { + color: 'red', + title: 'Error occurred', + message: `Error occurred, please check your query and try again ${error}`, + icon: , + autoClose: 3000, + }, + true, + ); + } + }, + }); + + const { data: getLogAlertData } = useQuery( + ['fetch-log-stream-alert', streamName, updateLogStreamIsSuccess], + () => getLogStreamAlerts(streamName), + { + onError: () => { + notifyApi({ + color: 'red', + message: 'Failed to get log streams alert', + icon: , + }); + }, + onSuccess: (data) => { + setAlertQuery(JSON.stringify(data?.data)); + }, + retry: false, + enabled: streamName !== '', + }, + ); + + const handleAlertQueryChange = (value: string | undefined) => setAlertQuery(value); + + const submitAlertQuery = () => { + try { + if (alertQuery) { + JSON.parse(alertQuery); + updateLogStreamAlerts(alertQuery); + } else { + throw new Error('Invalid JSON'); + } + } catch (e) { + notifications.show({ + id: 'load-data', + loading: false, + color: 'red', + title: 'Error occurred', + message: `Error occurred, please check your query and try again ${e}`, + icon: , + autoClose: 3000, + }); + } + }; + + return { + alertQuery, + handleAlertQueryChange, + submitAlertQuery, + alertQueryData, + getLogAlertData, + }; +}; diff --git a/src/hooks/useCacheToggle.tsx b/src/hooks/useCacheToggle.tsx new file mode 100644 index 00000000..20865dfd --- /dev/null +++ b/src/hooks/useCacheToggle.tsx @@ -0,0 +1,44 @@ +import { useMutation, useQuery } from 'react-query'; +import { getCachingStatus, updateCaching } from '@/api/caching'; +import { IconCheck, IconFileAlert } from '@tabler/icons-react'; +import { notifyApi } from '@/utils/notification'; + +export const useCacheToggle = (streamName: string) => { + const { mutate: updateCacheStatus, isSuccess: updateCacheIsSuccess } = useMutation( + ({ type }: { type: boolean }) => updateCaching(streamName, type), + { + onError: () => { + notifyApi({ + color: 'red', + message: 'Failed to change cache setting', + icon: , + }); + }, + onSuccess: () => { + notifyApi({ + color: 'green', + message: 'Succesfully updated cache setting', + icon: , + }); + }, + }, + ); + + const { data: checkCacheData } = useQuery( + ['fetch-cache-status', streamName, updateCacheIsSuccess], + () => getCachingStatus(streamName), + { + retry: false, + enabled: streamName !== '', + }, + ); + + const handleCacheToggle = () => { + updateCacheStatus({ type: !checkCacheData?.data }); + }; + + return { + isCacheEnabled: checkCacheData?.data, + handleCacheToggle, + }; +}; diff --git a/src/hooks/usePutLogStreamRetention.ts b/src/hooks/usePutLogStreamRetention.ts index 3c85567b..7b9071cd 100644 --- a/src/hooks/usePutLogStreamRetention.ts +++ b/src/hooks/usePutLogStreamRetention.ts @@ -9,7 +9,7 @@ export const usePutLogStreamRetention = () => { const [error, setError] = useMountedState(null); const [loading, setLoading] = useMountedState(false); - const putRetentionData = async (streamName :string, payload:any) => { + const putRetentionData = async (streamName: string, payload: any) => { try { setLoading(true); setError(null); @@ -36,4 +36,4 @@ export const usePutLogStreamRetention = () => { }; return { data, error, loading, putRetentionData, resetData }; -}; \ No newline at end of file +}; diff --git a/src/hooks/useRetentionEditor.tsx b/src/hooks/useRetentionEditor.tsx new file mode 100644 index 00000000..55d21bf0 --- /dev/null +++ b/src/hooks/useRetentionEditor.tsx @@ -0,0 +1,90 @@ +import { useMutation, useQuery } from 'react-query'; +import { getLogStreamRetention, putLogStreamRetention } from '@/api/logStream'; +import { notifyApi } from '@/utils/notification'; +import { IconCheck, IconFileAlert } from '@tabler/icons-react'; +import { notifications } from '@mantine/notifications'; +import useMountedState from './useMountedState'; +import { isAxiosError, AxiosError } from 'axios'; + +export const useRetentionEditor = (streamName: string) => { + const [retentionQuery, setRetentionQuery] = useMountedState(''); + + const { + mutate: updateLogStreamRetention, + data: retentionEditorData, + isSuccess: updateLogRetentionIsSuccess, + } = useMutation((data: string) => putLogStreamRetention(streamName, data), { + onSuccess: () => { + notifyApi({ + color: 'green', + message: 'Retention Added.', + icon: , + }); + }, + onError: (data: AxiosError) => { + if (isAxiosError(data) && data.response) { + const error = data.response.data; + notifyApi( + { + color: 'red', + title: 'Error occurred', + message: `Error occurred, please check your query and try again ${error}`, + icon: , + autoClose: 3000, + }, + true, + ); + } + }, + }); + + const { data: getLogRetentionData } = useQuery( + ['fetch-log-stream-retention', streamName, updateLogRetentionIsSuccess], + () => getLogStreamRetention(streamName), + { + onError: () => { + notifyApi({ + color: 'red', + message: 'Failed to get log streams retention', + icon: , + }); + }, + onSuccess: (data) => { + setRetentionQuery(JSON.stringify(data?.data)); + }, + retry: false, + enabled: streamName !== '', + }, + ); + + const handleRetentionQueryChange = (value: string | undefined) => setRetentionQuery(value); + + const submitRetentionQuery = () => { + try { + if (retentionQuery) { + JSON.parse(retentionQuery); + updateLogStreamRetention(retentionQuery); + } else { + throw new Error('Invalid JSON'); + } + } catch (e) { + notifications.show({ + id: 'load-data', + loading: false, + color: 'red', + title: 'Error occurred', + message: `Error occurred, please check your query and try again ${e}`, + icon: , + autoClose: 3000, + }); + } + }; + + return { + retentionQuery, + handleRetentionQueryChange, + submitRetentionQuery, + retentionEditorData, + getLogRetentionData, + }; +}; diff --git a/src/main.tsx b/src/main.tsx index 26e93068..259cde75 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,15 +5,20 @@ import App from '@/components/App'; import Mantine from '@/components/Mantine'; import { BrowserRouter } from 'react-router-dom'; import ErrorBoundary from './components/ErrorBoundary'; +import { QueryClient, QueryClientProvider } from 'react-query'; + +const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - - - - - + + + + + + + + + , ); diff --git a/src/pages/Config/index.tsx b/src/pages/Config/index.tsx index 72def4b8..381837bb 100644 --- a/src/pages/Config/index.tsx +++ b/src/pages/Config/index.tsx @@ -1,253 +1,55 @@ -import { Accordion, Box, Button } from '@mantine/core'; +import { Accordion, Box, Button, Switch } from '@mantine/core'; import { useDocumentTitle } from '@mantine/hooks'; import Editor from '@monaco-editor/react'; import { FC, useEffect } from 'react'; import { useConfigStyles } from './styles'; import useMountedState from '@/hooks/useMountedState'; -import { usePutLogStreamAlerts } from '@/hooks/usePutLogStreamAlerts'; import { useHeaderContext } from '@/layouts/MainLayout/Context'; -import { notifications } from '@mantine/notifications'; -import { IconCheck, IconFileAlert } from '@tabler/icons-react'; -import { usePutLogStreamRetention } from '@/hooks/usePutLogStreamRetention'; -import { useGetLogStreamAlert } from '@/hooks/useGetLogStreamAlert'; -import { useGetLogStreamRetention } from '@/hooks/useGetLogStreamRetention'; -import { useGetCacheStatus } from '@/hooks/useGetCacheStatus'; -import { usePutCache } from '@/hooks/usePutCache'; +import { useCacheToggle } from '@/hooks/useCacheToggle'; +import { useAlertsEditor } from '@/hooks/useAlertsEditor'; +import { useRetentionEditor } from '@/hooks/useRetentionEditor'; const Config: FC = () => { useDocumentTitle('Parseable | Config'); - const { data, getCacheStatus } = useGetCacheStatus(); - usePutCache(); - const { error: updateCacheError, updateCache } = usePutCache(); - - const [alertQuery, setAlertQuery] = useMountedState(''); - const [retentionQuery, setRetentionQuery] = useMountedState(''); - const { state: { subLogQuery }, } = useHeaderContext(); - const { - data: resultAlertsData, - error: alertError, - loading: alertLoading, - putAlertsData, - resetData: resetAlertData, - } = usePutLogStreamAlerts(); - const { - data: resultRetentionData, - error: retentionError, - loading: retentionLoading, - putRetentionData, - resetData: resetRetentionData, - } = usePutLogStreamRetention(); - - // useGetLogStreamAlert - - const { - data: intialAlert, - error: intialAlertError, - loading: intialAlertLoading, - getLogAlert, - resetData: resetIntailAlertData, - } = useGetLogStreamAlert(); - - const { - data: intialRetention, - error: intialRetentionError, - loading: intialRetentionLoading, - getLogRetention, - resetData: ResetIntialRetentionData, - } = useGetLogStreamRetention(); - - const handleAlertQueryEditorChange = (code: any) => { - setAlertQuery(code); - }; - const handleRetentionQueryEditorChange = (code: any) => { - setRetentionQuery(code); - }; - const onSubmitAlertQuery = () => { - let alertQueryObj = {}; - try { - alertQueryObj = JSON.parse(alertQuery); - } catch (e) { - notifications.show({ - id: 'load-data', - loading: false, - color: 'red', - title: 'Error occurred', - message: `Error occurred, please check your query and try again ${e}`, - icon: , - autoClose: 3000, - }); - return; - } - putAlertsData(subLogQuery.get().streamName, alertQueryObj); - }; - const onSubmitRetentionQuery = () => { - let retentionQueryObj = {}; - try { - retentionQueryObj = JSON.parse(retentionQuery); - } catch (e) { - notifications.show({ - id: 'load-data', - loading: false, - color: 'red', - title: 'Error occurred', - message: `Error occurred, please check your query and try again ${e}`, - icon: , - autoClose: 3000, - }); - return; - } - putRetentionData(subLogQuery.get().streamName, retentionQueryObj); - }; - - useEffect(() => { - if (!subLogQuery.get().streamName) return; - getLogAlert(subLogQuery.get().streamName); - getLogRetention(subLogQuery.get().streamName); - getCacheStatus(subLogQuery.get().streamName); - }, [subLogQuery.get().streamName]); + const [streamName, setStreamName] = useMountedState(subLogQuery.get().streamName ?? ''); useEffect(() => { const subQuery = subLogQuery.subscribe((value: any) => { - if (intialAlert) { - resetIntailAlertData(); - setAlertQuery(''); - } - if (intialRetention) { - ResetIntialRetentionData(); - setRetentionQuery(''); - } - getLogAlert(value.streamName); - getLogRetention(value.streamName); + setStreamName(value.streamName); }); return () => { subQuery(); }; - }, []); - - useEffect(() => { - if (intialAlertLoading) return; - if (intialAlertError) return; - if (intialAlert) { - setAlertQuery(JSON.stringify(intialAlert, null, 2)); - } - }, [intialAlert, intialAlertError, intialAlertLoading]); + }, [subLogQuery]); - useEffect(() => { - if (intialRetentionLoading) return; - if (intialRetentionError) return; - if (intialRetention) { - setRetentionQuery(JSON.stringify(intialRetention, null, 2)); - } - }, [intialRetention, intialRetentionError, intialRetentionLoading]); + const { handleCacheToggle, isCacheEnabled } = useCacheToggle(streamName); - useEffect(() => { - if (alertLoading) { - notifications.show({ - id: 'load-data', - loading: true, - color: '#545BEB', - title: 'Running Query', - message: 'Alert will be Added.', - autoClose: false, - withCloseButton: false, - }); - } - if (resultAlertsData) { - notifications.update({ - id: 'load-data', - loading: false, - color: 'green', - title: 'Success', - message: 'Alert Added.', - icon: , - autoClose: 1000, - }); - resetAlertData(); - } - if (alertError) { - notifications.update({ - id: 'load-data', - loading: false, - color: 'red', - title: 'Error occurred', - message: `Error occurred, please check your query and try again ${alertError}`, - icon: , - autoClose: 3000, - }); - resetAlertData(); - } - }, [resultAlertsData, alertError, alertLoading]); + const { handleAlertQueryChange, submitAlertQuery, getLogAlertData } = useAlertsEditor(streamName); - useEffect(() => { - if (retentionLoading) { - notifications.show({ - id: 'load-data', - loading: true, - color: '#545BEB', - title: 'Running Query', - message: 'Retention will be Added.', - autoClose: false, - withCloseButton: false, - }); - } - if (resultRetentionData) { - notifications.update({ - id: 'load-data', - loading: false, - color: 'green', - title: 'Success', - message: 'Retention Added.', - icon: , - autoClose: 1000, - }); - resetRetentionData(); - } - if (retentionError) { - notifications.update({ - id: 'load-data', - loading: false, - color: 'red', - title: 'Error occurred', - message: `Error occurred, please check your query and try again ${retentionError}`, - icon: , - autoClose: 3000, - }); - resetRetentionData(); - } - if (updateCacheError) { - notifications.update({ - id: 'load-data', - loading: false, - color: 'red', - title: 'Error occurred', - message: `Error occurred, failed to change cache setting`, - icon: , - autoClose: 3000, - }); - } - }, [resultRetentionData, retentionError, retentionLoading, updateCacheError]); + const { handleRetentionQueryChange, submitRetentionQuery, getLogRetentionData } = useRetentionEditor(streamName); const { classes } = useConfigStyles(); - const { container, submitBtn, accordionSt, innerContainer, containerWrapper, primaryBtn } = classes; + const { container, submitBtn, accordionSt, innerContainer, containerWrapper, trackStyle } = classes; + + const switchStyles = { + track: isCacheEnabled ? trackStyle : {}, + }; return ( - - + @@ -257,8 +59,8 @@ const Config: FC = () => { { }} /> - @@ -289,8 +91,8 @@ const Config: FC = () => { { }} /> - diff --git a/src/pages/Config/styles.tsx b/src/pages/Config/styles.tsx index 50400afd..439e0bac 100644 --- a/src/pages/Config/styles.tsx +++ b/src/pages/Config/styles.tsx @@ -10,6 +10,9 @@ export const useConfigStyles = createStyles((theme) => { margin: spacing.xl, gap: spacing.lg, }, + trackStyle: { + backgroundColor: colors.brandPrimary[0], + }, containerWrapper: { display: 'flex', gap: '20px', diff --git a/src/utils/notification.ts b/src/utils/notification.ts index cb4d6be5..9312294e 100644 --- a/src/utils/notification.ts +++ b/src/utils/notification.ts @@ -24,3 +24,14 @@ export const notify = (payload: NotificationProps) => { autoClose, }); }; + +export const notifyApi = (payload: NotificationProps, customTitle: boolean = true) => { + const autoClose = payload.autoClose ?? 3000; + const title = customTitle && payload.color === 'green' ? 'Success' : 'Error'; + + showNotification({ + ...payload, + autoClose, + title, + }); +};