1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-27 13:49:10 +02:00

feat: add link ui (#9918)

This commit is contained in:
Mateusz Kwasniewski 2025-05-07 13:24:59 +02:00 committed by GitHub
parent eb238f502a
commit 36c8efceae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 140 additions and 3 deletions

View File

@ -0,0 +1,71 @@
import { type FC, useState } from 'react';
import { Box, styled, TextField } from '@mui/material';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import { useFeatureLinkApi } from 'hooks/api/actions/useFeatureLinkApi/useFeatureLinkApi';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
interface IAddLinkDialogueProps {
project: string;
featureId: string;
showAddLinkDialogue: boolean;
onClose: () => void;
}
const StyledTextField = styled(TextField)(({ theme }) => ({
width: '100%',
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
}));
export const AddLinkDialogue: FC<IAddLinkDialogueProps> = ({
showAddLinkDialogue,
onClose,
project,
featureId,
}) => {
const [url, setUrl] = useState('');
const [title, setTitle] = useState('');
const { addLink, loading } = useFeatureLinkApi(project, featureId);
const { refetchFeature } = useFeature(project, featureId);
const { setToastData, setToastApiError } = useToast();
return (
<Dialogue
open={showAddLinkDialogue}
title='Add link'
onClose={onClose}
disabledPrimaryButton={url.trim() === '' || loading}
onClick={async () => {
try {
await addLink({ url, title: title ?? null });
setToastData({ text: 'Link added', type: 'success' });
onClose();
refetchFeature();
setTitle('');
setUrl('');
} catch (error) {
setToastApiError(formatUnknownError(error));
}
}}
primaryButtonText='Add'
secondaryButtonText='Cancel'
>
<Box>
<StyledTextField
label='Link'
variant='outlined'
value={url}
onChange={(e) => setUrl(e.target.value)}
/>
<StyledTextField
label='Title (optional)'
variant='outlined'
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</Box>
</Dialogue>
);
};

View File

@ -30,6 +30,9 @@ import AddIcon from '@mui/icons-material/Add';
import { useUiFlag } from 'hooks/useUiFlag';
import { Badge } from 'component/common/Badge/Badge';
import LinkIcon from '@mui/icons-material/Link';
import { UPDATE_FEATURE } from '../../../../providers/AccessProvider/permissions';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
import { AddLinkDialogue } from './AddLinkDialogue';
const StyledMetaDataContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(3),
@ -91,7 +94,13 @@ type FeatureOverviewMetaDataProps = {
onChange: () => void;
};
const FeatureLinks: FC<{ links: FeatureLink[] }> = ({ links }) => {
const FeatureLinks: FC<{
links: FeatureLink[];
project: string;
feature: string;
}> = ({ links, project, feature }) => {
const [showAddLinkDialogue, setShowAddLinkDialogue] = useState(false);
return links.length === 0 ? (
<StyledMetaDataContainer>
<StyledTitle>
@ -105,6 +114,18 @@ const FeatureLinks: FC<{ links: FeatureLink[] }> = ({ links }) => {
trackers, code repositories or analytics tooling
</StyledMetaDataItem>
<div>
<PermissionButton
size='small'
startIcon={<AddIcon />}
permission={UPDATE_FEATURE}
projectId={project}
variant='text'
onClick={() => {
setShowAddLinkDialogue(true);
}}
>
Add parent flag
</PermissionButton>
<Button
size='small'
variant='text'
@ -150,11 +171,17 @@ const FeatureLinks: FC<{ links: FeatureLink[] }> = ({ links }) => {
size='small'
variant='text'
startIcon={<AddIcon />}
onClick={() => {}}
onClick={() => setShowAddLinkDialogue(true)}
>
Add link
</Button>
</div>
<AddLinkDialogue
project={project}
featureId={feature}
showAddLinkDialogue={showAddLinkDialogue}
onClose={() => setShowAddLinkDialogue(false)}
/>
</StyledMetaDataContainer>
);
};
@ -181,7 +208,11 @@ const FeatureOverviewMetaData: FC<FeatureOverviewMetaDataProps> = ({
return (
<>
{featureLinksEnabled ? (
<FeatureLinks links={feature.links || []} />
<FeatureLinks
links={feature.links || []}
project={feature.project}
feature={feature.name}
/>
) : null}
<StyledMetaDataContainer>
<div>

View File

@ -0,0 +1,35 @@
import useAPI from '../useApi/useApi';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useCallback } from 'react';
export const useFeatureLinkApi = (project: string, feature: string) => {
const { makeRequest, createRequest, errors, loading } = useAPI({
propagateErrors: true,
});
const addLink = async (linkSchema: {
url: string;
title: string | null;
}) => {
const req = createRequest(
`/api/admin/projects/${project}/features/${feature}/link`,
{
method: 'POST',
body: JSON.stringify(linkSchema),
},
);
await makeRequest(req.caller, req.id);
};
const callbackDeps = [
createRequest,
makeRequest,
formatUnknownError,
project,
];
return {
addLink: useCallback(addLink, callbackDeps),
errors,
loading,
};
};