mirror of
https://github.com/datahub-project/datahub.git
synced 2025-11-11 08:52:58 +00:00
feat(homepage): add reset to default template functionality and update home page settings option (#14202)
This commit is contained in:
parent
bcee2a4fb7
commit
b1ef86b83e
@ -102,6 +102,9 @@ public class MeResolver implements DataFetcher<CompletableFuture<AuthenticatedUs
|
|||||||
platformPrivileges.setManageApplications(
|
platformPrivileges.setManageApplications(
|
||||||
ApplicationAuthorizationUtils.canManageApplications(context));
|
ApplicationAuthorizationUtils.canManageApplications(context));
|
||||||
platformPrivileges.setManageFeatures(AuthorizationUtils.canManageFeatures(context));
|
platformPrivileges.setManageFeatures(AuthorizationUtils.canManageFeatures(context));
|
||||||
|
platformPrivileges.setManageHomePageTemplates(
|
||||||
|
AuthorizationUtils.canManageHomePageTemplates(context));
|
||||||
|
|
||||||
// Construct and return authenticated user object.
|
// Construct and return authenticated user object.
|
||||||
final AuthenticatedUser authUser = new AuthenticatedUser();
|
final AuthenticatedUser authUser = new AuthenticatedUser();
|
||||||
authUser.setCorpUser(corpUser);
|
authUser.setCorpUser(corpUser);
|
||||||
|
|||||||
@ -86,7 +86,16 @@ public class UpdateUserHomePageSettingsResolver implements DataFetcher<Completab
|
|||||||
@Nonnull final CorpUserHomePageSettings settings,
|
@Nonnull final CorpUserHomePageSettings settings,
|
||||||
@Nonnull final UpdateUserHomePageSettingsInput input) {
|
@Nonnull final UpdateUserHomePageSettingsInput input) {
|
||||||
|
|
||||||
if (input.getPageTemplate() != null) {
|
Boolean shouldRemoveTemplate = input.getRemovePageTemplate();
|
||||||
|
|
||||||
|
if (input.getPageTemplate() != null && Boolean.TRUE.equals(shouldRemoveTemplate)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid inputs: Cannot specify both pageTemplate and removePageTemplate.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(shouldRemoveTemplate)) {
|
||||||
|
settings.data().remove("pageTemplate");
|
||||||
|
} else if (input.getPageTemplate() != null) {
|
||||||
settings.setPageTemplate(UrnUtils.getUrn(input.getPageTemplate()));
|
settings.setPageTemplate(UrnUtils.getUrn(input.getPageTemplate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -169,9 +169,11 @@ public class CorpUserMapper {
|
|||||||
@Nonnull final com.linkedin.identity.CorpUserHomePageSettings homePageSettings) {
|
@Nonnull final com.linkedin.identity.CorpUserHomePageSettings homePageSettings) {
|
||||||
CorpUserHomePageSettings result = new CorpUserHomePageSettings();
|
CorpUserHomePageSettings result = new CorpUserHomePageSettings();
|
||||||
|
|
||||||
if (homePageSettings.hasPageTemplate()) {
|
if (homePageSettings.getPageTemplate() != null) {
|
||||||
result.setPageTemplate(
|
result.setPageTemplate(
|
||||||
(DataHubPageTemplate) UrnToEntityMapper.map(null, homePageSettings.getPageTemplate()));
|
(DataHubPageTemplate) UrnToEntityMapper.map(null, homePageSettings.getPageTemplate()));
|
||||||
|
} else {
|
||||||
|
result.setPageTemplate(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (homePageSettings.hasDismissedAnnouncements()) {
|
if (homePageSettings.hasDismissedAnnouncements()) {
|
||||||
|
|||||||
@ -192,6 +192,11 @@ type PlatformPrivileges {
|
|||||||
Whether the user can manage platform features.
|
Whether the user can manage platform features.
|
||||||
"""
|
"""
|
||||||
manageFeatures: Boolean!
|
manageFeatures: Boolean!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Whether the user can manage default home page template.
|
||||||
|
"""
|
||||||
|
manageHomePageTemplates: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -12503,6 +12503,11 @@ input UpdateUserHomePageSettingsInput {
|
|||||||
The list of urns of announcement posts dismissed by the user.
|
The list of urns of announcement posts dismissed by the user.
|
||||||
"""
|
"""
|
||||||
newDismissedAnnouncements: [String]
|
newDismissedAnnouncements: [String]
|
||||||
|
|
||||||
|
"""
|
||||||
|
Whether to remove the page template for the user.
|
||||||
|
"""
|
||||||
|
removePageTemplate: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -3741,6 +3741,7 @@ export const mocks = [
|
|||||||
viewStructuredPropertiesPage: true,
|
viewStructuredPropertiesPage: true,
|
||||||
manageApplications: true,
|
manageApplications: true,
|
||||||
manageFeatures: true,
|
manageFeatures: true,
|
||||||
|
manageHomePageTemplates: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -4027,6 +4028,7 @@ export const platformPrivileges: PlatformPrivileges = {
|
|||||||
viewStructuredPropertiesPage: true,
|
viewStructuredPropertiesPage: true,
|
||||||
manageApplications: true,
|
manageApplications: true,
|
||||||
manageFeatures: true,
|
manageFeatures: true,
|
||||||
|
manageHomePageTemplates: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DomainMock1 = {
|
export const DomainMock1 = {
|
||||||
|
|||||||
@ -89,6 +89,7 @@ describe('handleAccessRoles', () => {
|
|||||||
viewStructuredPropertiesPage: true,
|
viewStructuredPropertiesPage: true,
|
||||||
manageApplications: true,
|
manageApplications: true,
|
||||||
manageFeatures: true,
|
manageFeatures: true,
|
||||||
|
manageHomePageTemplates: true,
|
||||||
__typename: 'PlatformPrivileges',
|
__typename: 'PlatformPrivileges',
|
||||||
},
|
},
|
||||||
__typename: 'AuthenticatedUser',
|
__typename: 'AuthenticatedUser',
|
||||||
@ -174,6 +175,7 @@ describe('handleAccessRoles', () => {
|
|||||||
viewStructuredPropertiesPage: true,
|
viewStructuredPropertiesPage: true,
|
||||||
manageApplications: true,
|
manageApplications: true,
|
||||||
manageFeatures: true,
|
manageFeatures: true,
|
||||||
|
manageHomePageTemplates: true,
|
||||||
__typename: 'PlatformPrivileges',
|
__typename: 'PlatformPrivileges',
|
||||||
},
|
},
|
||||||
__typename: 'AuthenticatedUser',
|
__typename: 'AuthenticatedUser',
|
||||||
@ -267,6 +269,7 @@ describe('handleAccessRoles', () => {
|
|||||||
viewStructuredPropertiesPage: true,
|
viewStructuredPropertiesPage: true,
|
||||||
manageApplications: true,
|
manageApplications: true,
|
||||||
manageFeatures: true,
|
manageFeatures: true,
|
||||||
|
manageHomePageTemplates: true,
|
||||||
__typename: 'PlatformPrivileges',
|
__typename: 'PlatformPrivileges',
|
||||||
},
|
},
|
||||||
__typename: 'AuthenticatedUser',
|
__typename: 'AuthenticatedUser',
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
import { Button, Tooltip } from '@components';
|
|
||||||
import React, { useCallback } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import analytics, { EventType } from '@app/analytics';
|
|
||||||
import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext';
|
|
||||||
|
|
||||||
const ButtonWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin-right: 42px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default function EditDefaultTemplateButton() {
|
|
||||||
const { setIsEditingGlobalTemplate, isEditingGlobalTemplate } = usePageTemplateContext();
|
|
||||||
|
|
||||||
const onClick = useCallback(() => {
|
|
||||||
setIsEditingGlobalTemplate(true);
|
|
||||||
analytics.event({
|
|
||||||
type: EventType.HomePageTemplateGlobalTemplateEditingStart,
|
|
||||||
});
|
|
||||||
}, [setIsEditingGlobalTemplate]);
|
|
||||||
|
|
||||||
// TODO: also hide this if you don't have permissions - CH-510
|
|
||||||
if (isEditingGlobalTemplate) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ButtonWrapper>
|
|
||||||
<Tooltip title="Edit the home page that users see by default">
|
|
||||||
<Button
|
|
||||||
icon={{ icon: 'PencilSimpleLine', color: 'gray', source: 'phosphor' }}
|
|
||||||
variant="text"
|
|
||||||
onClick={onClick}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</ButtonWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import EditDefaultTemplateBar from '@app/homeV3/EditDefaultTemplateBar';
|
|
||||||
import EditDefaultTemplateButton from '@app/homeV3/EditDefaultTemplateButton';
|
|
||||||
import { Announcements } from '@app/homeV3/announcements/Announcements';
|
import { Announcements } from '@app/homeV3/announcements/Announcements';
|
||||||
|
import EditDefaultTemplateBar from '@app/homeV3/settings/EditDefaultTemplateBar';
|
||||||
|
import EditHomePageSettingsButton from '@app/homeV3/settings/EditHomePageSettingsButton';
|
||||||
import { CenteredContainer, ContentContainer, ContentDiv } from '@app/homeV3/styledComponents';
|
import { CenteredContainer, ContentContainer, ContentDiv } from '@app/homeV3/styledComponents';
|
||||||
import Template from '@app/homeV3/template/Template';
|
import Template from '@app/homeV3/template/Template';
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ const HomePageContent = () => {
|
|||||||
<ContentContainer>
|
<ContentContainer>
|
||||||
<CenteredContainer>
|
<CenteredContainer>
|
||||||
<ContentDiv>
|
<ContentDiv>
|
||||||
<EditDefaultTemplateButton />
|
<EditHomePageSettingsButton />
|
||||||
<Announcements />
|
<Announcements />
|
||||||
<Template />
|
<Template />
|
||||||
<EditDefaultTemplateBar />
|
<EditDefaultTemplateBar />
|
||||||
|
|||||||
@ -22,7 +22,8 @@ export const PageTemplateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
} = useTemplateState();
|
} = useTemplateState();
|
||||||
|
|
||||||
// Template operations
|
// Template operations
|
||||||
const { updateTemplateWithModule, removeModuleFromTemplate, upsertTemplate } = useTemplateOperations();
|
const { updateTemplateWithModule, removeModuleFromTemplate, upsertTemplate, resetTemplateToDefault } =
|
||||||
|
useTemplateOperations(setPersonalTemplate);
|
||||||
|
|
||||||
// Modal state
|
// Modal state
|
||||||
const moduleModalState = useModuleModalState();
|
const moduleModalState = useModuleModalState();
|
||||||
@ -56,6 +57,7 @@ export const PageTemplateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
upsertModule,
|
upsertModule,
|
||||||
moduleModalState,
|
moduleModalState,
|
||||||
moveModule,
|
moveModule,
|
||||||
|
resetTemplateToDefault,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
personalTemplate,
|
personalTemplate,
|
||||||
@ -71,6 +73,7 @@ export const PageTemplateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
upsertModule,
|
upsertModule,
|
||||||
moduleModalState,
|
moduleModalState,
|
||||||
moveModule,
|
moveModule,
|
||||||
|
resetTemplateToDefault,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -92,6 +92,7 @@ const mockMoveModule = vi.fn();
|
|||||||
const mockUpdateTemplateWithModule = vi.fn();
|
const mockUpdateTemplateWithModule = vi.fn();
|
||||||
const mockRemoveModuleFromTemplate = vi.fn();
|
const mockRemoveModuleFromTemplate = vi.fn();
|
||||||
const mockUpsertTemplate = vi.fn();
|
const mockUpsertTemplate = vi.fn();
|
||||||
|
const mockResetTemplateToDefault = vi.fn();
|
||||||
|
|
||||||
describe('PageTemplateContext', () => {
|
describe('PageTemplateContext', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -113,6 +114,7 @@ describe('PageTemplateContext', () => {
|
|||||||
updateTemplateWithModule: mockUpdateTemplateWithModule,
|
updateTemplateWithModule: mockUpdateTemplateWithModule,
|
||||||
removeModuleFromTemplate: mockRemoveModuleFromTemplate,
|
removeModuleFromTemplate: mockRemoveModuleFromTemplate,
|
||||||
upsertTemplate: mockUpsertTemplate,
|
upsertTemplate: mockUpsertTemplate,
|
||||||
|
resetTemplateToDefault: mockResetTemplateToDefault,
|
||||||
});
|
});
|
||||||
|
|
||||||
mockUseModuleOperations.mockReturnValue({
|
mockUseModuleOperations.mockReturnValue({
|
||||||
|
|||||||
@ -52,6 +52,8 @@ const mockModule: PageModuleFragment = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setPersonalTemplate = vi.fn();
|
||||||
|
|
||||||
describe('useTemplateOperations', () => {
|
describe('useTemplateOperations', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
@ -61,7 +63,7 @@ describe('useTemplateOperations', () => {
|
|||||||
|
|
||||||
describe('updateTemplateWithModule', () => {
|
describe('updateTemplateWithModule', () => {
|
||||||
it('should add module to new row when rowIndex is undefined', () => {
|
it('should add module to new row when rowIndex is undefined', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: undefined,
|
rowIndex: undefined,
|
||||||
@ -77,7 +79,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add module to existing row on the left side', () => {
|
it('should add module to existing row on the left side', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: 0,
|
rowIndex: 0,
|
||||||
@ -94,7 +96,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add module to existing row on the right side', () => {
|
it('should add module to existing row on the right side', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: 0,
|
rowIndex: 0,
|
||||||
@ -111,7 +113,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create new row when rowIndex is out of bounds', () => {
|
it('should create new row when rowIndex is out of bounds', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: 5,
|
rowIndex: 5,
|
||||||
@ -127,7 +129,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle template with no rows', () => {
|
it('should handle template with no rows', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithoutRows: PageTemplateFragment = {
|
const templateWithoutRows: PageTemplateFragment = {
|
||||||
urn: 'urn:li:pageTemplate:empty',
|
urn: 'urn:li:pageTemplate:empty',
|
||||||
@ -162,7 +164,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return null when template is null', () => {
|
it('should return null when template is null', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: 0,
|
rowIndex: 0,
|
||||||
@ -177,7 +179,7 @@ describe('useTemplateOperations', () => {
|
|||||||
|
|
||||||
describe('removeModuleFromTemplate', () => {
|
describe('removeModuleFromTemplate', () => {
|
||||||
it('should remove module by moduleIndex when provided', () => {
|
it('should remove module by moduleIndex when provided', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithMultipleModules: PageTemplateFragment = {
|
const templateWithMultipleModules: PageTemplateFragment = {
|
||||||
...mockTemplate,
|
...mockTemplate,
|
||||||
@ -231,7 +233,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle duplicate URNs correctly with moduleIndex', () => {
|
it('should handle duplicate URNs correctly with moduleIndex', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithDuplicateUrns: PageTemplateFragment = {
|
const templateWithDuplicateUrns: PageTemplateFragment = {
|
||||||
...mockTemplate,
|
...mockTemplate,
|
||||||
@ -297,7 +299,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fall back to URN search when moduleIndex is invalid', () => {
|
it('should fall back to URN search when moduleIndex is invalid', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithMultipleModules: PageTemplateFragment = {
|
const templateWithMultipleModules: PageTemplateFragment = {
|
||||||
...mockTemplate,
|
...mockTemplate,
|
||||||
@ -351,7 +353,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fall back to URN search when moduleIndex URN does not match', () => {
|
it('should fall back to URN search when moduleIndex URN does not match', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithMultipleModules: PageTemplateFragment = {
|
const templateWithMultipleModules: PageTemplateFragment = {
|
||||||
...mockTemplate,
|
...mockTemplate,
|
||||||
@ -405,7 +407,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should remove entire row when last module is removed', () => {
|
it('should remove entire row when last module is removed', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithSingleModule: PageTemplateFragment = {
|
const templateWithSingleModule: PageTemplateFragment = {
|
||||||
...mockTemplate,
|
...mockTemplate,
|
||||||
@ -462,7 +464,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return original template when module is not found', () => {
|
it('should return original template when module is not found', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: 0,
|
rowIndex: 0,
|
||||||
@ -480,7 +482,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return original template when rowIndex is invalid', () => {
|
it('should return original template when rowIndex is invalid', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: 5, // Invalid index
|
rowIndex: 5, // Invalid index
|
||||||
@ -498,7 +500,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return original template when rowIndex is undefined', () => {
|
it('should return original template when rowIndex is undefined', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: undefined,
|
rowIndex: undefined,
|
||||||
@ -516,7 +518,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return original template when template is null', () => {
|
it('should return original template when template is null', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const position: ModulePositionInput = {
|
const position: ModulePositionInput = {
|
||||||
rowIndex: 0,
|
rowIndex: 0,
|
||||||
@ -530,7 +532,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle edge case with no modules in row', () => {
|
it('should handle edge case with no modules in row', () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithEmptyRow: PageTemplateFragment = {
|
const templateWithEmptyRow: PageTemplateFragment = {
|
||||||
...mockTemplate,
|
...mockTemplate,
|
||||||
@ -562,7 +564,7 @@ describe('useTemplateOperations', () => {
|
|||||||
|
|
||||||
describe('upsertTemplate', () => {
|
describe('upsertTemplate', () => {
|
||||||
it('should upsert personal template with correct input', async () => {
|
it('should upsert personal template with correct input', async () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
mockUpsertPageTemplateMutation.mockResolvedValue({
|
mockUpsertPageTemplateMutation.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
@ -593,7 +595,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should upsert existing personal template with URN', async () => {
|
it('should upsert existing personal template with URN', async () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
mockUpsertPageTemplateMutation.mockResolvedValue({
|
mockUpsertPageTemplateMutation.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
@ -624,7 +626,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should upsert global template with correct input', async () => {
|
it('should upsert global template with correct input', async () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
mockUpsertPageTemplateMutation.mockResolvedValue({
|
mockUpsertPageTemplateMutation.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
@ -655,7 +657,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update user settings when creating personal template', async () => {
|
it('should update user settings when creating personal template', async () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
mockUpsertPageTemplateMutation.mockResolvedValue({
|
mockUpsertPageTemplateMutation.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
@ -679,7 +681,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not update user settings when updating existing personal template', async () => {
|
it('should not update user settings when updating existing personal template', async () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
mockUpsertPageTemplateMutation.mockResolvedValue({
|
mockUpsertPageTemplateMutation.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
@ -697,7 +699,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle template with empty rows', async () => {
|
it('should handle template with empty rows', async () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const templateWithEmptyRows: PageTemplateFragment = {
|
const templateWithEmptyRows: PageTemplateFragment = {
|
||||||
urn: 'urn:li:pageTemplate:empty',
|
urn: 'urn:li:pageTemplate:empty',
|
||||||
@ -742,7 +744,7 @@ describe('useTemplateOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle mutation error', async () => {
|
it('should handle mutation error', async () => {
|
||||||
const { result } = renderHook(() => useTemplateOperations());
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
const error = new Error('Mutation failed');
|
const error = new Error('Mutation failed');
|
||||||
mockUpsertPageTemplateMutation.mockRejectedValue(error);
|
mockUpsertPageTemplateMutation.mockRejectedValue(error);
|
||||||
@ -750,4 +752,61 @@ describe('useTemplateOperations', () => {
|
|||||||
await expect(result.current.upsertTemplate(mockTemplate, true, null)).rejects.toThrow('Mutation failed');
|
await expect(result.current.upsertTemplate(mockTemplate, true, null)).rejects.toThrow('Mutation failed');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('resetTemplateToDefault', () => {
|
||||||
|
it('should call setPersonalTemplate with null', () => {
|
||||||
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.resetTemplateToDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(setPersonalTemplate).toHaveBeenCalledWith(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call updateUserHomePageSettingsMutation with pageTemplate: null', () => {
|
||||||
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.resetTemplateToDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockUpdateUserHomePageSettings).toHaveBeenCalledWith({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
removePageTemplate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call setPersonalTemplate and mutation exactly once on multiple calls', () => {
|
||||||
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.resetTemplateToDefault();
|
||||||
|
result.current.resetTemplateToDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(setPersonalTemplate).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockUpdateUserHomePageSettings).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle async mutation call correctly', async () => {
|
||||||
|
const { result } = renderHook(() => useTemplateOperations(setPersonalTemplate));
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await result.current.resetTemplateToDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(setPersonalTemplate).toHaveBeenCalledWith(null);
|
||||||
|
expect(mockUpdateUserHomePageSettings).toHaveBeenCalledWith({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
removePageTemplate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -43,7 +43,7 @@ const isValidRemovalPosition = (template: PageTemplateFragment | null, position:
|
|||||||
return rowIndex !== undefined && rowIndex >= 0 && rowIndex < rows.length;
|
return rowIndex !== undefined && rowIndex >= 0 && rowIndex < rows.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useTemplateOperations() {
|
export function useTemplateOperations(setPersonalTemplate: (template: PageTemplateFragment | null) => void) {
|
||||||
const [upsertPageTemplateMutation] = useUpsertPageTemplateMutation();
|
const [upsertPageTemplateMutation] = useUpsertPageTemplateMutation();
|
||||||
const [updateUserHomePageSettings] = useUpdateUserHomePageSettingsMutation();
|
const [updateUserHomePageSettings] = useUpdateUserHomePageSettingsMutation();
|
||||||
|
|
||||||
@ -194,9 +194,21 @@ export function useTemplateOperations() {
|
|||||||
[upsertPageTemplateMutation, updateUserHomePageSettings],
|
[upsertPageTemplateMutation, updateUserHomePageSettings],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const resetTemplateToDefault = () => {
|
||||||
|
setPersonalTemplate(null);
|
||||||
|
updateUserHomePageSettings({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
removePageTemplate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updateTemplateWithModule,
|
updateTemplateWithModule,
|
||||||
removeModuleFromTemplate,
|
removeModuleFromTemplate,
|
||||||
upsertTemplate,
|
upsertTemplate,
|
||||||
|
resetTemplateToDefault,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,4 +60,5 @@ export type PageTemplateContextState = {
|
|||||||
moduleModalState: ModuleModalState;
|
moduleModalState: ModuleModalState;
|
||||||
removeModule: (input: RemoveModuleInput) => void;
|
removeModule: (input: RemoveModuleInput) => void;
|
||||||
moveModule: (input: MoveModuleInput) => void;
|
moveModule: (input: MoveModuleInput) => void;
|
||||||
|
resetTemplateToDefault: () => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,116 @@
|
|||||||
|
import { Button, Dropdown, colors } from '@components';
|
||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import analytics, { EventType } from '@app/analytics';
|
||||||
|
import { useUserContext } from '@app/context/useUserContext';
|
||||||
|
import { usePageTemplateContext } from '@app/homeV3/context/PageTemplateContext';
|
||||||
|
import { ConfirmationModal } from '@app/sharedV2/modals/ConfirmationModal';
|
||||||
|
|
||||||
|
const ButtonWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: ${colors.white};
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
position: fixed;
|
||||||
|
right: 32px;
|
||||||
|
bottom: 32px;
|
||||||
|
border-radius: 200px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(9, 1, 61, 0.12);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DropdownContainer = styled.div`
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(9, 1, 61, 0.12);
|
||||||
|
background-color: white;
|
||||||
|
overflow: hidden; // Cleanly rounds edges
|
||||||
|
|
||||||
|
.ant-dropdown-menu-item {
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function EditHomePageSettingsButton() {
|
||||||
|
const user = useUserContext();
|
||||||
|
const canEditDefaultTemplate = user.platformPrivileges?.manageHomePageTemplates;
|
||||||
|
|
||||||
|
const { setIsEditingGlobalTemplate, isEditingGlobalTemplate, resetTemplateToDefault, personalTemplate } =
|
||||||
|
usePageTemplateContext();
|
||||||
|
|
||||||
|
const isOnPersonalTemplate = !!personalTemplate;
|
||||||
|
|
||||||
|
const [showConfirmResetModal, setShowConfirmResetModal] = useState(false);
|
||||||
|
|
||||||
|
const startGlobalTemplateEdit = useCallback(() => {
|
||||||
|
setIsEditingGlobalTemplate(true);
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.HomePageTemplateGlobalTemplateEditingStart,
|
||||||
|
});
|
||||||
|
}, [setIsEditingGlobalTemplate]);
|
||||||
|
|
||||||
|
const handleResetToDefault = useCallback(() => {
|
||||||
|
resetTemplateToDefault();
|
||||||
|
setShowConfirmResetModal(false);
|
||||||
|
analytics.event({
|
||||||
|
type: EventType.HomePageTemplateResetToGlobalTemplate,
|
||||||
|
});
|
||||||
|
}, [resetTemplateToDefault]);
|
||||||
|
|
||||||
|
if (isEditingGlobalTemplate || (!canEditDefaultTemplate && !isOnPersonalTemplate)) return null;
|
||||||
|
|
||||||
|
const menu = {
|
||||||
|
items: [
|
||||||
|
...(canEditDefaultTemplate
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
label: 'Edit Organization Default',
|
||||||
|
key: 'edit-organization-default',
|
||||||
|
style: {
|
||||||
|
color: colors.gray[600],
|
||||||
|
fontSize: '14px',
|
||||||
|
},
|
||||||
|
onClick: startGlobalTemplateEdit,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...(isOnPersonalTemplate
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
label: 'Reset to Organization Default',
|
||||||
|
key: 'reset-to-organization-default',
|
||||||
|
style: {
|
||||||
|
color: colors.red[1000],
|
||||||
|
fontSize: '14px',
|
||||||
|
},
|
||||||
|
onClick: () => setShowConfirmResetModal(true),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ButtonWrapper>
|
||||||
|
<Dropdown
|
||||||
|
menu={menu}
|
||||||
|
trigger={['click']}
|
||||||
|
dropdownRender={(menuNode) => <DropdownContainer>{menuNode}</DropdownContainer>}
|
||||||
|
>
|
||||||
|
<Button icon={{ icon: 'Gear', color: 'gray', source: 'phosphor', size: '4xl' }} variant="text" />
|
||||||
|
</Dropdown>
|
||||||
|
</ButtonWrapper>
|
||||||
|
<ConfirmationModal
|
||||||
|
isOpen={!!showConfirmResetModal}
|
||||||
|
handleConfirm={handleResetToDefault}
|
||||||
|
handleClose={() => setShowConfirmResetModal(false)}
|
||||||
|
modalTitle="Confirm reset to default template"
|
||||||
|
modalText="Are you sure you want to reset your homepage to the organization's default template? You will lose all your personal modules."
|
||||||
|
closeButtonText="Cancel"
|
||||||
|
confirmButtonText="Confirm"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -54,7 +54,7 @@ export const ConfirmationModal = ({
|
|||||||
centered
|
centered
|
||||||
footer={
|
footer={
|
||||||
<ButtonsContainer>
|
<ButtonsContainer>
|
||||||
<Button variant="text" onClick={handleClose} data-testid="modal-cancel-button">
|
<Button variant="text" color="gray" onClick={handleClose} data-testid="modal-cancel-button">
|
||||||
{closeButtonText || 'Cancel'}
|
{closeButtonText || 'Cancel'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="filled" onClick={handleConfirm} data-testid="modal-confirm-button">
|
<Button variant="filled" onClick={handleConfirm} data-testid="modal-confirm-button">
|
||||||
|
|||||||
@ -69,6 +69,7 @@ query getMe {
|
|||||||
viewStructuredPropertiesPage
|
viewStructuredPropertiesPage
|
||||||
manageApplications
|
manageApplications
|
||||||
manageFeatures
|
manageFeatures
|
||||||
|
manageHomePageTemplates
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user