Skip to content

Commit

Permalink
feat(capabilities): functionality to disable UI capabilities for diff…
Browse files Browse the repository at this point in the history
…erent contexts (#1553)
  • Loading branch information
andrewazores authored Jan 31, 2025
1 parent b122e0e commit bd78b9d
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 45 deletions.
45 changes: 32 additions & 13 deletions src/app/Archives/Archives.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { BreadcrumbPage } from '@app/BreadcrumbPage/BreadcrumbPage';
import { ArchivedRecordingsTable } from '@app/Recordings/ArchivedRecordingsTable';
import { Target, UPLOADS_SUBDIRECTORY } from '@app/Shared/Services/api.types';
import { CapabilitiesContext } from '@app/Shared/Services/Capabilities';
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { getActiveTab, switchTab } from '@app/utils/utils';
Expand Down Expand Up @@ -65,6 +66,7 @@ export const Archives: React.FC<ArchivesProps> = ({ ...props }) => {
const { search, pathname } = useLocation();
const navigate = useNavigate();
const context = React.useContext(ServiceContext);
const capabilities = React.useContext(CapabilitiesContext);
const addSubscription = useSubscriptions();

const activeTab = React.useMemo(() => {
Expand All @@ -73,6 +75,33 @@ export const Archives: React.FC<ArchivesProps> = ({ ...props }) => {

const [archiveEnabled, setArchiveEnabled] = React.useState(false);

const uploadTargetAsObs = React.useMemo(() => of(uploadAsTarget), []);

const tabs = React.useMemo(() => {
const arr: JSX.Element[] = [];
if (archiveEnabled) {
arr.push(
<Tab id="all-targets" eventKey={ArchiveTab.ALL_TARGETS} title={<TabTitleText>All Targets</TabTitleText>}>
<AllTargetsArchivedRecordingsTable />
</Tab>,
);
arr.push(
<Tab id="all-archives" eventKey={ArchiveTab.ALL_ARCHIVES} title={<TabTitleText>All Archives</TabTitleText>}>
<AllArchivedRecordingsTable />
</Tab>,
);

if (capabilities.fileUploads) {
arr.push(
<Tab id="uploads" eventKey={ArchiveTab.UPLOADS} title={<TabTitleText>Uploads</TabTitleText>}>
<ArchivedRecordingsTable target={uploadTargetAsObs} isUploadsTable={true} isNestedTable={false} />
</Tab>,
);
}
}
return arr;
}, [capabilities.fileUploads, archiveEnabled, uploadTargetAsObs]);

React.useEffect(() => {
addSubscription(context.api.isArchiveEnabled().subscribe(setArchiveEnabled));
}, [context.api, addSubscription, setArchiveEnabled]);
Expand All @@ -83,20 +112,10 @@ export const Archives: React.FC<ArchivesProps> = ({ ...props }) => {
[navigate, pathname, search],
);

const uploadTargetAsObs = React.useMemo(() => of(uploadAsTarget), []);

const cardBody = React.useMemo(() => {
return archiveEnabled ? (
return tabs.length > 0 ? (
<Tabs id="archives" activeKey={activeTab} onSelect={onTabSelect} unmountOnExit>
<Tab id="all-targets" eventKey={ArchiveTab.ALL_TARGETS} title={<TabTitleText>All Targets</TabTitleText>}>
<AllTargetsArchivedRecordingsTable />
</Tab>
<Tab id="all-archives" eventKey={ArchiveTab.ALL_ARCHIVES} title={<TabTitleText>All Archives</TabTitleText>}>
<AllArchivedRecordingsTable />
</Tab>
<Tab id="uploads" eventKey={ArchiveTab.UPLOADS} title={<TabTitleText>Uploads</TabTitleText>}>
<ArchivedRecordingsTable target={uploadTargetAsObs} isUploadsTable={true} isNestedTable={false} />
</Tab>
{tabs}
</Tabs>
) : (
<EmptyState>
Expand All @@ -107,7 +126,7 @@ export const Archives: React.FC<ArchivesProps> = ({ ...props }) => {
/>
</EmptyState>
);
}, [archiveEnabled, activeTab, uploadTargetAsObs, onTabSelect]);
}, [tabs, activeTab, onTabSelect]);

return (
<BreadcrumbPage {...props} pageTitle="Archives">
Expand Down
36 changes: 22 additions & 14 deletions src/app/Events/EventTemplates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { FUpload, MultiFileUpload, UploadCallbacks } from '@app/Shared/Component
import { LoadingView } from '@app/Shared/Components/LoadingView';
import { LoadingProps } from '@app/Shared/Components/types';
import { EventTemplate, NotificationCategory, Target } from '@app/Shared/Services/api.types';
import { CapabilitiesContext } from '@app/Shared/Services/Capabilities';
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { portalRoot, sortResources, TableColumn, toPath } from '@app/utils/utils';
Expand Down Expand Up @@ -91,6 +92,7 @@ export interface EventTemplatesProps {}

export const EventTemplates: React.FC<EventTemplatesProps> = () => {
const context = React.useContext(ServiceContext);
const capabilities = React.useContext(CapabilitiesContext);
const navigate = useNavigate();
const { t } = useCryostatTranslation();

Expand Down Expand Up @@ -364,20 +366,26 @@ export const EventTemplates: React.FC<EventTemplatesProps> = () => {
/>
</ToolbarItem>
</ToolbarGroup>
<ToolbarItem variant="separator" />
<ToolbarGroup variant="icon-button-group">
<ToolbarItem>
<Button
key="upload"
aria-label="Upload"
variant="secondary"
onClick={handleUploadModalOpen}
isDisabled={errorMessage != ''}
>
<UploadIcon />
</Button>
</ToolbarItem>
</ToolbarGroup>
{capabilities.fileUploads ? (
<>
<ToolbarItem variant="separator" />
<ToolbarGroup variant="icon-button-group">
<ToolbarItem>
<Button
key="upload"
aria-label="Upload"
variant="secondary"
onClick={handleUploadModalOpen}
isDisabled={errorMessage != ''}
>
<UploadIcon />
</Button>
</ToolbarItem>
</ToolbarGroup>
</>
) : (
<></>
)}
<DeleteWarningModal
warningType={DeleteOrDisableWarningType.DeleteEventTemplates}
visible={warningModalOpen}
Expand Down
17 changes: 12 additions & 5 deletions src/app/Rules/Rules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { EmptyText } from '@app/Shared/Components/EmptyText';
import { LoadingView } from '@app/Shared/Components/LoadingView';
import { MatchExpressionDisplay } from '@app/Shared/Components/MatchExpression/MatchExpressionDisplay';
import { Rule, NotificationCategory } from '@app/Shared/Services/api.types';
import { CapabilitiesContext } from '@app/Shared/Services/Capabilities';
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { TableColumn, formatBytes, formatDuration, sortResources, portalRoot } from '@app/utils/utils';
Expand Down Expand Up @@ -71,6 +72,7 @@ export interface RulesTableProps {}

export const RulesTable: React.FC<RulesTableProps> = () => {
const context = React.useContext(ServiceContext);
const capabilities = React.useContext(CapabilitiesContext);
const navigate = useNavigate();
const addSubscription = useSubscriptions();
const { t } = useCryostatTranslation();
Expand Down Expand Up @@ -396,11 +398,15 @@ export const RulesTable: React.FC<RulesTableProps> = () => {
{t('CREATE')}
</Button>
</ToolbarItem>
<ToolbarItem key="upload">
<Button variant="secondary" aria-label="Upload" onClick={handleUploadRule}>
<UploadIcon />
</Button>
</ToolbarItem>
{capabilities.fileUploads ? (
<ToolbarItem key="upload">
<Button variant="secondary" aria-label="Upload" onClick={handleUploadRule}>
<UploadIcon />
</Button>
</ToolbarItem>
) : (
<></>
)}
{ruleToWarn ? (
<RuleDeleteWarningModal
warningType={
Expand Down Expand Up @@ -431,6 +437,7 @@ export const RulesTable: React.FC<RulesTableProps> = () => {
handleWarningModalClose,
cleanRuleEnabled,
setCleanRuleEnabled,
capabilities.fileUploads,
],
);

Expand Down
27 changes: 27 additions & 0 deletions src/app/Shared/Services/Capabilities.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright The Cryostat Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from 'react';

interface Capabilities {
fileUploads: boolean;
}
const defaultCapabilities: Capabilities = {
fileUploads: true,
};

const CapabilitiesContext: React.Context<Capabilities> = React.createContext(defaultCapabilities);

export { Capabilities, CapabilitiesContext, defaultCapabilities };
29 changes: 16 additions & 13 deletions src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,22 @@ import * as React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom-v5-compat';
import { JoyrideProvider } from './Joyride/JoyrideProvider';
import { CapabilitiesContext, defaultCapabilities } from './Shared/Services/Capabilities';

export const App: React.FC = () => (
<ServiceContext.Provider value={defaultServices}>
<NotificationsContext.Provider value={NotificationsInstance}>
<Provider store={store}>
<Router>
<JoyrideProvider>
<AppLayout>
<AppRoutes />
</AppLayout>
</JoyrideProvider>
</Router>
</Provider>
</NotificationsContext.Provider>
</ServiceContext.Provider>
<CapabilitiesContext.Provider value={defaultCapabilities}>
<ServiceContext.Provider value={defaultServices}>
<NotificationsContext.Provider value={NotificationsInstance}>
<Provider store={store}>
<Router>
<JoyrideProvider>
<AppLayout>
<AppRoutes />
</AppLayout>
</JoyrideProvider>
</Router>
</Provider>
</NotificationsContext.Provider>
</ServiceContext.Provider>
</CapabilitiesContext.Provider>
);

0 comments on commit bd78b9d

Please sign in to comment.