diff --git a/frontend/src/component/application/application-view.jsx b/frontend/src/component/application/ApplicationView.tsx similarity index 75% rename from frontend/src/component/application/application-view.jsx rename to frontend/src/component/application/ApplicationView.tsx index 5c0a0c485d..471875690c 100644 --- a/frontend/src/component/application/application-view.jsx +++ b/frontend/src/component/application/ApplicationView.tsx @@ -1,6 +1,5 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import PropTypes from 'prop-types'; +import { useContext } from 'react'; +import { Link, useParams } from 'react-router-dom'; import { Grid, List, @@ -9,8 +8,13 @@ import { ListItemAvatar, Typography, } from '@material-ui/core'; -import { Report, Extension, Timeline, FlagRounded } from '@material-ui/icons'; - +import { + Report, + Extension, + Timeline, + FlagRounded, + SvgIconComponent, +} from '@material-ui/icons'; import { shorten } from '../common'; import { CREATE_FEATURE, @@ -18,21 +22,30 @@ import { } from '../providers/AccessProvider/permissions'; import ConditionallyRender from '../common/ConditionallyRender/ConditionallyRender'; import { getTogglePath } from '../../utils/route-path-helpers'; -function ApplicationView({ - seenToggles, - hasAccess, - strategies, - instances, - formatFullDateTime, -}) { - const notFoundListItem = ({ createUrl, name, permission }) => ( +import useApplication from '../../hooks/api/getters/useApplication/useApplication'; +import AccessContext from '../../contexts/AccessContext'; +import { formatFullDateTimeWithLocale } from '../common/util'; +const ApplicationView = () => { + const { hasAccess } = useContext(AccessContext); + const { name } = useParams<{ name: string }>(); + const { application } = useApplication(name); + const { instances, strategies, seenToggles } = application; + const notFoundListItem = ({ + createUrl, + name, + permission, + }: { + createUrl: string; + name: string; + permission: string; + }) => ( - + {name}} @@ -54,13 +67,18 @@ function ApplicationView({ /> ); - // eslint-disable-next-line react/prop-types const foundListItem = ({ viewUrl, name, description, Icon, i, + }: { + viewUrl: string; + name: string; + description: string; + Icon: SvgIconComponent; + i: number; }) => ( @@ -83,10 +101,7 @@ function ApplicationView({
{seenToggles.map( - ( - { name, description, notFound, project }, - i - ) => ( + ({ name, description, notFound, project }, i) => ( {instances.map( - ({ instanceId, clientIp, lastSeen, sdkVersion }) => ( + ({ + instanceId, + clientIp, + lastSeen, + sdkVersion, + }: { + instanceId: string; + clientIp: string; + lastSeen: string; + sdkVersion: string; + }) => ( @@ -152,16 +177,18 @@ function ApplicationView({ primary={ {instanceId} {(sdkVersion)}} + elseShow={{instanceId}} /> } secondary={ {clientIp} last seen at{' '} - {formatFullDateTime(lastSeen)} + {formatFullDateTimeWithLocale( + lastSeen + )} } @@ -173,17 +200,6 @@ function ApplicationView({ ); -} - -ApplicationView.propTypes = { - createUrl: PropTypes.string, - name: PropTypes.string, - permission: PropTypes.string, - instances: PropTypes.array.isRequired, - seenToggles: PropTypes.array.isRequired, - strategies: PropTypes.array.isRequired, - hasAccess: PropTypes.func.isRequired, - formatFullDateTime: PropTypes.func.isRequired, }; export default ApplicationView; diff --git a/frontend/src/component/application/application-edit-component.js b/frontend/src/component/application/application-edit-component.js index ecab61cd3e..f3b7e7677a 100644 --- a/frontend/src/component/application/application-edit-component.js +++ b/frontend/src/component/application/application-edit-component.js @@ -19,7 +19,7 @@ import { formatDateWithLocale, } from '../common/util'; import { UPDATE_APPLICATION } from '../providers/AccessProvider/permissions'; -import ApplicationView from './application-view'; +import ApplicationView from './ApplicationView'; import ApplicationUpdate from './application-update'; import TabNav from '../common/TabNav/TabNav'; import Dialogue from '../common/Dialogue'; diff --git a/frontend/src/hooks/api/getters/useApplication/useApplication.ts b/frontend/src/hooks/api/getters/useApplication/useApplication.ts new file mode 100644 index 0000000000..782b63b820 --- /dev/null +++ b/frontend/src/hooks/api/getters/useApplication/useApplication.ts @@ -0,0 +1,50 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useState, useEffect } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; + +const useApplication = (name: string, options: SWRConfiguration = {}) => { + const fetcher = async () => { + const path = formatApiPath(`api/admin/metrics/applications/${name}`); + return fetch(path, { + method: 'GET', + }) + .then(handleErrorResponses('Application')) + .then(res => res.json()); + }; + + const FEATURE_CACHE_KEY = `api/admin/metrics/applications/${name}`; + + const { data, error } = useSWR(FEATURE_CACHE_KEY, fetcher, { + ...options, + }); + + const [loading, setLoading] = useState(!error && !data); + + const refetchApplication = () => { + mutate(FEATURE_CACHE_KEY); + }; + + useEffect(() => { + setLoading(!error && !data); + }, [data, error]); + + return { + application: data || { + appName: name, + color: null, + createdAt: '2022-02-02T21:04:00.268Z', + descriotion: '', + instances: [], + strategies: [], + seenToggles: [], + url: '', + }, + error, + loading, + refetchApplication, + FEATURE_CACHE_KEY, + }; +}; + +export default useApplication; diff --git a/frontend/src/hooks/api/getters/useApplications/useApplications.ts b/frontend/src/hooks/api/getters/useApplications/useApplications.ts new file mode 100644 index 0000000000..13efed5a8d --- /dev/null +++ b/frontend/src/hooks/api/getters/useApplications/useApplications.ts @@ -0,0 +1,41 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useState, useEffect } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; + +const useApplications = (options: SWRConfiguration = {}) => { + const fetcher = async () => { + const path = formatApiPath('api/admin/metrics/applications'); + return fetch(path, { + method: 'GET', + }) + .then(handleErrorResponses('Context data')) + .then(res => res.json()); + }; + + const FEATURE_CACHE_KEY = 'api/admin/metrics/applications'; + + const { data, error } = useSWR(FEATURE_CACHE_KEY, fetcher, { + ...options, + }); + + const [loading, setLoading] = useState(!error && !data); + + const refetchApplications = () => { + mutate(FEATURE_CACHE_KEY); + }; + + useEffect(() => { + setLoading(!error && !data); + }, [data, error]); + + return { + applications: data?.applications || {}, + error, + loading, + refetchApplications, + FEATURE_CACHE_KEY, + }; +}; + +export default useApplications;