--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export const deleteProperty = (properties: any, key: string, value: string) => {
+ if (Array.isArray(properties[key])) {
+ properties[key] = properties[key].filter((v: string) => v !== value);
+ if (properties[key].length === 1) {
+ properties[key] = properties[key][0];
+ } else if (properties[key].length === 0) {
+ delete properties[key];
+ }
+ } else if (properties[key] === value) {
+ delete properties[key];
+ }
+ return properties;
+}
+
+export const addProperty = (properties: any, key: string, value: string) => {
+ if (properties[key]) {
+ if (Array.isArray(properties[key])) {
+ properties[key] = [...properties[key], value];
+ } else {
+ properties[key] = [properties[key], value];
+ }
+ // Remove potential duplicate and save as single value if needed
+ properties[key] = Array.from(new Set(properties[key]));
+ if (properties[key].length === 1) {
+ properties[key] = properties[key][0];
+ }
+ } else {
+ properties[key] = value;
+ }
+ return properties;
+}
\ No newline at end of file
import { SnackbarKind } from '~/store/snackbar/snackbar-actions';
import { navigateTo } from '~/store/navigation/navigation-action';
import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
+import { deleteProperty, addProperty } from "~/lib/resource-properties";
export const collectionPanelActions = unionize({
SET_COLLECTION: ofType<CollectionResource>(),
if (item) {
const key = data.keyID || data.key;
const value = data.valueID || data.value;
- if (item.properties[key]) {
- if (Array.isArray(item.properties[key])) {
- item.properties[key] = [...item.properties[key], value];
- // Remove potential duplicates
- item.properties[key] = Array.from(new Set(item.properties[key]));
- } else {
- item.properties[key] = [item.properties[key], value];
- }
- } else {
- item.properties[key] = value;
- }
+ item.properties = addProperty(item.properties, key, value);
const updatedCollection = await services.collectionService.update(
uuid, {
- properties: {...JSON.parse(JSON.stringify(item.properties))}
+ properties: {...item.properties}
}
);
item.properties = updatedCollection.properties;
const uuid = item ? item.uuid : '';
try {
if (item) {
- if (Array.isArray(item.properties[key])) {
- item.properties[key] = item.properties[key].filter((v: string) => v !== value);
- if (item.properties[key].length === 1) {
- item.properties[key] = item.properties[key][0];
- } else if (item.properties[key].length === 0) {
- delete item.properties[key];
- }
- } else if (item.properties[key] === value) {
- delete item.properties[key];
- }
+ item.properties = deleteProperty(item.properties, key, value);
const updatedCollection = await services.collectionService.update(
uuid, {
import { startSubmit, stopSubmit } from 'redux-form';
import { resourcesActions } from '~/store/resources/resources-actions';
import {snackbarActions, SnackbarKind} from '~/store/snackbar/snackbar-actions';
+import { addProperty, deleteProperty } from '~/lib/resource-properties';
export const SLIDE_TIMEOUT = 500;
dispatch<any>(dialogActions.OPEN_DIALOG({ id: PROJECT_PROPERTIES_DIALOG_NAME, data: { } }));
};
-export const deleteProjectProperty = (key: string) =>
+export const deleteProjectProperty = (key: string, value: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const { detailsPanel, resources } = getState();
const project = getResource(detailsPanel.resourceUuid)(resources) as ProjectResource;
try {
if (project) {
- delete project.properties[key];
+ project.properties = deleteProperty(project.properties, key, value);
const updatedProject = await services.projectService.update(project.uuid, { properties: project.properties });
dispatch(resourcesActions.SET_RESOURCES([updatedProject]));
dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Property has been successfully deleted.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
dispatch(startSubmit(PROJECT_PROPERTIES_FORM_NAME));
try {
if (project) {
+ const key = data.keyID || data.key;
+ const value = data.valueID || data.value;
+ project.properties = addProperty(project.properties, key, value);
const updatedProject = await services.projectService.update(
project.uuid, {
- properties: {
- ...JSON.parse(JSON.stringify(project.properties)),
- [data.keyID || data.key]: data.valueID || data.value
- }
+ properties: {...project.properties}
}
);
dispatch(resourcesActions.SET_RESOURCES([updatedProject]));
import { withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
import { ArvadosTheme } from '~/common/custom-theme';
import { Dispatch } from 'redux';
-import { PropertyChipComponent } from '../resource-properties-form/property-chip';
+import { getPropertyChip } from '../resource-properties-form/property-chip';
export class ProjectDetails extends DetailsData<ProjectResource> {
getIcon(className?: string) {
</DetailsAttribute>
{
Object.keys(project.properties).map(k =>
- <PropertyChipComponent key={k}
- propKey={k} propValue={project.properties[k]}
- className={classes.tag} />
+ Array.isArray(project.properties[k])
+ ? project.properties[k].map((v: string) =>
+ getPropertyChip(k, v, undefined, classes.tag))
+ : getPropertyChip(k, project.properties[k], undefined, classes.tag)
)
}
</div>
import { ArvadosTheme } from '~/common/custom-theme';
import { ProjectPropertiesForm } from '~/views-components/project-properties-dialog/project-properties-form';
import { getResource } from '~/store/resources/resources';
-import { PropertyChipComponent } from "../resource-properties-form/property-chip";
+import { getPropertyChip } from "../resource-properties-form/property-chip";
type CssRules = 'tag';
}
interface ProjectPropertiesDialogActionProps {
- handleDelete: (key: string) => void;
+ handleDelete: (key: string, value: string) => void;
}
const mapStateToProps = ({ detailsPanel, resources, properties }: RootState): ProjectPropertiesDialogDataProps => ({
});
const mapDispatchToProps = (dispatch: Dispatch): ProjectPropertiesDialogActionProps => ({
- handleDelete: (key: string) => dispatch<any>(deleteProjectProperty(key)),
+ handleDelete: (key: string, value: string) => () => dispatch<any>(deleteProjectProperty(key, value)),
});
type ProjectPropertiesDialogProps = ProjectPropertiesDialogDataProps & ProjectPropertiesDialogActionProps & WithDialogProps<{}> & WithStyles<CssRules>;
<ProjectPropertiesForm />
{project && project.properties &&
Object.keys(project.properties).map(k =>
- <PropertyChipComponent
- onDelete={() => handleDelete(k)}
- key={k} className={classes.tag}
- propKey={k} propValue={project.properties[k]} />)
+ Array.isArray(project.properties[k])
+ ? project.properties[k].map((v: string) =>
+ getPropertyChip(
+ k, v,
+ handleDelete(k, v),
+ classes.tag))
+ : getPropertyChip(
+ k, project.properties[k],
+ handleDelete(k, project.properties[k]),
+ classes.tag)
+ )
}
</DialogContent>
<DialogActions>
);
}
);
+
+export const getPropertyChip = (k:string, v:string, handleDelete:any, className:string) =>
+ <PropertyChipComponent
+ key={k} className={className}
+ onDelete={handleDelete}
+ propKey={k} propValue={v} />;
import { formatFileSize } from "~/common/formatters";
import { openDetailsPanel } from '~/store/details-panel/details-panel-action';
import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { PropertyChipComponent } from '~/views-components/resource-properties-form/property-chip';
+import { getPropertyChip } from '~/views-components/resource-properties-form/property-chip';
import { IllegalNamingWarning } from '~/components/warning/warning';
type CssRules = 'card' | 'iconHeader' | 'tag' | 'label' | 'value' | 'link';
{Object.keys(item.properties).map(k =>
Array.isArray(item.properties[k])
? item.properties[k].map((v: string) =>
- getPropertyChip(k, v, this.handleDelete, classes.tag))
- : getPropertyChip(k, item.properties[k], this.handleDelete, classes.tag)
+ getPropertyChip(
+ k, v,
+ this.handleDelete(k, v),
+ classes.tag))
+ : getPropertyChip(
+ k, item.properties[k],
+ this.handleDelete(k, item.properties[k]),
+ classes.tag)
)}
</Grid>
</Grid>
}
)
);
-
-const getPropertyChip = (k:string, v:string, handleDelete:any, className:string) =>
- <PropertyChipComponent
- key={k} className={className}
- onDelete={handleDelete(k, v)}
- propKey={k} propValue={v} />;
-