Skip to content
Snippets Groups Projects
Unverified Commit 17e20c17 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Allow archiving apps (#25580)

* Remove collection actions not suitable for apps

* Allow archiving apps
parent 0967b7cb
No related branches found
No related tags found
No related merge requests found
...@@ -27,4 +27,5 @@ export type GetState = () => State; ...@@ -27,4 +27,5 @@ export type GetState = () => State;
export type ReduxAction<Type = string, Payload = any> = { export type ReduxAction<Type = string, Payload = any> = {
type: Type; type: Type;
payload: Payload; payload: Payload;
error?: string;
}; };
...@@ -2,8 +2,6 @@ import React from "react"; ...@@ -2,8 +2,6 @@ import React from "react";
import { withRouter } from "react-router"; import { withRouter } from "react-router";
import type { Location } from "history"; import type { Location } from "history";
import * as Urls from "metabase/lib/urls";
import { isDataAppCollection } from "metabase/entities/data-apps"; import { isDataAppCollection } from "metabase/entities/data-apps";
import { Collection } from "metabase-types/api"; import { Collection } from "metabase-types/api";
...@@ -37,6 +35,8 @@ const CollectionHeader = ({ ...@@ -37,6 +35,8 @@ const CollectionHeader = ({
onCreateBookmark, onCreateBookmark,
onDeleteBookmark, onDeleteBookmark,
}: CollectionHeaderProps): JSX.Element => { }: CollectionHeaderProps): JSX.Element => {
const isDataApp = isDataAppCollection(collection);
return ( return (
<HeaderRoot> <HeaderRoot>
<CollectionCaption <CollectionCaption
...@@ -44,10 +44,7 @@ const CollectionHeader = ({ ...@@ -44,10 +44,7 @@ const CollectionHeader = ({
onUpdateCollection={onUpdateCollection} onUpdateCollection={onUpdateCollection}
/> />
<HeaderActions data-testid="collection-menu"> <HeaderActions data-testid="collection-menu">
{isDataAppCollection(collection) && {isDataApp && <LaunchDataAppButton collection={collection} />}
Urls.isDataAppPreviewPath(location.pathname) && (
<LaunchDataAppButton collection={collection} />
)}
<CollectionTimeline collection={collection} /> <CollectionTimeline collection={collection} />
<CollectionBookmark <CollectionBookmark
collection={collection} collection={collection}
...@@ -58,6 +55,7 @@ const CollectionHeader = ({ ...@@ -58,6 +55,7 @@ const CollectionHeader = ({
<CollectionMenu <CollectionMenu
collection={collection} collection={collection}
isAdmin={isAdmin} isAdmin={isAdmin}
isDataApp={isDataApp}
isPersonalCollectionChild={isPersonalCollectionChild} isPersonalCollectionChild={isPersonalCollectionChild}
onUpdateCollection={onUpdateCollection} onUpdateCollection={onUpdateCollection}
/> />
......
import React from "react"; import React from "react";
import { t } from "ttag"; import { t } from "ttag";
import _ from "underscore";
import { PLUGIN_COLLECTIONS } from "metabase/plugins"; import { PLUGIN_COLLECTIONS } from "metabase/plugins";
import * as Urls from "metabase/lib/urls"; import * as Urls from "metabase/lib/urls";
import EntityMenu from "metabase/components/EntityMenu"; import EntityMenu from "metabase/components/EntityMenu";
...@@ -14,6 +16,7 @@ export interface CollectionMenuProps { ...@@ -14,6 +16,7 @@ export interface CollectionMenuProps {
collection: Collection; collection: Collection;
isAdmin: boolean; isAdmin: boolean;
isPersonalCollectionChild: boolean; isPersonalCollectionChild: boolean;
isDataApp: boolean;
onUpdateCollection: (entity: Collection, values: Partial<Collection>) => void; onUpdateCollection: (entity: Collection, values: Partial<Collection>) => void;
} }
...@@ -21,10 +24,13 @@ const CollectionMenu = ({ ...@@ -21,10 +24,13 @@ const CollectionMenu = ({
collection, collection,
isAdmin, isAdmin,
isPersonalCollectionChild, isPersonalCollectionChild,
isDataApp,
onUpdateCollection, onUpdateCollection,
}: CollectionMenuProps): JSX.Element | null => { }: CollectionMenuProps): JSX.Element | null => {
const items = []; const items = [];
const url = Urls.collection(collection); const url = isDataApp
? Urls.collection(_.omit(collection, "app_id"))
: Urls.collection(collection);
const isRoot = isRootCollection(collection); const isRoot = isRootCollection(collection);
const isPersonal = isPersonalCollection(collection); const isPersonal = isPersonalCollection(collection);
const canWrite = collection.can_write; const canWrite = collection.can_write;
...@@ -38,7 +44,7 @@ const CollectionMenu = ({ ...@@ -38,7 +44,7 @@ const CollectionMenu = ({
); );
} }
if (isAdmin && !isPersonal && !isPersonalCollectionChild) { if (isAdmin && !isPersonal && !isPersonalCollectionChild && !isDataApp) {
items.push({ items.push({
title: t`Edit permissions`, title: t`Edit permissions`,
icon: "lock", icon: "lock",
...@@ -48,20 +54,20 @@ const CollectionMenu = ({ ...@@ -48,20 +54,20 @@ const CollectionMenu = ({
} }
if (!isRoot && !isPersonal && canWrite) { if (!isRoot && !isPersonal && canWrite) {
items.push( if (!isDataApp) {
{ items.push({
title: t`Move`, title: t`Move`,
icon: "move", icon: "move",
link: `${url}/move`, link: `${url}/move`,
event: `${ANALYTICS_CONTEXT};Edit Menu;Move Collection`, event: `${ANALYTICS_CONTEXT};Edit Menu;Move Collection`,
}, });
{ }
title: t`Archive`, items.push({
icon: "archive", title: t`Archive`,
link: `${url}/archive`, icon: "archive",
event: `${ANALYTICS_CONTEXT};Edit Menu;Archive Collection`, link: `${url}/archive`,
}, event: `${ANALYTICS_CONTEXT};Edit Menu;Archive Collection`,
); });
} }
if (items.length > 0) { if (items.length > 0) {
......
...@@ -10,6 +10,7 @@ import { Collection, DataApp, DataAppSearchItem } from "metabase-types/api"; ...@@ -10,6 +10,7 @@ import { Collection, DataApp, DataAppSearchItem } from "metabase-types/api";
import { DEFAULT_COLLECTION_COLOR_ALIAS } from "../collections/constants"; import { DEFAULT_COLLECTION_COLOR_ALIAS } from "../collections/constants";
import { createNewAppForm, createAppSettingsForm } from "./forms"; import { createNewAppForm, createAppSettingsForm } from "./forms";
import reducer from "./reducer";
import { getDataAppIcon } from "./utils"; import { getDataAppIcon } from "./utils";
type EditableDataAppParams = Pick< type EditableDataAppParams = Pick<
...@@ -109,6 +110,8 @@ const DataApps = createEntity({ ...@@ -109,6 +110,8 @@ const DataApps = createEntity({
fields: createAppSettingsForm, fields: createAppSettingsForm,
}, },
}, },
reducer,
}); });
export * from "./utils"; export * from "./utils";
......
import _ from "underscore";
import Collections from "metabase/entities/collections";
import type {
Collection,
RegularCollectionId,
DataApp,
DataAppId,
} from "metabase-types/api";
import type { ReduxAction } from "metabase-types/store";
type CollectionUpdateActionPayload = {
collection: Collection;
object: Collection;
entities: {
collections: Record<RegularCollectionId, Collection>;
};
};
type NormalizedDataApp = Omit<DataApp, "collection">;
type DataAppsState = {
[id: DataAppId]: NormalizedDataApp;
};
function reducer(
state: DataAppsState = {},
{ type, payload, error }: ReduxAction,
) {
if (type === Collections.actionTypes.UPDATE && !error) {
const { collection } = payload as CollectionUpdateActionPayload;
if (collection.archived) {
const apps = Object.values(state);
const app = _.findWhere(apps, { collection_id: collection.id as number });
return app ? _.omit(state, app.id as any) : state;
}
}
return state;
}
export default reducer;
import {
createMockDataApp,
createMockCollection,
} from "metabase-types/api/mocks";
import type { Collection } from "metabase-types/api";
import Collections from "../collections";
import DataApps from "./data-apps";
function getCollectionUpdateAction({
collection,
error,
}: {
collection: Collection;
error?: string;
}) {
return {
type: Collections.actionTypes.UPDATE,
payload: {
collection,
object: collection,
entities: {
[collection.id]: collection,
},
},
error,
};
}
describe("entities > data apps > reducer", () => {
describe("archiving", () => {
const collection = createMockCollection({ id: 5 });
const dataApp = createMockDataApp({ id: 1, collection });
const state = { [dataApp.id]: dataApp };
it("should remove data app when underlying collection is archived", () => {
const anotherDataApp = createMockDataApp({ id: 2 });
const nextState = DataApps.reducer(
{ ...state, [anotherDataApp.id]: anotherDataApp },
getCollectionUpdateAction({
collection: { ...collection, archived: true },
}),
);
expect(nextState).toEqual({ [anotherDataApp.id]: anotherDataApp });
});
it("should ignore failed update actions", () => {
const nextState = DataApps.reducer(
state,
getCollectionUpdateAction({
collection: { ...collection, archived: true },
error: "Something went wrong",
}),
);
expect(nextState).toEqual(state);
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment