-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(billable-metric): allow to edit groups and warn on change
- Loading branch information
Showing
12 changed files
with
803 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
src/components/billableMetrics/EditBillableMetricGroupDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { forwardRef, useImperativeHandle, useRef, useState } from 'react' | ||
|
||
import { DialogRef } from '~/components/designSystem' | ||
import { WarningDialog, WarningDialogMode } from '~/components/WarningDialog' | ||
import { useInternationalization } from '~/hooks/core/useInternationalization' | ||
|
||
interface EditBillableMetricGroupDialogProps { | ||
mode: WarningDialogMode | ||
onContinue: () => unknown | ||
plansCount?: number | ||
subscriptionsCount?: number | ||
} | ||
|
||
export interface EditBillableMetricGroupDialogRef { | ||
openDialog: ({ | ||
onContinue, | ||
plansCount, | ||
subscriptionsCount, | ||
}: EditBillableMetricGroupDialogProps) => unknown | ||
closeDialog: () => unknown | ||
} | ||
|
||
export const EditBillableMetricGroupDialog = forwardRef<EditBillableMetricGroupDialogRef>( | ||
(_, ref) => { | ||
const dialogRef = useRef<DialogRef>(null) | ||
const { translate } = useInternationalization() | ||
const [data, setData] = useState<EditBillableMetricGroupDialogProps | undefined>(undefined) | ||
|
||
const { mode, onContinue, plansCount, subscriptionsCount } = data || {} | ||
|
||
useImperativeHandle(ref, () => ({ | ||
openDialog: (infos) => { | ||
setData(infos) | ||
dialogRef.current?.openDialog() | ||
}, | ||
closeDialog: () => dialogRef.current?.closeDialog(), | ||
})) | ||
|
||
return ( | ||
<WarningDialog | ||
mode={mode} | ||
ref={dialogRef} | ||
title={translate('text_64f2044bd3655501184fe142')} | ||
description={ | ||
mode === WarningDialogMode.danger ? ( | ||
translate('text_64f1e90251fc8c40b9174943', { plansCount, subscriptionsCount }) | ||
) : ( | ||
<> | ||
{translate('text_64f2044bd3655501184fe143', { plansCount, subscriptionsCount })} | ||
<ul> | ||
<li>{translate('text_64f2044bd3655501184fe144')}</li> | ||
<li>{translate('text_64f2044bd3655501184fe145')}</li> | ||
</ul> | ||
</> | ||
) | ||
} | ||
onContinue={async () => onContinue && onContinue()} | ||
continueText={translate('text_64f2044bd3655501184fe147')} | ||
/> | ||
) | ||
} | ||
) | ||
|
||
EditBillableMetricGroupDialog.displayName = 'EditBillableMetricGroupDialog' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
export const GroupLevelEnum = { | ||
NoChange: 'NoChange', | ||
AddOrRemove: 'AddOrRemove', | ||
StructuralChange: 'StructuralChange', | ||
} as const | ||
|
||
type determineGroupDiffLevelReturnType = | ||
| (typeof GroupLevelEnum)[keyof typeof GroupLevelEnum] | ||
| undefined | ||
type oneDimensionGroupType = { | ||
key: string | ||
values: string[] | ||
} | ||
type twoDimensionGroupType = { | ||
key: string | ||
values: { | ||
name: string | ||
key: string | ||
values: string[] | ||
}[] | ||
} | ||
type groupType = oneDimensionGroupType | twoDimensionGroupType | {} | ||
|
||
const isValidJSON = (string: string) => { | ||
try { | ||
JSON.parse(string) | ||
} catch (e) { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
const isContainingObjectValues = (object: twoDimensionGroupType): boolean => { | ||
if (!object) return false | ||
return object.values?.every((value) => typeof value === 'object') | ||
} | ||
|
||
const isContainingStringValues = (object: oneDimensionGroupType): boolean => { | ||
if (!object) return false | ||
return object.values?.every((value) => typeof value === 'string') | ||
} | ||
|
||
export const isOneDimension = (object: oneDimensionGroupType): boolean => { | ||
if (!object) return false | ||
|
||
return ( | ||
!!object.key && !!object.values && !!object.values.length && !!isContainingStringValues(object) | ||
) | ||
} | ||
|
||
export const isTwoDimension = (object: twoDimensionGroupType): boolean => { | ||
if (!object) return false | ||
|
||
return ( | ||
!!object.key && !!object.values && !!object.values.length && !!isContainingObjectValues(object) | ||
) | ||
} | ||
|
||
export const isGroupValid = (object: groupType) => | ||
!!object && object !== '{}' && isValidJSON(JSON.stringify(object)) | ||
|
||
const areGroupsOneDimension = ( | ||
group1: oneDimensionGroupType, | ||
group2: oneDimensionGroupType | ||
): boolean => { | ||
return isOneDimension(group1) && isOneDimension(group2) | ||
} | ||
|
||
const areGroupsTwoDimension = ( | ||
group1: twoDimensionGroupType, | ||
group2: twoDimensionGroupType | ||
): boolean => { | ||
return isTwoDimension(group1) && isTwoDimension(group2) | ||
} | ||
|
||
export const determineGroupDiffLevel: ( | ||
group1: groupType | string, | ||
group2: groupType | string | ||
) => determineGroupDiffLevelReturnType = (group1 = {}, group2 = {}) => { | ||
// Groups can be empty, replace them with empty object | ||
if (group1 === '') group1 = '{}' | ||
if (group2 === '') group2 = '{}' | ||
|
||
// Key stringify/parse to shallow copy the value | ||
const parsedGroup1 = | ||
typeof group1 === 'string' ? JSON.parse(group1) : JSON.parse(JSON.stringify(group1)) | ||
const parsedGroup2 = | ||
typeof group2 === 'string' ? JSON.parse(group2) : JSON.parse(JSON.stringify(group2)) | ||
|
||
// Check if one of the groups are both valid | ||
if (!isGroupValid(parsedGroup1) || !isGroupValid(parsedGroup2)) { | ||
return GroupLevelEnum.StructuralChange | ||
} | ||
|
||
// Check if groups have the same dimension | ||
if ( | ||
!( | ||
areGroupsTwoDimension(parsedGroup1, parsedGroup2) || | ||
areGroupsOneDimension(parsedGroup1, parsedGroup2) | ||
) | ||
) { | ||
return GroupLevelEnum.StructuralChange | ||
} | ||
|
||
// Check if group has value change (added or removed) | ||
if (areGroupsTwoDimension(parsedGroup1, parsedGroup2)) { | ||
const parsedGroup1ValuesKeys = (parsedGroup1 as twoDimensionGroupType).values | ||
.map((value) => value.key) | ||
.sort() | ||
const parsedGroup2ValuesKeys = (parsedGroup2 as twoDimensionGroupType).values | ||
.map((value) => value.key) | ||
.sort() | ||
const parsedGroup1ValuesNames = (parsedGroup1 as twoDimensionGroupType).values | ||
.map((value) => value.name) | ||
.sort() | ||
const parsedGroup2ValuesNames = (parsedGroup2 as twoDimensionGroupType).values | ||
.map((value) => value.name) | ||
.sort() | ||
const parsedGroup1ValuesValues = (parsedGroup1 as twoDimensionGroupType).values | ||
.map((value) => value.values.sort()) | ||
.sort() | ||
const parsedGroup2ValuesValues = (parsedGroup2 as twoDimensionGroupType).values | ||
.map((value) => value.values.sort()) | ||
.sort() | ||
|
||
if ( | ||
parsedGroup1.key !== parsedGroup2.key || | ||
JSON.stringify(parsedGroup1ValuesKeys) !== JSON.stringify(parsedGroup2ValuesKeys) || | ||
JSON.stringify(parsedGroup1ValuesNames) !== JSON.stringify(parsedGroup2ValuesNames) || | ||
JSON.stringify(parsedGroup1ValuesValues) !== JSON.stringify(parsedGroup2ValuesValues) | ||
) | ||
return GroupLevelEnum.AddOrRemove | ||
} else if (areGroupsOneDimension(parsedGroup1, parsedGroup2)) { | ||
const parsedGroup1Values = (parsedGroup1 as oneDimensionGroupType).values.sort() | ||
const parsedGroup2Values = (parsedGroup2 as oneDimensionGroupType).values.sort() | ||
|
||
if ( | ||
parsedGroup1.key !== parsedGroup2.key || | ||
JSON.stringify(parsedGroup1Values) !== JSON.stringify(parsedGroup2Values) | ||
) | ||
return GroupLevelEnum.AddOrRemove | ||
} | ||
|
||
return GroupLevelEnum.NoChange | ||
} |
Oops, something went wrong.