mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: edit link UI (#9926)
This commit is contained in:
parent
919db76629
commit
dea785fb96
@ -1,71 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -32,7 +32,7 @@ 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';
|
||||
import { EditLinkDialogue, AddLinkDialogue } from './LinkDialogue';
|
||||
import { useFeatureLinkApi } from 'hooks/api/actions/useFeatureLinkApi/useFeatureLinkApi';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
@ -107,6 +107,7 @@ interface FeatureLinksProps {
|
||||
|
||||
const FeatureLinks: FC<FeatureLinksProps> = ({ links, project, feature }) => {
|
||||
const [showAddLinkDialogue, setShowAddLinkDialogue] = useState(false);
|
||||
const [editLink, setEditLink] = useState<FeatureLink | null>(null);
|
||||
const { deleteLink, loading } = useFeatureLinkApi(project, feature);
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { refetchFeature } = useFeature(project, feature);
|
||||
@ -132,7 +133,9 @@ const FeatureLinks: FC<FeatureLinksProps> = ({ links, project, feature }) => {
|
||||
<ExtraActions
|
||||
capabilityId='link'
|
||||
feature={feature}
|
||||
onEdit={() => {}}
|
||||
onEdit={() => {
|
||||
setEditLink(link);
|
||||
}}
|
||||
onDelete={async () => {
|
||||
try {
|
||||
await deleteLink(link.id);
|
||||
@ -212,9 +215,15 @@ const FeatureLinks: FC<FeatureLinksProps> = ({ links, project, feature }) => {
|
||||
<AddLinkDialogue
|
||||
project={project}
|
||||
featureId={feature}
|
||||
showAddLinkDialogue={showAddLinkDialogue}
|
||||
showDialogue={showAddLinkDialogue}
|
||||
onClose={() => setShowAddLinkDialogue(false)}
|
||||
/>
|
||||
<EditLinkDialogue
|
||||
project={project}
|
||||
featureId={feature}
|
||||
link={editLink}
|
||||
onClose={() => setEditLink(null)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,123 @@
|
||||
import { type FC, useEffect, 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';
|
||||
import type { FeatureLink } from 'interfaces/featureToggle';
|
||||
|
||||
const StyledTextField = styled(TextField)(({ theme }) => ({
|
||||
width: '100%',
|
||||
marginTop: theme.spacing(1),
|
||||
marginBottom: theme.spacing(1),
|
||||
}));
|
||||
|
||||
interface ILinkDialogueProps {
|
||||
project: string;
|
||||
featureId: string;
|
||||
onClose: () => void;
|
||||
mode: 'add' | 'edit';
|
||||
showDialogue: boolean;
|
||||
link: FeatureLink | null;
|
||||
}
|
||||
|
||||
const LinkDialogue: FC<ILinkDialogueProps> = ({
|
||||
showDialogue,
|
||||
onClose,
|
||||
project,
|
||||
featureId,
|
||||
mode,
|
||||
link,
|
||||
}) => {
|
||||
const [url, setUrl] = useState('');
|
||||
const [title, setTitle] = useState('');
|
||||
const [id, setId] = useState('');
|
||||
const { addLink, editLink, loading } = useFeatureLinkApi(
|
||||
project,
|
||||
featureId,
|
||||
);
|
||||
const { refetchFeature } = useFeature(project, featureId);
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
|
||||
const isEditMode = mode === 'edit';
|
||||
const dialogueTitle = isEditMode ? 'Edit link' : 'Add link';
|
||||
const successMessage = isEditMode ? 'Link updated' : 'Link added';
|
||||
|
||||
useEffect(() => {
|
||||
if (isEditMode && link) {
|
||||
setUrl(link.url || '');
|
||||
setTitle(link.title || '');
|
||||
setId(link.id || '');
|
||||
} else if (!isEditMode) {
|
||||
setUrl('');
|
||||
setTitle('');
|
||||
setId('');
|
||||
}
|
||||
}, [isEditMode, link]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
if (isEditMode) {
|
||||
await editLink(id, { url, title: title || null });
|
||||
} else {
|
||||
await addLink({ url, title: title || null });
|
||||
}
|
||||
|
||||
setToastData({ text: successMessage, type: 'success' });
|
||||
onClose();
|
||||
refetchFeature();
|
||||
setTitle('');
|
||||
setUrl('');
|
||||
} catch (error) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
};
|
||||
|
||||
const isOpen = isEditMode ? link !== null : showDialogue;
|
||||
|
||||
return (
|
||||
<Dialogue
|
||||
open={isOpen}
|
||||
title={dialogueTitle}
|
||||
onClose={onClose}
|
||||
disabledPrimaryButton={url.trim() === '' || loading}
|
||||
onClick={handleSubmit}
|
||||
primaryButtonText='Save'
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export const AddLinkDialogue: FC<Omit<ILinkDialogueProps, 'mode' | 'link'>> = (
|
||||
props,
|
||||
) => {
|
||||
return <LinkDialogue {...props} mode='add' link={null} />;
|
||||
};
|
||||
|
||||
export const EditLinkDialogue: FC<
|
||||
Omit<ILinkDialogueProps, 'mode' | 'showDialogue'>
|
||||
> = (props) => {
|
||||
return (
|
||||
<LinkDialogue
|
||||
{...props}
|
||||
mode='edit'
|
||||
showDialogue={props.link !== null}
|
||||
/>
|
||||
);
|
||||
};
|
@ -19,6 +19,17 @@ export const useFeatureLinkApi = (project: string, feature: string) => {
|
||||
await makeRequest(req.caller, req.id);
|
||||
};
|
||||
|
||||
const editLink = async (linkId: string, linkSchema: FeatureLinkSchema) => {
|
||||
const req = createRequest(
|
||||
`/api/admin/projects/${project}/features/${feature}/link/${linkId}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(linkSchema),
|
||||
},
|
||||
);
|
||||
await makeRequest(req.caller, req.id);
|
||||
};
|
||||
|
||||
const deleteLink = async (linkId: string) => {
|
||||
const req = createRequest(
|
||||
`/api/admin/projects/${project}/features/${feature}/link/${linkId}`,
|
||||
@ -37,6 +48,7 @@ export const useFeatureLinkApi = (project: string, feature: string) => {
|
||||
];
|
||||
return {
|
||||
addLink: useCallback(addLink, callbackDeps),
|
||||
editLink: useCallback(editLink, callbackDeps),
|
||||
deleteLink: useCallback(deleteLink, callbackDeps),
|
||||
errors,
|
||||
loading,
|
||||
|
Loading…
Reference in New Issue
Block a user