Skip to content

Commit 8151543

Browse files
authored
Supported hidden (deleted) properties for rewards and proposals (#5168)
* hide deleted properties * update * disable editing deleted fields * fix tests * update delete modal * allow undeleting * fix tests
1 parent ef0f070 commit 8151543

File tree

16 files changed

+842
-26
lines changed

16 files changed

+842
-26
lines changed

components/common/DatabaseEditor/components/cardDetail/cardDetailProperties.tsx

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Props = {
3939
disableEditPropertyOption?: boolean;
4040
boardType?: 'proposals' | 'rewards';
4141
showCard?: (cardId: string | null) => void;
42+
isTemplate?: boolean; // whether or not to always hide deleted properties
4243
};
4344

4445
function CardDetailProperties(props: Props) {
@@ -52,7 +53,8 @@ function CardDetailProperties(props: Props) {
5253
pageUpdatedBy,
5354
syncWithPageId,
5455
mutator = defaultMutator,
55-
disableEditPropertyOption
56+
disableEditPropertyOption,
57+
isTemplate
5658
} = props;
5759

5860
const [newTemplateId, setNewTemplateId] = useState('');
@@ -196,14 +198,10 @@ function CardDetailProperties(props: Props) {
196198
id: 'CardDetailProperty.confirm-delete-heading',
197199
defaultMessage: 'Confirm Delete Property'
198200
}),
199-
subText: intl.formatMessage(
200-
{
201-
id: 'CardDetailProperty.confirm-delete-subtext',
202-
defaultMessage:
203-
'Are you sure you want to delete the property "{propertyName}"? Deleting it will delete the property from all cards in this board.'
204-
},
205-
{ propertyName: propertyTemplate.name }
206-
),
201+
subText:
202+
board.id === '__defaultBoard' // __defaultBoard type sis used to capture reward and proposal properties
203+
? `Are you sure you want to delete the property "${propertyTemplate.name}"? Deleting it will not destroy data from previous submissions.`
204+
: `Are you sure you want to delete the property "${propertyTemplate.name}"? Deleting it will delete the property from all cards in this board.`,
207205
confirmButtonText: intl.formatMessage({
208206
id: 'CardDetailProperty.delete-action-button',
209207
defaultMessage: 'Delete'
@@ -234,6 +232,14 @@ function CardDetailProperties(props: Props) {
234232
setShowConfirmationDialog(true);
235233
}
236234

235+
// only used for proposal and reward boards at this time. we fully delete the properties of other boards
236+
function restoreProperty(propertyTemplate: IPropertyTemplate) {
237+
mutator.updateProperty(board, propertyTemplate.id, {
238+
...propertyTemplate,
239+
deletedAt: undefined
240+
});
241+
}
242+
237243
function getDeleteDisabled(template: IPropertyTemplate) {
238244
if (
239245
views.some((view) => view.fields.viewType === 'calendar' && view.fields.dateDisplayPropertyId === template.id)
@@ -270,11 +276,13 @@ function CardDetailProperties(props: Props) {
270276
[mutator, props.boardType, board, activeView, isSmallScreen]
271277
);
272278

273-
let boardProperties = board.fields.cardProperties || [];
279+
let boardProperties = (board.fields.cardProperties || [])
280+
// hide deleted properties unless they were deleted before the card was created and it's not a template
281+
.filter((p) => !p.deletedAt || (!isTemplate && new Date(p.deletedAt) > new Date(card.createdAt)));
274282

275283
if (board.fields.sourceType === 'proposals') {
276284
// remove properties that belong to a different template
277-
boardProperties = board.fields?.cardProperties.filter(
285+
boardProperties = boardProperties.filter(
278286
(property) => !property.templateId || card.fields.properties[property.id] !== undefined
279287
);
280288
}
@@ -299,6 +307,7 @@ function CardDetailProperties(props: Props) {
299307
showCard={props.showCard}
300308
deleteDisabledMessage={getDeleteDisabled(propertyTemplate)}
301309
onDelete={() => onPropertyDeleteSetAndOpenConfirmationDialog(propertyTemplate)}
310+
onRestore={() => restoreProperty(propertyTemplate)}
302311
onTypeAndNameChanged={(newType: PropertyType, newName: string, relationData?: RelationPropertyData) => {
303312
onPropertyChangeSetAndOpenConfirmationDialog(newType, newName, propertyTemplate, relationData);
304313
}}

components/common/DatabaseEditor/components/cardProperties/CardDetailProperty.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export function CardDetailProperty({
4040
board,
4141
card,
4242
onDelete,
43+
onRestore,
4344
pageUpdatedBy,
4445
pageUpdatedAt,
4546
deleteDisabledMessage,
@@ -56,6 +57,7 @@ export function CardDetailProperty({
5657
board: Board;
5758
onTypeAndNameChanged: (newType: PropertyType, newName: string, relationData?: RelationPropertyData) => void;
5859
onDelete: VoidFunction;
60+
onRestore: VoidFunction;
5961
pageUpdatedAt: string;
6062
pageUpdatedBy: string;
6163
deleteDisabledMessage?: string;
@@ -66,7 +68,12 @@ export function CardDetailProperty({
6668
}) {
6769
const [isDragging, isOver, columnRef] = useSortable('column', property, !readOnly, onDrop);
6870
const changePropertyPopupState = usePopupState({ variant: 'popover', popupId: 'card-property' });
69-
const propertyTooltip = property.name.length > 20 ? property.name : '';
71+
const propertyTooltip = property.deletedAt
72+
? 'This property was deleted'
73+
: property.name.length > 20
74+
? property.name
75+
: '';
76+
7077
return (
7178
<Stack
7279
ref={columnRef}
@@ -83,7 +90,7 @@ export function CardDetailProperty({
8390
className='octo-propertyrow'
8491
>
8592
{(readOnly || disableEditPropertyOption) && (
86-
<PropertyLabel tooltip={propertyTooltip} readOnly>
93+
<PropertyLabel tooltip={propertyTooltip} readOnly deleted={!!property.deletedAt}>
8794
{property.name}
8895
</PropertyLabel>
8996
)}
@@ -101,14 +108,18 @@ export function CardDetailProperty({
101108
<DragIndicatorIcon className='icons' fontSize='small' color='secondary' />
102109
<Tooltip title={propertyTooltip} disableInteractive>
103110
<span>
104-
<Button>{property.name}</Button>
111+
<Button deleted={!!property.deletedAt}>{property.name}</Button>
105112
</span>
106113
</Tooltip>
107114
</PropertyNameContainer>
108115
<Menu {...bindMenu(changePropertyPopupState)}>
109116
<PropertyMenu
110117
board={board}
111118
onDelete={onDelete}
119+
onRestore={() => {
120+
onRestore();
121+
changePropertyPopupState.close();
122+
}}
112123
deleteDisabled={deleteDisabledMessage?.length !== 0}
113124
property={property}
114125
onTypeAndNameChanged={(newType, newName, relationData) => {

components/common/DatabaseEditor/components/properties/PropertyLabel.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type PropertyLabelProps = {
1111
highlighted?: boolean;
1212
fullWidth?: boolean;
1313
tooltip?: string;
14+
deleted?: boolean; // when showing deleted card properties
1415
};
1516

1617
const Wrapper = styled(({ highlighted, fullWidth, ...props }: any) => <Box {...props} />)<{
@@ -31,7 +32,8 @@ export function PropertyLabel({
3132
fullWidth,
3233
tooltip,
3334
readOnly = true,
34-
highlighted
35+
highlighted,
36+
deleted
3537
}: PropertyLabelProps) {
3638
if (readOnly) {
3739
return (
@@ -42,7 +44,7 @@ export function PropertyLabel({
4244
>
4345
<Tooltip title={tooltip} disableInteractive>
4446
<span>
45-
<Button rightIcon icon={required && <Asterisk>&nbsp;*</Asterisk>}>
47+
<Button rightIcon icon={required && <Asterisk>&nbsp;*</Asterisk>} deleted={deleted}>
4648
{children}
4749
</Button>
4850
</span>

components/common/DatabaseEditor/widgets/buttons/button.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Props = {
1313
rightIcon?: boolean;
1414
disabled?: boolean;
1515
'data-test'?: string;
16+
deleted?: boolean;
1617
};
1718

1819
function Button({ size = 'small', ...props }: Props): JSX.Element {
@@ -31,6 +32,7 @@ function Button({ size = 'small', ...props }: Props): JSX.Element {
3132
title={props.title}
3233
onBlur={props.onBlur}
3334
disabled={props.disabled}
35+
style={{ textDecoration: props.deleted ? 'line-through' : undefined }}
3436
>
3537
{!props.rightIcon && props.icon}
3638
<span>{props.children}</span>

components/common/DatabaseEditor/widgets/propertyMenu.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
22
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
3+
import RestoreIcon from '@mui/icons-material/RestoreOutlined';
34
import {
45
ListItemIcon,
56
ListItemText,
@@ -25,6 +26,7 @@ import { typeDisplayName } from './typeDisplayName';
2526
type Props = {
2627
onTypeAndNameChanged: (newType: PropertyType, newName: string, relationData?: RelationPropertyData) => void;
2728
onDelete: (id: string) => void;
29+
onRestore: (id: string) => void;
2830
deleteDisabled?: boolean;
2931
property: IPropertyTemplate;
3032
board: Board;
@@ -84,12 +86,21 @@ const PropertyMenu = React.memo((props: Props) => {
8486
</Tooltip>
8587
</MenuItem>
8688
<Divider sx={{ my: '0 !important' }} />
87-
<MenuItem onClick={() => props.onDelete(propertyId)}>
88-
<ListItemIcon>
89-
<DeleteOutlinedIcon fontSize='small' />
90-
</ListItemIcon>
91-
<ListItemText>Delete property</ListItemText>
92-
</MenuItem>
89+
{props.property.deletedAt ? (
90+
<MenuItem onClick={() => props.onRestore(propertyId)}>
91+
<ListItemIcon>
92+
<RestoreIcon fontSize='small' />
93+
</ListItemIcon>
94+
<ListItemText>Undelete property</ListItemText>
95+
</MenuItem>
96+
) : (
97+
<MenuItem onClick={() => props.onDelete(propertyId)}>
98+
<ListItemIcon>
99+
<DeleteOutlinedIcon fontSize='small' />
100+
</ListItemIcon>
101+
<ListItemText>Delete property</ListItemText>
102+
</MenuItem>
103+
)}
93104
<Menu
94105
{...bindMenu(changePropertyTypePopupState)}
95106
anchorOrigin={{

components/proposals/ProposalPage/components/ProposalProperties/components/CustomPropertiesAdapter.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export function CustomPropertiesAdapter({ onChange, readOnly, readOnlyProperties
7878
mutator={mutator ?? undefined}
7979
readOnlyProperties={readOnlyProperties}
8080
disableEditPropertyOption={!isAdmin}
81+
isTemplate={!proposalFromDb} // templates are not loaded into the proposalsmap
8182
boardType='proposals'
8283
/>
8384
);

components/proposals/ProposalPage/components/ProposalProperties/proposalsMutator.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ export class ProposalsMutator extends Mutator {
8080
});
8181
}
8282

83+
// to enable undeleting properties
84+
async updateProperty(board: Board, propertyId: string, updatedProperty: IPropertyTemplate) {
85+
this.blocksContext.updateProperty({
86+
...updatedProperty
87+
});
88+
}
89+
8390
async reorderProperties(boardId: string, cardProperties: IPropertyTemplate[]): Promise<void> {
8491
const proposalBoardBlock = this.blocksContext.proposalBoardBlock;
8592
const oldFields = proposalBoardBlock?.fields || {};

components/rewards/components/RewardProperties/CustomPropertiesAdapter.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export function CustomPropertiesAdapter({ reward, onChange, readOnly }: Props) {
8787
mutator={mutator ?? undefined}
8888
disableEditPropertyOption={!isAdmin}
8989
boardType='rewards'
90+
isTemplate={rewardPage?.type === 'bounty_template'}
9091
/>
9192
);
9293
}

components/rewards/components/RewardProperties/rewardsMutator.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ export class RewardsMutator extends Mutator {
8080
});
8181
}
8282

83+
// to enable undeleting properties
84+
async updateProperty(board: Board, propertyId: string, updatedProperty: IPropertyTemplate) {
85+
this.blocksContext.updateProperty({
86+
...updatedProperty
87+
});
88+
}
89+
8390
async reorderProperties(boardId: string, cardProperties: IPropertyTemplate[]): Promise<void> {
8491
const rewardsBoardBlock = this.blocksContext.rewardsBoardBlock;
8592
const oldFields = rewardsBoardBlock?.fields || {};

components/rewards/hooks/useRewardsBoard.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,14 @@ export function useRewardsBoard() {
126126
return;
127127
}
128128

129-
const updatedProperties = dbBlock.fields.cardProperties.filter((p) => p.id !== propertyTemplateId);
129+
const updatedProperties = dbBlock.fields.cardProperties.map((p) =>
130+
p.id === propertyTemplateId
131+
? {
132+
...p,
133+
deletedAt: new Date().toISOString()
134+
}
135+
: p
136+
);
130137
const updatedBlock = {
131138
...dbBlock,
132139
fields: { ...(dbBlock.fields as BoardFields), cardProperties: updatedProperties }

0 commit comments

Comments
 (0)