mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
feat: dialogs for project revive and delete (#7863)
Dialog needed to confirm revive/delete actions
This commit is contained in:
parent
627768b96c
commit
3baeb4c541
@ -22,59 +22,35 @@ import TimeAgo from 'react-timeago';
|
|||||||
import { Box, Link, Tooltip } from '@mui/material';
|
import { Box, Link, Tooltip } from '@mui/material';
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
CREATE_PROJECT,
|
|
||||||
DELETE_PROJECT,
|
DELETE_PROJECT,
|
||||||
|
UPDATE_PROJECT,
|
||||||
} from 'component/providers/AccessProvider/permissions';
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import Undo from '@mui/icons-material/Undo';
|
import Undo from '@mui/icons-material/Undo';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
import Delete from '@mui/icons-material/Delete';
|
import Delete from '@mui/icons-material/Delete';
|
||||||
|
|
||||||
interface IProjectArchiveCardProps {
|
export type ProjectArchiveCardProps = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
createdAt?: string;
|
|
||||||
archivedAt?: string;
|
archivedAt?: string;
|
||||||
featureCount: number;
|
archivedFeaturesCount?: number;
|
||||||
onRevive: () => void;
|
onRevive: () => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
mode: string;
|
mode?: string;
|
||||||
owners?: ProjectSchemaOwners;
|
owners?: ProjectSchemaOwners;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const ProjectArchiveCard: FC<IProjectArchiveCardProps> = ({
|
export const ProjectArchiveCard: FC<ProjectArchiveCardProps> = ({
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
archivedAt,
|
archivedAt,
|
||||||
featureCount = 0,
|
archivedFeaturesCount,
|
||||||
onRevive,
|
onRevive,
|
||||||
onDelete,
|
onDelete,
|
||||||
mode,
|
mode,
|
||||||
owners,
|
owners,
|
||||||
}) => {
|
}) => {
|
||||||
const { locationSettings } = useLocationSettings();
|
const { locationSettings } = useLocationSettings();
|
||||||
const Actions: FC<{
|
|
||||||
id: string;
|
|
||||||
}> = ({ id }) => (
|
|
||||||
<StyledActions>
|
|
||||||
<PermissionIconButton
|
|
||||||
onClick={onRevive}
|
|
||||||
projectId={id}
|
|
||||||
permission={CREATE_PROJECT}
|
|
||||||
tooltipProps={{ title: 'Restore project' }}
|
|
||||||
data-testid={`revive-feature-flag-button`}
|
|
||||||
>
|
|
||||||
<Undo />
|
|
||||||
</PermissionIconButton>
|
|
||||||
<PermissionIconButton
|
|
||||||
permission={DELETE_PROJECT}
|
|
||||||
projectId={id}
|
|
||||||
tooltipProps={{ title: 'Permanently delete project' }}
|
|
||||||
onClick={onDelete}
|
|
||||||
>
|
|
||||||
<Delete />
|
|
||||||
</PermissionIconButton>
|
|
||||||
</StyledActions>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledProjectCard disabled>
|
<StyledProjectCard disabled>
|
||||||
@ -89,17 +65,6 @@ export const ProjectArchiveCard: FC<IProjectArchiveCardProps> = ({
|
|||||||
<ProjectModeBadge mode={mode} />
|
<ProjectModeBadge mode={mode} />
|
||||||
</StyledDivHeader>
|
</StyledDivHeader>
|
||||||
<StyledDivInfo>
|
<StyledDivInfo>
|
||||||
<Link
|
|
||||||
component={RouterLink}
|
|
||||||
to={`/archive?search=project%3A${encodeURI(id)}`}
|
|
||||||
>
|
|
||||||
<StyledParagraphInfo disabled data-loading>
|
|
||||||
{featureCount}
|
|
||||||
</StyledParagraphInfo>
|
|
||||||
<p data-loading>
|
|
||||||
archived {featureCount === 1 ? 'flag' : 'flags'}
|
|
||||||
</p>
|
|
||||||
</Link>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(archivedAt)}
|
condition={Boolean(archivedAt)}
|
||||||
show={
|
show={
|
||||||
@ -129,15 +94,47 @@ export const ProjectArchiveCard: FC<IProjectArchiveCardProps> = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={typeof archivedFeaturesCount !== 'undefined'}
|
||||||
|
show={
|
||||||
|
<Link
|
||||||
|
component={RouterLink}
|
||||||
|
to={`/archive?search=project%3A${encodeURI(id)}`}
|
||||||
|
>
|
||||||
|
<StyledParagraphInfo disabled data-loading>
|
||||||
|
{archivedFeaturesCount}
|
||||||
|
</StyledParagraphInfo>
|
||||||
|
<p data-loading>
|
||||||
|
archived{' '}
|
||||||
|
{archivedFeaturesCount === 1
|
||||||
|
? 'flag'
|
||||||
|
: 'flags'}
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</StyledDivInfo>
|
</StyledDivInfo>
|
||||||
</StyledProjectCardBody>
|
</StyledProjectCardBody>
|
||||||
<ProjectCardFooter
|
<ProjectCardFooter id={id} disabled owners={owners}>
|
||||||
id={id}
|
<StyledActions>
|
||||||
Actions={Actions}
|
<PermissionIconButton
|
||||||
disabled
|
onClick={onRevive}
|
||||||
owners={owners}
|
projectId={id}
|
||||||
>
|
permission={UPDATE_PROJECT}
|
||||||
<Actions id={id} />
|
tooltipProps={{ title: 'Restore project' }}
|
||||||
|
data-testid={`revive-feature-flag-button`}
|
||||||
|
>
|
||||||
|
<Undo />
|
||||||
|
</PermissionIconButton>
|
||||||
|
<PermissionIconButton
|
||||||
|
permission={DELETE_PROJECT}
|
||||||
|
projectId={id}
|
||||||
|
tooltipProps={{ title: 'Permanently delete project' }}
|
||||||
|
onClick={onDelete}
|
||||||
|
>
|
||||||
|
<Delete />
|
||||||
|
</PermissionIconButton>
|
||||||
|
</StyledActions>
|
||||||
</ProjectCardFooter>
|
</ProjectCardFooter>
|
||||||
</StyledProjectCard>
|
</StyledProjectCard>
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,6 @@ interface IProjectCardFooterProps {
|
|||||||
id: string;
|
id: string;
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
Actions?: FC<{ id: string; isFavorite?: boolean }>;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
owners: IProjectOwnersProps['owners'];
|
owners: IProjectOwnersProps['owners'];
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
|||||||
import { Badge } from 'component/common/Badge/Badge';
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
|
|
||||||
interface IProjectModeBadgeProps {
|
interface IProjectModeBadgeProps {
|
||||||
mode: 'private' | 'protected' | 'public' | string;
|
mode?: 'private' | 'protected' | 'public' | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectModeBadge: FC<IProjectModeBadgeProps> = ({ mode }) => {
|
export const ProjectModeBadge: FC<IProjectModeBadgeProps> = ({ mode }) => {
|
||||||
|
@ -23,7 +23,8 @@ export const DeleteProjectDialogue = ({
|
|||||||
onSuccess,
|
onSuccess,
|
||||||
}: IDeleteProjectDialogueProps) => {
|
}: IDeleteProjectDialogueProps) => {
|
||||||
const { deleteProject } = useProjectApi();
|
const { deleteProject } = useProjectApi();
|
||||||
const { refetch: refetchProjectOverview } = useProjects();
|
const { refetch: refetchProjects } = useProjects();
|
||||||
|
const { refetch: refetchProjectArchive } = useProjects({ archived: true });
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const automatedActionsEnabled = useUiFlag('automatedActions');
|
const automatedActionsEnabled = useUiFlag('automatedActions');
|
||||||
@ -32,7 +33,8 @@ export const DeleteProjectDialogue = ({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
await deleteProject(project);
|
await deleteProject(project);
|
||||||
refetchProjectOverview();
|
refetchProjects();
|
||||||
|
refetchProjectArchive();
|
||||||
setToastData({
|
setToastData({
|
||||||
title: 'Deleted project',
|
title: 'Deleted project',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -34,7 +34,6 @@ export const ArchiveProject = ({
|
|||||||
}: IDeleteProjectProps) => {
|
}: IDeleteProjectProps) => {
|
||||||
const { isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const automatedActionsEnabled = useUiFlag('automatedActions');
|
const automatedActionsEnabled = useUiFlag('automatedActions');
|
||||||
const archiveProjectsEnabled = useUiFlag('archiveProjects');
|
|
||||||
const { actions } = useActions(projectId);
|
const { actions } = useActions(projectId);
|
||||||
const [showArchiveDialog, setShowArchiveDialog] = useState(false);
|
const [showArchiveDialog, setShowArchiveDialog] = useState(false);
|
||||||
const actionsCount = actions.filter(({ enabled }) => enabled).length;
|
const actionsCount = actions.filter(({ enabled }) => enabled).length;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { type FC, useEffect, useState } from 'react';
|
import { type FC, useEffect, useState } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import useProjectsArchive from 'hooks/api/getters/useProjectsArchive/useProjectsArchive';
|
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
@ -9,7 +8,13 @@ import { styled, useMediaQuery } from '@mui/material';
|
|||||||
import theme from 'themes/theme';
|
import theme from 'themes/theme';
|
||||||
import { Search } from 'component/common/Search/Search';
|
import { Search } from 'component/common/Search/Search';
|
||||||
import { ProjectGroup } from './ProjectGroup';
|
import { ProjectGroup } from './ProjectGroup';
|
||||||
import { ProjectArchiveCard } from '../NewProjectCard/ProjectArchiveCard';
|
import {
|
||||||
|
ProjectArchiveCard,
|
||||||
|
type ProjectArchiveCardProps,
|
||||||
|
} from '../NewProjectCard/ProjectArchiveCard';
|
||||||
|
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||||
|
import { ReviveProjectDialog } from './ReviveProjectDialog/ReviveProjectDialog';
|
||||||
|
import { DeleteProjectDialogue } from '../Project/DeleteProject/DeleteProjectDialogue';
|
||||||
|
|
||||||
const StyledApiError = styled(ApiError)(({ theme }) => ({
|
const StyledApiError = styled(ApiError)(({ theme }) => ({
|
||||||
maxWidth: '500px',
|
maxWidth: '500px',
|
||||||
@ -25,13 +30,24 @@ const StyledContainer = styled('div')(({ theme }) => ({
|
|||||||
type PageQueryType = Partial<Record<'search', string>>;
|
type PageQueryType = Partial<Record<'search', string>>;
|
||||||
|
|
||||||
export const ArchiveProjectList: FC = () => {
|
export const ArchiveProjectList: FC = () => {
|
||||||
const { projects, loading, error, refetch } = useProjectsArchive();
|
const { projects, loading, error, refetch } = useProjects({
|
||||||
|
archived: true,
|
||||||
|
});
|
||||||
|
|
||||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const [searchValue, setSearchValue] = useState(
|
const [searchValue, setSearchValue] = useState(
|
||||||
searchParams.get('search') || '',
|
searchParams.get('search') || '',
|
||||||
);
|
);
|
||||||
|
const [reviveProject, setReviveProject] = useState<{
|
||||||
|
isOpen: boolean;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
}>({ isOpen: false });
|
||||||
|
const [deleteProject, setDeleteProject] = useState<{
|
||||||
|
isOpen: boolean;
|
||||||
|
id?: string;
|
||||||
|
}>({ isOpen: false });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tableState: PageQueryType = {};
|
const tableState: PageQueryType = {};
|
||||||
@ -44,6 +60,28 @@ export const ArchiveProjectList: FC = () => {
|
|||||||
});
|
});
|
||||||
}, [searchValue, setSearchParams]);
|
}, [searchValue, setSearchParams]);
|
||||||
|
|
||||||
|
const ProjectCard: FC<
|
||||||
|
Omit<ProjectArchiveCardProps, 'onRevive' | 'onDelete'>
|
||||||
|
> = ({ id, ...props }) => (
|
||||||
|
<ProjectArchiveCard
|
||||||
|
onRevive={() =>
|
||||||
|
setReviveProject({
|
||||||
|
isOpen: true,
|
||||||
|
id,
|
||||||
|
name: projects?.find((project) => project.id === id)?.name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onDelete={() =>
|
||||||
|
setDeleteProject({
|
||||||
|
id,
|
||||||
|
isOpen: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
id={id}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<PageContent
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
@ -90,10 +128,25 @@ export const ArchiveProjectList: FC = () => {
|
|||||||
searchValue={searchValue}
|
searchValue={searchValue}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
placeholder='No archived projects found'
|
placeholder='No archived projects found'
|
||||||
ProjectCardComponent={ProjectArchiveCard}
|
ProjectCardComponent={ProjectCard}
|
||||||
link={false}
|
link={false}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
<ReviveProjectDialog
|
||||||
|
id={reviveProject.id || ''}
|
||||||
|
name={reviveProject.name || ''}
|
||||||
|
open={reviveProject.isOpen}
|
||||||
|
onClose={() =>
|
||||||
|
setReviveProject((state) => ({ ...state, isOpen: false }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<DeleteProjectDialogue
|
||||||
|
project={deleteProject.id || ''}
|
||||||
|
open={deleteProject.isOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setDeleteProject((state) => ({ ...state, isOpen: false }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||||
|
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||||
|
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||||
|
import useToast from 'hooks/useToast';
|
||||||
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
|
|
||||||
|
type ReviveProjectDialogProps = {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ReviveProjectDialog = ({
|
||||||
|
name,
|
||||||
|
id,
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
}: ReviveProjectDialogProps) => {
|
||||||
|
const { reviveProject } = useProjectApi();
|
||||||
|
const { refetch: refetchProjects } = useProjects();
|
||||||
|
const { refetch: refetchProjectArchive } = useProjects({ archived: true });
|
||||||
|
const { setToastData, setToastApiError } = useToast();
|
||||||
|
|
||||||
|
const onClick = async (e: React.SyntheticEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!id) return;
|
||||||
|
try {
|
||||||
|
await reviveProject(id);
|
||||||
|
refetchProjects();
|
||||||
|
refetchProjectArchive();
|
||||||
|
setToastData({
|
||||||
|
title: 'Restored project',
|
||||||
|
type: 'success',
|
||||||
|
text: 'Successfully restored project',
|
||||||
|
});
|
||||||
|
} catch (ex: unknown) {
|
||||||
|
setToastApiError(formatUnknownError(ex));
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialogue
|
||||||
|
open={open}
|
||||||
|
secondaryButtonText='Close'
|
||||||
|
onClose={onClose}
|
||||||
|
onClick={onClick}
|
||||||
|
title='Restore archived project'
|
||||||
|
>
|
||||||
|
Are you sure you'd like to restore project <strong>{name}</strong>{' '}
|
||||||
|
(id: <code>{id}</code>)?
|
||||||
|
{/* TODO: more explanation */}
|
||||||
|
</Dialogue>
|
||||||
|
);
|
||||||
|
};
|
@ -81,7 +81,16 @@ const useProjectApi = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const archiveProject = async (projectId: string) => {
|
const archiveProject = async (projectId: string) => {
|
||||||
const path = `api/admin/projects/${projectId}/archive`;
|
const path = `api/admin/projects/archive/${projectId}`;
|
||||||
|
const req = createRequest(path, { method: 'POST' });
|
||||||
|
|
||||||
|
const res = await makeRequest(req.caller, req.id);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reviveProject = async (projectId: string) => {
|
||||||
|
const path = `api/admin/projects/revive/${projectId}`;
|
||||||
const req = createRequest(path, { method: 'POST' });
|
const req = createRequest(path, { method: 'POST' });
|
||||||
|
|
||||||
const res = await makeRequest(req.caller, req.id);
|
const res = await makeRequest(req.caller, req.id);
|
||||||
@ -263,6 +272,7 @@ const useProjectApi = () => {
|
|||||||
editProjectSettings,
|
editProjectSettings,
|
||||||
deleteProject,
|
deleteProject,
|
||||||
archiveProject,
|
archiveProject,
|
||||||
|
reviveProject,
|
||||||
addEnvironmentToProject,
|
addEnvironmentToProject,
|
||||||
removeEnvironmentFromProject,
|
removeEnvironmentFromProject,
|
||||||
addAccessToProject,
|
addAccessToProject,
|
||||||
|
@ -4,10 +4,13 @@ import { formatApiPath } from 'utils/formatPath';
|
|||||||
|
|
||||||
import type { IProjectCard } from 'interfaces/project';
|
import type { IProjectCard } from 'interfaces/project';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
|
import type { GetProjectsParams } from 'openapi';
|
||||||
|
|
||||||
|
const useProjects = (options: SWRConfiguration & GetProjectsParams = {}) => {
|
||||||
|
const KEY = `api/admin/projects${options.archived ? '?archived=true' : ''}`;
|
||||||
|
|
||||||
const useProjects = (options: SWRConfiguration = {}) => {
|
|
||||||
const fetcher = () => {
|
const fetcher = () => {
|
||||||
const path = formatApiPath(`api/admin/projects`);
|
const path = formatApiPath(KEY);
|
||||||
return fetch(path, {
|
return fetch(path, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
})
|
})
|
||||||
@ -15,8 +18,6 @@ const useProjects = (options: SWRConfiguration = {}) => {
|
|||||||
.then((res) => res.json());
|
.then((res) => res.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
const KEY = `api/admin/projects`;
|
|
||||||
|
|
||||||
const { data, error } = useSWR<{ projects: IProjectCard[] }>(
|
const { data, error } = useSWR<{ projects: IProjectCard[] }>(
|
||||||
KEY,
|
KEY,
|
||||||
fetcher,
|
fetcher,
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
import type { ProjectSchema } from 'openapi';
|
|
||||||
|
|
||||||
// FIXME: import tpye
|
|
||||||
interface IProjectArchiveCard {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
createdAt: string;
|
|
||||||
archivedAt: string;
|
|
||||||
description: string;
|
|
||||||
featureCount: number;
|
|
||||||
owners?: ProjectSchema['owners'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implement data fetching
|
|
||||||
const useProjectsArchive = () => {
|
|
||||||
return {
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'Archived something',
|
|
||||||
id: 'archi',
|
|
||||||
createdAt: new Date('2024-08-10 16:06').toISOString(),
|
|
||||||
archivedAt: new Date('2024-08-12 17:07').toISOString(),
|
|
||||||
owners: [{ ownerType: 'system' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Second example',
|
|
||||||
id: 'pid',
|
|
||||||
createdAt: new Date('2024-08-10 16:06').toISOString(),
|
|
||||||
archivedAt: new Date('2024-08-12 17:07').toISOString(),
|
|
||||||
owners: [{ ownerType: 'system' }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
error: undefined as any,
|
|
||||||
loading: false,
|
|
||||||
refetch: () => {},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useProjectsArchive;
|
|
9
frontend/src/openapi/models/getProjectsParams.ts
Normal file
9
frontend/src/openapi/models/getProjectsParams.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Generated by Orval
|
||||||
|
* Do not edit manually.
|
||||||
|
* See `gen:api` script in package.json
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type GetProjectsParams = {
|
||||||
|
archived?: boolean;
|
||||||
|
};
|
@ -722,6 +722,7 @@ export * from './getProjectUsers401';
|
|||||||
export * from './getProjectUsers403';
|
export * from './getProjectUsers403';
|
||||||
export * from './getProjects401';
|
export * from './getProjects401';
|
||||||
export * from './getProjects403';
|
export * from './getProjects403';
|
||||||
|
export * from './getProjectsParams';
|
||||||
export * from './getPublicSignupToken401';
|
export * from './getPublicSignupToken401';
|
||||||
export * from './getPublicSignupToken403';
|
export * from './getPublicSignupToken403';
|
||||||
export * from './getRawFeatureMetrics401';
|
export * from './getRawFeatureMetrics401';
|
||||||
|
@ -77,6 +77,16 @@ export default class ProjectController extends Controller {
|
|||||||
summary: 'Get a list of all projects.',
|
summary: 'Get a list of all projects.',
|
||||||
description:
|
description:
|
||||||
'This endpoint returns an list of all the projects in the Unleash instance.',
|
'This endpoint returns an list of all the projects in the Unleash instance.',
|
||||||
|
parameters: [
|
||||||
|
{
|
||||||
|
name: 'archived',
|
||||||
|
in: 'query',
|
||||||
|
required: false,
|
||||||
|
schema: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
responses: {
|
responses: {
|
||||||
200: createResponseSchema('projectsSchema'),
|
200: createResponseSchema('projectsSchema'),
|
||||||
...getStandardResponses(401, 403),
|
...getStandardResponses(401, 403),
|
||||||
|
Loading…
Reference in New Issue
Block a user