Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b382a27
init cost estimation component
Oct 13, 2022
573f8a3
add cost estimation section above the Gitops
Oct 13, 2022
3e7f0a1
add cost estimation functionality to capa cluster template
Oct 16, 2022
2b70dc9
send view logic to the child component
Oct 17, 2022
ec45a64
refactor currency formater
Oct 17, 2022
9469f6f
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
ahussein3 Oct 17, 2022
10a31e9
refactor variables usage
Oct 17, 2022
b32dfc9
Merge branch 'UI-1678-AddCostEstimationComponentToCreateClusterForm' …
Oct 17, 2022
57681f9
remove uneeded props
Oct 17, 2022
020fbc5
reset estimation to undefined when OnFormDataChange
Oct 17, 2022
a87b38f
reset cost estimation on parameterValues changes
Oct 17, 2022
863cf36
get latest from main and fix conflict
Oct 19, 2022
91bb22d
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
AlinaGoaga Oct 19, 2022
ada8232
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
ahussein3 Oct 19, 2022
65e42a2
Add cost estimation to render template api response
AlinaGoaga Oct 19, 2022
465bcf5
Return low to 0
AlinaGoaga Oct 19, 2022
768dfa7
Merge branch 'UI-1678-AddCostEstimationComponentToCreateClusterForm' …
AlinaGoaga Oct 19, 2022
eda8145
apply peer review
Oct 19, 2022
8730075
Merge branch 'UI-1678-AddCostEstimationComponentToCreateClusterForm' …
Oct 19, 2022
3f85944
add default null value
Oct 19, 2022
5814432
remove uneeded div
Oct 19, 2022
4698850
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
ahussein3 Oct 19, 2022
86d4dd7
create single state for cost estimation in the parent component and r…
Oct 20, 2022
c243170
Merge branch 'UI-1678-AddCostEstimationComponentToCreateClusterForm' …
Oct 20, 2022
47ada7e
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
ahussein3 Oct 20, 2022
05ef0aa
remove uneeded import
Oct 20, 2022
e78e8be
Merge branch 'UI-1678-AddCostEstimationComponentToCreateClusterForm' …
Oct 20, 2022
9072307
refactor section conditional view to parent to be responsible on it n…
Oct 21, 2022
68cd934
remove unused variables
Oct 21, 2022
d538c2b
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
AlinaGoaga Oct 21, 2022
9c42304
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
AlinaGoaga Oct 21, 2022
20656c7
Merge branch 'main' of github.com:weaveworks/weave-gitops-enterprise …
AlinaGoaga Oct 21, 2022
8d50154
Merge branch 'UI-1678-AddCostEstimationComponentToCreateClusterForm' …
AlinaGoaga Oct 21, 2022
34603e5
Add namespaces to render template for costs
AlinaGoaga Oct 21, 2022
fa5c8fe
remove amount from costEstimate Object
Oct 24, 2022
f28bd36
Merge branch 'UI-1678-AddCostEstimationComponentToCreateClusterForm' …
Oct 24, 2022
94ce8dc
Merge branch 'main' into UI-1678-AddCostEstimationComponentToCreateCl…
ahussein3 Oct 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/clusters-service/pkg/server/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func (s *server) RenderTemplate(ctx context.Context, msg *capiv1_proto.RenderTem
}
}

return &capiv1_proto.RenderTemplateResponse{RenderedTemplate: files.RenderedTemplate, ProfileFiles: profileFiles, KustomizationFiles: kustomizationFiles}, err
return &capiv1_proto.RenderTemplateResponse{RenderedTemplate: files.RenderedTemplate, ProfileFiles: profileFiles, KustomizationFiles: kustomizationFiles, CostEstimate: files.CostEstimate}, err
}

func (s *server) getFiles(ctx context.Context, tmpl template.Template, msg GetFilesRequest, createRequestMessage *capiv1_proto.CreatePullRequestRequest) (*GetFilesReturn, error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ const useStyles = makeStyles(() =>
export const ApplicationsWrapper: FC<{
formData: any;
setFormData: Dispatch<React.SetStateAction<any>>;
isKustomizationsEnabled?: string;
}> = ({ formData, setFormData, isKustomizationsEnabled = 'true' }) => {
}> = ({ formData, setFormData }) => {
const classes = useStyles();

const handleAddApplication = () => {
Expand All @@ -39,14 +38,14 @@ export const ApplicationsWrapper: FC<{
setFormData({ ...formData, clusterAutomations: newAutomations });
};

return isKustomizationsEnabled === 'false' ? null : (
return (
<div className={classes.addApplicationSectionWrapper}>
<h2>Applications</h2>
{formData.clusterAutomations?.map((kustomization: any, index: number) => {
return (
<div key={index} className={classes.applicationWrapper}>
<h3>Application No.{index + 1}</h3>
<Grid container className="">
<Grid container>
<Grid item xs={12} sm={8} md={8} lg={8}>
<AppFields
index={index}
Expand Down
74 changes: 74 additions & 0 deletions ui-cra/src/components/Clusters/Form/Partials/CostEstimation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { createStyles, Grid, makeStyles } from '@material-ui/core';
import { Button, LoadingPage, theme } from '@weaveworks/weave-gitops';
import React, { FC } from 'react';
import { validateFormData } from '../../../../utils/form';

const CostEstimation: FC<{
costEstimate: string;
isCostEstimationLoading: boolean;
handleCostEstimation: () => Promise<void>;
}> = ({ handleCostEstimation, costEstimate, isCostEstimationLoading }) => {
const useStyles = makeStyles(() =>
createStyles({
getEstimationButton: {
marginRight: theme.spacing.medium,
},
costWrapper: {
marginRight: theme.spacing.medium,
fontWeight: 'bold',
},
previewLoading: {
padding: theme.spacing.base,
},
}),
);
const classes = useStyles();
return isCostEstimationLoading ? (
<LoadingPage className={classes.previewLoading} />
) : (
<div>
<h2>Cost Estimation</h2>
<Grid container>
<Grid item xs={6} sm={6} md={6} lg={6}>
<Grid container>
<Grid
item
xs={6}
justifyContent="flex-start"
alignItems="center"
container
>
<div>Monthly Cost:</div>
</Grid>
<Grid
item
xs={6}
justifyContent="flex-end"
alignItems="center"
container
>
<div className={classes.costWrapper}>{costEstimate}</div>
</Grid>
</Grid>
</Grid>
<Grid
item
xs={6}
justifyContent="flex-end"
alignItems="center"
container
>
<Button
id="get-estimation"
className={classes.getEstimationButton}
onClick={event => validateFormData(event, handleCostEstimation)}
>
GET ESTIMATION
</Button>
</Grid>
</Grid>
</div>
);
};

export default CostEstimation;
9 changes: 2 additions & 7 deletions ui-cra/src/components/Clusters/Form/Partials/Credentials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ import { useListCredentials } from '../../../../hooks/credentials';
const Credentials: FC<{
infraCredential: Credential | null;
setInfraCredential: Dispatch<React.SetStateAction<Credential | null>>;
isCredentialEnabled?: string;
}> = ({
infraCredential,
setInfraCredential,
isCredentialEnabled = 'true',
}) => {
}> = ({ infraCredential, setInfraCredential }) => {
const { data, isLoading } = useListCredentials();
const credentials = useMemo(
() => data?.credentials || [],
Expand Down Expand Up @@ -46,7 +41,7 @@ const Credentials: FC<{
[credentials, setInfraCredential],
);

return isCredentialEnabled === 'false' ? null : (
return (
<div className="credentials">
<span>Infrastructure provider credentials:</span>
<FormControl>
Expand Down
2 changes: 1 addition & 1 deletion ui-cra/src/components/Clusters/Form/Partials/Profiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const Profiles: FC<{
const numSelected = updatedProfilesList.filter(up => up.selected).length;
const rowCount = updatedProfilesList.length || 0;

return isProfilesEnabled === 'false' ? null : (
return (
<ProfilesWrapper>
<>
<h2>{context === 'app' ? 'Helm Releases' : 'Profiles'}</h2>
Expand Down
112 changes: 86 additions & 26 deletions ui-cra/src/components/Clusters/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ import {
FLUX_BOOSTRAP_KUSTOMIZATION_NAMESPACE,
} from '../../../utils/config';
import { validateFormData } from '../../../utils/form';
import { getFormattedCostEstimate } from '../../../utils/formatters';
import { Routes } from '../../../utils/nav';
import { isUnauthenticated, removeToken } from '../../../utils/request';
import { ApplicationsWrapper } from './Partials/ApplicationsWrapper';
import CostEstimation from './Partials/CostEstimation';
import Credentials from './Partials/Credentials';
import GitOps from './Partials/GitOps';
import Preview from './Partials/Preview';
Expand Down Expand Up @@ -127,9 +129,6 @@ const useStyles = makeStyles(theme =>
display: 'flex',
justifyContent: 'flex-end',
padding: small,
button: {
width: '200px',
},
},
previewLoading: {
padding: base,
Expand Down Expand Up @@ -294,8 +293,20 @@ const ClusterForm: FC<ClusterFormProps> = ({ template, cluster }) => {
null,
);
const [loading, setLoading] = useState<boolean>(false);
const [costEstimationLoading, setCostEstimationLoading] =
useState<boolean>(false);
const [costEstimate, setCostEstimate] = useState<string>('00.00 USD');
const [enableCreatePR, setEnableCreatePR] = useState<boolean>(false);

const isCredentialEnabled =
annotations?.['templates.weave.works/credentials-enabled'] || 'true';
const isProfilesEnabled =
annotations?.['templates.weave.works/profiles-enabled'] || 'true';
const isKustomizationsEnabled =
annotations?.['templates.weave.works/kustomizations-enabled'] || 'true';
const isCostEstimationEnabled =
annotations?.['templates.weave.works/cost-estimation-enabled'] || 'false';

const handlePRPreview = useCallback(() => {
const { parameterValues } = formData;
setPreviewLoading(true);
Expand Down Expand Up @@ -330,6 +341,39 @@ const ClusterForm: FC<ClusterFormProps> = ({ template, cluster }) => {
updatedProfiles,
]);

const handleCostEstimation = useCallback(() => {
const { parameterValues } = formData;
setCostEstimationLoading(true);
return renderTemplate({
templateName: template.name,
templateNamespace: template.namespace,
values: parameterValues,
profiles: encodedProfiles(updatedProfiles),
credentials: infraCredential || undefined,
kustomizations: getKustomizations(formData),
templateKind: template.templateKind,
})
.then(data => {
const { costEstimate } = data;
setCostEstimate(getFormattedCostEstimate(costEstimate));
})
.catch(err =>
setNotifications([
{ message: { text: err.message }, variant: 'danger' },
]),
)
.finally(() => setCostEstimationLoading(false));
}, [
formData,
renderTemplate,
infraCredential,
setNotifications,
template.name,
template.templateKind,
template.namespace,
updatedProfiles,
]);

const handleAddCluster = useCallback(() => {
const payload = toPayload(
formData,
Expand Down Expand Up @@ -400,6 +444,10 @@ const ClusterForm: FC<ClusterFormProps> = ({ template, cluster }) => {
}
}, [cluster, formData.parameterValues, setFormData]);

useEffect(() => {
setCostEstimate('00.00 USD');
}, [formData.parameterValues]);

return useMemo(() => {
return (
<CallbackStateContextProvider
Expand All @@ -418,13 +466,12 @@ const ClusterForm: FC<ClusterFormProps> = ({ template, cluster }) => {
<div className="template-title">
Template: <span>{template.name}</span>
</div>
<Credentials
infraCredential={infraCredential}
setInfraCredential={setInfraCredential}
isCredentialEnabled={
annotations?.['templates.weave.works/credentials-enabled']
}
/>
{isCredentialEnabled === 'true' ? (
<Credentials
infraCredential={infraCredential}
setInfraCredential={setInfraCredential}
/>
) : null}
</CredentialsWrapper>
<Divider
className={
Expand All @@ -437,22 +484,20 @@ const ClusterForm: FC<ClusterFormProps> = ({ template, cluster }) => {
setFormData={setFormData}
/>
</Grid>
<Profiles
isLoading={profilesIsLoading}
updatedProfiles={updatedProfiles}
setUpdatedProfiles={setUpdatedProfiles}
isProfilesEnabled={
annotations?.['templates.weave.works/profiles-enabled']
}
/>
<Grid item xs={12} sm={10} md={10} lg={8}>
<ApplicationsWrapper
formData={formData}
setFormData={setFormData}
isKustomizationsEnabled={
annotations?.['templates.weave.works/kustomizations-enabled']
}
{isProfilesEnabled === 'true' ? (
<Profiles
isLoading={profilesIsLoading}
updatedProfiles={updatedProfiles}
setUpdatedProfiles={setUpdatedProfiles}
/>
) : null}
<Grid item xs={12} sm={10} md={10} lg={8}>
{isKustomizationsEnabled === 'true' ? (
<ApplicationsWrapper
formData={formData}
setFormData={setFormData}
/>
) : null}
{previewLoading ? (
<LoadingPage className={classes.previewLoading} />
) : (
Expand All @@ -472,6 +517,15 @@ const ClusterForm: FC<ClusterFormProps> = ({ template, cluster }) => {
PRPreview={PRPreview}
/>
) : null}
<Grid item xs={12} sm={10} md={10} lg={8}>
{isCostEstimationEnabled === 'true' ? (
<CostEstimation
handleCostEstimation={handleCostEstimation}
costEstimate={costEstimate}
isCostEstimationLoading={costEstimationLoading}
/>
) : null}
</Grid>
<Grid item xs={12} sm={10} md={10} lg={8}>
<GitOps
formData={formData}
Expand Down Expand Up @@ -514,7 +568,13 @@ const ClusterForm: FC<ClusterFormProps> = ({ template, cluster }) => {
previewLoading,
loading,
enableCreatePR,
annotations,
costEstimationLoading,
handleCostEstimation,
costEstimate,
isCredentialEnabled,
isCostEstimationEnabled,
isKustomizationsEnabled,
isProfilesEnabled,
]);
};

Expand Down
17 changes: 17 additions & 0 deletions ui-cra/src/utils/formatters.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { URL } from '../types/global';
import GitUrlParse from 'git-url-parse';
import { CostEstimate } from '../cluster-services/cluster_services.pb';

export const toPercent = (value: number, precision = 0) =>
`${(100 * value).toFixed(precision)}%`;
Expand Down Expand Up @@ -31,3 +32,19 @@ export const getGitRepoHTTPSURL = (
}
return '';
};

export const getFormattedCostEstimate = (
costEstimate: CostEstimate | undefined,
): string => {
const costFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
});
if (costEstimate) {
const { currency, range } = costEstimate;
const estimate = `${costFormatter.format(
range?.low || 0,
)} - ${costFormatter.format(range?.high || 0)} ${currency}`;
return estimate;
} else return 'N/A';
};