Skip to content

Commit

Permalink
chore: release template sidebar (#8871)
Browse files Browse the repository at this point in the history
https://linear.app/unleash/issue/2-3026/release-template-sidebar-documentationcommand

Implements the release plan template form sidebar / description.

Took some liberties in the text compared to what we had in our sketches.
Also includes some slight refactoring.


![image](https://github.com/user-attachments/assets/529bf306-b545-4efa-8330-afc19782765a)
  • Loading branch information
nunogois authored Nov 27, 2024
1 parent 9044d4c commit 679e9d1
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 23 deletions.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/milestone.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { scrollToTop } from 'component/common/util';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';

const StyledButtonContainer = styled('div')(() => ({
marginTop: 'auto',
Expand All @@ -23,6 +24,7 @@ const StyledCancelButton = styled(Button)(({ theme }) => ({
}));

export const CreateReleasePlanTemplate = () => {
const { uiConfig } = useUiConfig();
const releasePlansEnabled = useUiFlag('releasePlans');
const { setToastApiError, setToastData } = useToast();
const navigate = useNavigate();
Expand Down Expand Up @@ -50,12 +52,10 @@ export const CreateReleasePlanTemplate = () => {
clearErrors();
const isValid = validate();
if (isValid) {
const payload = getTemplatePayload();
try {
const template = await createReleasePlanTemplate({
...payload,
milestones,
});
const template = await createReleasePlanTemplate(
getTemplatePayload(),
);
scrollToTop();
setToastData({
type: 'success',
Expand All @@ -68,6 +68,13 @@ export const CreateReleasePlanTemplate = () => {
}
};

const formatApiCode = () => `curl --location --request POST '${
uiConfig.unleashUrl
}/api/admin/release-plan-templates' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${JSON.stringify(getTemplatePayload(), undefined, 2)}'`;

if (!releasePlansEnabled) {
return null;
}
Expand All @@ -83,7 +90,7 @@ export const CreateReleasePlanTemplate = () => {
errors={errors}
clearErrors={clearErrors}
formTitle='Create release plan template'
formDescription='Create a release plan template to make it easier for you and your team to release features.'
formatApiCode={formatApiCode}
handleSubmit={handleSubmit}
>
<StyledButtonContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useNavigate } from 'react-router-dom';
import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast';
import useReleasePlanTemplatesApi from 'hooks/api/actions/useReleasePlanTemplatesApi/useReleasePlanTemplatesApi';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';

const StyledButtonContainer = styled('div')(() => ({
marginTop: 'auto',
Expand All @@ -23,6 +24,7 @@ const StyledCancelButton = styled(Button)(({ theme }) => ({
}));

export const EditReleasePlanTemplate = () => {
const { uiConfig } = useUiConfig();
const releasePlansEnabled = useUiFlag('releasePlans');
const templateId = useRequiredPathParam('templateId');
const { template, loading, error, refetch } =
Expand Down Expand Up @@ -56,13 +58,11 @@ export const EditReleasePlanTemplate = () => {
clearErrors();
const isValid = validate();
if (isValid) {
const payload = getTemplatePayload();
try {
await updateReleasePlanTemplate({
...payload,
id: templateId,
milestones,
});
await updateReleasePlanTemplate(
templateId,
getTemplatePayload(),
);
await refetch();
setToastData({
type: 'success',
Expand All @@ -74,6 +74,13 @@ export const EditReleasePlanTemplate = () => {
}
};

const formatApiCode = () => `curl --location --request PUT '${
uiConfig.unleashUrl
}/api/admin/release-plan-templates/${templateId}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${JSON.stringify(getTemplatePayload(), undefined, 2)}'`;

if (!releasePlansEnabled) {
return null;
}
Expand All @@ -89,7 +96,7 @@ export const EditReleasePlanTemplate = () => {
errors={errors}
clearErrors={clearErrors}
formTitle={`Edit template ${template.name}`}
formDescription='Edit a release plan template that makes it easier for you and your team to release features.'
formatApiCode={formatApiCode}
handleSubmit={handleSubmit}
loading={loading}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import type {
IReleasePlanMilestoneStrategy,
} from 'interfaces/releasePlans';
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import ReleaseTemplateIcon from '@mui/icons-material/DashboardOutlined';
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
import { useState } from 'react';
import { ReleasePlanTemplateAddStrategyForm } from './ReleasePlanTemplateAddStrategyForm';
import { TemplateFormDescription } from './TemplateFormDescription';

const StyledInputDescription = styled('p')(({ theme }) => ({
marginBottom: theme.spacing(1),
Expand Down Expand Up @@ -38,7 +38,7 @@ interface ITemplateFormProps {
errors: { [key: string]: string };
clearErrors: () => void;
formTitle: string;
formDescription: string;
formatApiCode: () => string;
handleSubmit: (e: React.FormEvent) => void;
loading?: boolean;
children?: React.ReactNode;
Expand All @@ -54,7 +54,7 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
errors,
clearErrors,
formTitle,
formDescription,
formatApiCode,
handleSubmit,
children,
}) => {
Expand Down Expand Up @@ -115,8 +115,8 @@ export const TemplateForm: React.FC<ITemplateFormProps> = ({
return (
<FormTemplate
title={formTitle}
description={formDescription}
documentationIcon={<ReleaseTemplateIcon />}
description={<TemplateFormDescription />}
formatApiCode={formatApiCode}
>
<StyledForm onSubmit={handleSubmit}>
<StyledInputDescription>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import ReleaseTemplateIcon from '@mui/icons-material/DashboardOutlined';
import { ReactComponent as MilestoneIcon } from 'assets/icons/milestone.svg';
import { styled } from '@mui/material';

const StyledDescription = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2),
fontSize: theme.fontSizes.smallBody,
}));

const StyledDescriptionHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
fontSize: theme.fontSizes.bodySize,
fontWeight: theme.fontWeight.bold,
}));

const StyledExampleUsage = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
}));

const StyledMilestones = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2),
}));

const StyledLabel = styled('p')(({ theme }) => ({
fontWeight: theme.fontWeight.bold,
}));

const StyledMilestoneHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
}));

export const TemplateFormDescription = () => {
return (
<StyledDescription>
<StyledDescriptionHeader>
<ReleaseTemplateIcon />
Release templates
</StyledDescriptionHeader>
<p>
Standardize your team's approach to rolling out new
functionality with release templates. These templates allow you
to predefine strategies, or groups of strategies, making it
easier to set up new flags and ensure alignment in how rollouts
are managed.
</p>
<p>
Customize templates to suit your needs by adding strategies to
specific milestones. Each milestone will execute sequentially,
streamlining your release process.
</p>
<StyledExampleUsage>
<StyledLabel>Example usage</StyledLabel>
<StyledMilestones>
<div>
<StyledMilestoneHeader>
<MilestoneIcon />
Milestone 1
</StyledMilestoneHeader>
<p>
Enable the feature for internal teams to test
functionality and resolve initial issues.
</p>
</div>
<div>
<StyledMilestoneHeader>
<MilestoneIcon />
Milestone 2
</StyledMilestoneHeader>
<p>
Expand the rollout to 20% of beta users to gather
feedback and monitor performance.
</p>
</div>
<div>
<StyledMilestoneHeader>
<MilestoneIcon />
Milestone 3
</StyledMilestoneHeader>
<p>
Release the feature to all users after confirming
stability and addressing earlier feedback.
</p>
</div>
</StyledMilestones>
</StyledExampleUsage>
</StyledDescription>
);
};
1 change: 1 addition & 0 deletions frontend/src/component/releases/hooks/useTemplateForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const useTemplateForm = (
return {
name,
description,
milestones,
};
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type { IReleasePlanTemplatePayload } from 'interfaces/releasePlans';
import type {
IReleasePlanTemplate,
IReleasePlanTemplatePayload,
} from 'interfaces/releasePlans';
import useAPI from '../useApi/useApi';

export const useReleasePlanTemplatesApi = () => {
Expand All @@ -23,7 +26,7 @@ export const useReleasePlanTemplatesApi = () => {

const createReleasePlanTemplate = async (
template: IReleasePlanTemplatePayload,
): Promise<IReleasePlanTemplatePayload> => {
): Promise<IReleasePlanTemplate> => {
const requestId = 'createReleasePlanTemplate';
const path = 'api/admin/release-plan-templates';
const req = createRequest(
Expand All @@ -40,10 +43,11 @@ export const useReleasePlanTemplatesApi = () => {
};

const updateReleasePlanTemplate = async (
templateId: string,
template: IReleasePlanTemplatePayload,
) => {
const requestId = 'updateReleasePlanTemplate';
const path = `api/admin/release-plan-templates/${template.id}`;
const path = `api/admin/release-plan-templates/${templateId}`;
const req = createRequest(
path,
{
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/interfaces/releasePlans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ export interface IReleasePlanMilestoneStrategy extends IFeatureStrategy {
}

export interface IReleasePlanTemplatePayload {
id?: string;
name: string;
description: string;
milestones?: IReleasePlanMilestonePayload[];
milestones: IReleasePlanMilestonePayload[];
}

export interface IReleasePlanMilestonePayload {
Expand Down

0 comments on commit 679e9d1

Please sign in to comment.