diff --git a/backend/VERSION.json b/backend/VERSION.json index dabe953f530..adba0b93c83 100644 --- a/backend/VERSION.json +++ b/backend/VERSION.json @@ -1,4 +1,4 @@ -{ "VERSION" : "5.6.3", +{ "VERSION" : "5.7.1", "unity" : { "current" : "2.20.0", "supported": [] diff --git a/backend/package.json b/backend/package.json index 73e3d843c2d..5bdda9284c2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "3drepo.io", - "version": "5.6.3", + "version": "5.7.1", "engines": { "node": "18.x.x" }, diff --git a/backend/src/v4/routes/apidoc.json b/backend/src/v4/routes/apidoc.json index d454533ba6a..4b0c45c6b05 100644 --- a/backend/src/v4/routes/apidoc.json +++ b/backend/src/v4/routes/apidoc.json @@ -1,3 +1,3 @@ { - "version": "5.6.3" + "version": "5.7.1" } diff --git a/backend/src/v5/schemas/tickets/templates.constants.js b/backend/src/v5/schemas/tickets/templates.constants.js index 20ba7cecf1c..fd97dbf2fd2 100644 --- a/backend/src/v5/schemas/tickets/templates.constants.js +++ b/backend/src/v5/schemas/tickets/templates.constants.js @@ -104,10 +104,10 @@ TemplateConstants.defaultProperties = [ createPropertyEntry('Owner', propTypes.TEXT, undefined, undefined, true), createPropertyEntry('Created at', propTypes.DATE, undefined, undefined, true), createPropertyEntry('Updated at', propTypes.DATE, undefined, undefined, true), + createPropertyEntry('Status', propTypes.ONE_OF, ['Open', 'In Progress', 'For Approval', 'Closed', 'Void'], 'Open'), createPropertyEntry('Default Image', propTypes.IMAGE, undefined, undefined, undefined, ({ defaultImage }) => defaultImage), createPropertyEntry('Default View', propTypes.VIEW, undefined, undefined, undefined, ({ defaultView }) => defaultView), createPropertyEntry('Priority', propTypes.ONE_OF, ['None', 'Low', 'Medium', 'High'], 'None', undefined, ({ issueProperties }) => issueProperties), - createPropertyEntry('Status', propTypes.ONE_OF, ['Open', 'In Progress', 'For Approval', 'Closed', 'Void'], 'Open', undefined, ({ issueProperties }) => issueProperties), createPropertyEntry('Assignees', propTypes.MANY_OF, presetEnumValues.JOBS_AND_USERS, undefined, undefined, ({ issueProperties }) => issueProperties), createPropertyEntry('Due Date', propTypes.DATE, undefined, undefined, undefined, ({ issueProperties }) => issueProperties), createPropertyEntry('Pin', propTypes.COORDS, undefined, undefined, undefined, ({ pin }) => pin), diff --git a/backend/tests/v5/e2e/routes/teamspaces/projects/models/common/tickets.test.js b/backend/tests/v5/e2e/routes/teamspaces/projects/models/common/tickets.test.js index 740c4d9223a..ca97807b7c4 100644 --- a/backend/tests/v5/e2e/routes/teamspaces/projects/models/common/tickets.test.js +++ b/backend/tests/v5/e2e/routes/teamspaces/projects/models/common/tickets.test.js @@ -719,6 +719,7 @@ const testUpdateTicket = () => { expect(updatedTicket).toHaveProperty('number'); expect(updatedTicket.properties).toHaveProperty(basePropertyLabels.UPDATED_AT); expect(updatedTicket.properties).toHaveProperty(basePropertyLabels.CREATED_AT); + expect(updatedTicket.properties).toHaveProperty(basePropertyLabels.STATUS); expect(updatedTicket.properties).toHaveProperty(basePropertyLabels.OWNER); expect(updatedTicket.properties[basePropertyLabels.UPDATED_AT]) .not.toEqual(ticket.properties[basePropertyLabels.UPDATED_AT]); @@ -729,6 +730,7 @@ const testUpdateTicket = () => { ...ticket.properties, [basePropertyLabels.UPDATED_AT]: updatedTicket.properties[basePropertyLabels.UPDATED_AT], [basePropertyLabels.CREATED_AT]: updatedTicket.properties[basePropertyLabels.CREATED_AT], + [basePropertyLabels.STATUS]: updatedTicket.properties[basePropertyLabels.STATUS], [basePropertyLabels.OWNER]: updatedTicket.properties[basePropertyLabels.OWNER], ...(payloadChanges?.properties ?? {}), [imagePropName]: updatedTicket.properties[imagePropName], diff --git a/backend/tests/v5/e2e/services/chat/modelEvents/tickets.test.js b/backend/tests/v5/e2e/services/chat/modelEvents/tickets.test.js index c6b876b6b47..200b29e13ec 100644 --- a/backend/tests/v5/e2e/services/chat/modelEvents/tickets.test.js +++ b/backend/tests/v5/e2e/services/chat/modelEvents/tickets.test.js @@ -99,6 +99,7 @@ const ticketAddedTest = () => { new Date(getRes.body.properties[basePropertyLabels.UPDATED_AT]).getTime(), [basePropertyLabels.CREATED_AT]: new Date(getRes.body.properties[basePropertyLabels.CREATED_AT]).getTime(), + [basePropertyLabels.STATUS]: getRes.body.properties[basePropertyLabels.STATUS], }, number: 1, }, @@ -138,6 +139,7 @@ const ticketAddedTest = () => { new Date(getRes.body.properties[basePropertyLabels.UPDATED_AT]).getTime(), [basePropertyLabels.CREATED_AT]: new Date(getRes.body.properties[basePropertyLabels.CREATED_AT]).getTime(), + [basePropertyLabels.STATUS]: getRes.body.properties[basePropertyLabels.STATUS], }, number: 1, }, diff --git a/backend/tests/v5/unit/schemas/tickets/templates.constants.test.js b/backend/tests/v5/unit/schemas/tickets/templates.constants.test.js index de9bf498e10..1bb84e06425 100644 --- a/backend/tests/v5/unit/schemas/tickets/templates.constants.test.js +++ b/backend/tests/v5/unit/schemas/tickets/templates.constants.test.js @@ -24,10 +24,10 @@ const testGetApplicableDefaultProperties = () => { const basicProp = [{ name: 'Description', type: TemplateConstants.propTypes.LONG_TEXT }, { name: 'Owner', type: TemplateConstants.propTypes.TEXT, readOnly: true }, { name: 'Created at', type: TemplateConstants.propTypes.DATE, readOnly: true }, - { name: 'Updated at', type: TemplateConstants.propTypes.DATE, readOnly: true }]; + { name: 'Updated at', type: TemplateConstants.propTypes.DATE, readOnly: true }, + { name: 'Status', type: TemplateConstants.propTypes.ONE_OF, values: ['Open', 'In Progress', 'For Approval', 'Closed', 'Void'], default: 'Open' }]; const issueProp = [{ name: 'Priority', type: TemplateConstants.propTypes.ONE_OF, values: ['None', 'Low', 'Medium', 'High'], default: 'None' }, - { name: 'Status', type: TemplateConstants.propTypes.ONE_OF, values: ['Open', 'In Progress', 'For Approval', 'Closed', 'Void'], default: 'Open' }, { name: 'Assignees', type: TemplateConstants.propTypes.MANY_OF, values: TemplateConstants.presetEnumValues.JOBS_AND_USERS }, { name: 'Due Date', type: TemplateConstants.propTypes.DATE }]; test('Should only return the basic properties if none of the optional flags are configured', () => { diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index 4c6f2791e3c..e4ced611ea3 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -1,7 +1,7 @@ import { theme as V5Theme } from '@/v5/ui/themes/theme'; import { theme as V5ViewerTheme } from '@/v5/ui/routes/viewer/theme'; import { IntlProvider } from 'react-intl'; -import { getIntlProviderProps } from '@/v5/services/intl'; +import { getIntl } from '@/v5/services/intl'; import { GlobalStyle } from '@/v5/ui/themes/global'; import { ThemeProvider as MuiThemeProvider } from '@mui/material'; import { ThemeProvider, createGlobalStyle } from 'styled-components'; @@ -73,7 +73,7 @@ const withThemeProvider = (Story, context)=>{ export const decorators = [ withThemeProvider, (Story) => ( - + ), diff --git a/frontend/package.json b/frontend/package.json index 039f6d3c70c..65cf75f2040 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "3drepo.io-frontend", - "version": "5.6.3", + "version": "5.7.1", "description": "The frontend for 3drepo.io", "engines": { "node": "18.x.x" diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index aa3dfa68d1c..44aa8e1f406 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -30,7 +30,7 @@ import { Root as V5Root } from '@/v5/ui/routes'; import { UnityUtil } from '@/globals/unity-util'; import { clientConfigService } from '@/v4/services/clientConfig'; -import { formatMessage, getIntlProviderProps, initializeIntl } from '@/v5/services/intl'; +import { formatMessage, getIntl, initializeIntl } from '@/v5/services/intl'; import { initializeActionsDispatchers } from '@/v5/helpers/actionsDistpatchers.helper'; import { IntlProvider } from 'react-intl'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; @@ -68,7 +68,7 @@ const render = () => { ReactDOM.render( - + diff --git a/frontend/src/v4/routes/components/dateField/dateField.component.tsx b/frontend/src/v4/routes/components/dateField/dateField.component.tsx index 186f23f70c9..11c78545e89 100644 --- a/frontend/src/v4/routes/components/dateField/dateField.component.tsx +++ b/frontend/src/v4/routes/components/dateField/dateField.component.tsx @@ -14,12 +14,10 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import { ComponentType, useEffect } from 'react'; -import { DatePickerProps } from '@mui/lab/DatePicker'; -import { DateTimePickerProps } from '@mui/lab/DateTimePicker'; import { TextField } from '@mui/material'; -import { useState } from 'react'; -import { StyledDatePicker, StyledDateTimePicker } from './dateField.styles'; +import { useEffect, useState } from 'react'; +import { DateTimePicker } from '@controls/inputs/datePicker/dateTimePicker.component'; +import { DatePicker } from '@controls/inputs/datePicker/datePicker.component'; interface IProps { value?: any; @@ -44,11 +42,10 @@ export const DateField = ({ placeholder, dateTime, defaultValue, - className, ...dateFieldProps }: IProps) => { const [value, setValue] = useState(propValue || null); - const Picker: ComponentType = dateTime ? StyledDateTimePicker : StyledDatePicker; + const Picker = dateTime ? DateTimePicker : DatePicker; const handleAccept = (newValue) => { if (newValue) { @@ -72,14 +69,15 @@ export const DateField = ({ onAccept={handleAccept} onChange={() => {}} disableHighlightToday + // @ts-ignore + inputProps={{ readOnly: true, placeholder }} + components={{ ActionBar: null }} renderInput={(props) => ( )} {...dateFieldProps} diff --git a/frontend/src/v4/routes/components/dateField/dateField.styles.ts b/frontend/src/v4/routes/components/dateField/dateField.styles.ts deleted file mode 100644 index 740bda8adcb..00000000000 --- a/frontend/src/v4/routes/components/dateField/dateField.styles.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (C) 2021 3D Repo Ltd - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import styled from 'styled-components'; - -import { MobileDatePicker, MobileDateTimePicker } from '@mui/lab'; - -export const Container = styled.div` - margin-top: 16px; -`; - -export const StyledDateTimePicker = styled(MobileDateTimePicker)` - width: 100%; -`; - -export const StyledDatePicker = styled(MobileDatePicker)` - width: 100%; -`; diff --git a/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx b/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx index b7d8df23ed4..67782058910 100644 --- a/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx +++ b/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx @@ -16,7 +16,7 @@ */ import { Tickets } from '@/v5/ui/routes/viewer/tickets/tickets.component'; -import { isEmpty } from 'lodash'; +import { isEmpty, isEqual } from 'lodash'; import { PureComponent } from 'react'; import { Toolbar } from '@/v5/ui/routes/viewer/toolbar/toolbar.component'; import { VIEWER_EVENTS } from '../../constants/viewer'; @@ -181,6 +181,7 @@ export class ViewerGui extends PureComponent { this.props.setPanelVisibility(VIEWER_PANELS.COMPARE, false); this.props.resetCompareComponent(); } + } public componentWillUnmount() { diff --git a/frontend/src/v4/routes/viewerGui/viewerGui.container.ts b/frontend/src/v4/routes/viewerGui/viewerGui.container.ts index bf11550dbda..7e82afc55e7 100644 --- a/frontend/src/v4/routes/viewerGui/viewerGui.container.ts +++ b/frontend/src/v4/routes/viewerGui/viewerGui.container.ts @@ -19,6 +19,7 @@ import { TeamspacesActions } from '@/v4/modules/teamspaces'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { createStructuredSelector } from 'reselect'; +import { selectSelectedTicket } from '@/v5/store/tickets/card/ticketsCard.selectors'; import { CompareActions } from '../../modules/compare'; import { selectCurrentTeamspace, selectCurrentUser } from '../../modules/currentUser'; diff --git a/frontend/src/v5/helpers/viewpoint.helpers.ts b/frontend/src/v5/helpers/viewpoint.helpers.ts index 39e8ca4654c..240bdd24e81 100644 --- a/frontend/src/v5/helpers/viewpoint.helpers.ts +++ b/frontend/src/v5/helpers/viewpoint.helpers.ts @@ -20,11 +20,8 @@ import { generateViewpoint as generateViewpointV4, getNodesIdsFromSharedIds, toS import { formatMessage } from '@/v5/services/intl'; import { dispatch, getState } from '@/v4/modules/store'; import { isEmpty, isString } from 'lodash'; -import { Viewer as ViewerService } from '@/v4/services/viewer/viewer'; -import { TreeActions } from '@/v4/modules/tree'; -import { ViewerGuiActions } from '@/v4/modules/viewerGui'; import { selectCurrentTeamspace } from '../store/teamspaces/teamspaces.selectors'; -import { TicketsCardActionsDispatchers } from '../services/actionsDispatchers'; +import { ViewpointsActions } from '@/v4/modules/viewpoints'; export const convertToV5GroupNodes = (objects) => objects.map((object) => ({ container: object.model as string, @@ -73,6 +70,12 @@ const convertToV5GroupOverride = (group: any, type: ViewpointGroupOverrideType): override.opacity = opacity; } + if (group.transformation) { + const { transformation } = group; + override.transformation = transformation; + } + + return override; }; @@ -103,10 +106,8 @@ export const getViewerState = async () => { return state; }; -const mergeGroups = (groups: any[]) => ({ objects: groups.flatMap((group) => group.objects) }); - const convertToV4Group = (groupOverride: GroupOverride) => { - const { color, opacity, group: v5Group } = groupOverride; + const { color, opacity, transformation, group: v5Group } = groupOverride; if (isString(v5Group)) { return { color: [0, 0, 0, 0], objects: [] }; // theres no info yet so I say us an empty group @@ -124,9 +125,15 @@ const convertToV4Group = (groupOverride: GroupOverride) => { group.opacity = opacity; } + if (transformation) { + group.transformation = transformation; + } + return group; }; +const mergeGroups = (groups: any[]) => ({ objects: groups.flatMap((group) => group.objects) }); + export const viewpointV5ToV4 = (viewpoint: Viewpoint) => { let v4Viewpoint:any = {}; if (viewpoint.camera) { @@ -143,14 +150,15 @@ export const viewpointV5ToV4 = (viewpoint: Viewpoint) => { v4Viewpoint.clippingPlanes = viewpoint.clippingPlanes; } - if (!isEmpty(viewpoint.state?.colored)) { - v4Viewpoint.override_groups = viewpoint.state.colored.map(convertToV4Group); - } - if (!isEmpty(viewpoint.state?.transformed)) { v4Viewpoint.transformation_groups = viewpoint.state.transformed.map(convertToV4Group); } + + if (!isEmpty(viewpoint.state?.colored)) { + v4Viewpoint.override_groups = viewpoint.state.colored.map(convertToV4Group); + } + if (!isEmpty(viewpoint.state?.hidden)) { v4Viewpoint.hidden_group = mergeGroups(viewpoint.state.hidden.map(convertToV4Group)); } @@ -163,19 +171,21 @@ export const meshObjectsToV5GroupNode = (objects) => objects.map((obj) => ({ _ids: obj.mesh_ids, })); -export const toColorAndTransparencyDicts = (overrides: GroupOverride[]): OverridesDicts => { - const toMeshDictionary = (objects: V4GroupObjects, color: string, opacity: number): OverridesDicts => objects.shared_ids.reduce((dict, id) => { - if (color !== undefined) { +export const toGroupPropertiesDicts = (overrides: GroupOverride[]): OverridesDicts => { + const toMeshDictionary = (objects: V4GroupObjects, color: string, opacity: number): OverridesDicts => + objects.shared_ids.reduce((dict, id) => { + if (color !== undefined) { // eslint-disable-next-line no-param-reassign - dict.overrides[id] = color; - } + dict.overrides[id] = color; + } - if (opacity !== undefined) { + if (opacity !== undefined) { // eslint-disable-next-line no-param-reassign - dict.transparencies[id] = opacity; - } - return dict; - }, { overrides: {}, transparencies: {} } as OverridesDicts); + dict.transparencies[id] = opacity; + } + + return dict; + }, { overrides: {}, transparencies: {} } as OverridesDicts); return overrides.reduce((acum, current) => { const color = current.color ? getGroupHexColor(current.color) : undefined; @@ -189,32 +199,21 @@ export const toColorAndTransparencyDicts = (overrides: GroupOverride[]): Overrid dict.overrides = { ...dict.overrides, ...overrideDict.overrides }; // eslint-disable-next-line no-param-reassign dict.transparencies = { ...dict.transparencies, ...overrideDict.transparencies }; + return dict; }, acum); }, { overrides: {}, transparencies: {} } as OverridesDicts); }; export const goToView = async (view: Viewpoint) => { - if (isEmpty(view?.state?.colored) && isEmpty(view?.state?.hidden) && isEmpty(view?.camera) && isEmpty(view?.clippingPlanes)) { + if ( + isEmpty(view?.state?.colored) && + isEmpty(view?.state?.hidden) && + isEmpty(view?.state?.transformed) && + isEmpty(view?.camera) && + isEmpty(view?.clippingPlanes)) { return; } - - dispatch(ViewerGuiActions.clearColorOverrides()); - await ViewerService.setViewpoint(view); - const overrides = toColorAndTransparencyDicts(view?.state?.colored || []); - TicketsCardActionsDispatchers.setOverrides(overrides); - - await ViewerService.clearHighlights(); - - if (view?.state) { - dispatch(TreeActions.setHiddenGeometryVisible(!!view.state.showHidden)); - } - - const v4HiddenObjects = convertToV4GroupNodes(view.state?.hidden?.flatMap((hiddenOverride) => (hiddenOverride.group as Group)?.objects || [])); - if (v4HiddenObjects.length) { - dispatch(TreeActions.hideNodesBySharedIds(v4HiddenObjects, true)); - } else { - dispatch(TreeActions.showAllNodes()); - } + dispatch(ViewpointsActions.showViewpoint(null, null, viewpointV5ToV4(view))); }; diff --git a/frontend/src/v5/services/intl.ts b/frontend/src/v5/services/intl.ts index 6df663fa6f1..681d178dbb3 100644 --- a/frontend/src/v5/services/intl.ts +++ b/frontend/src/v5/services/intl.ts @@ -43,12 +43,19 @@ export const initializeIntl = (locale: string) => { // Locale of the fallback defaultMessage defaultLocale: DEFAULT_LOCALE, messages, + onError: (error) => { + if (error.code === 'MISSING_TRANSLATION') { + return; + } + + console.error(error); + }, }, cache, ); }; -const getIntl = () => { +export const getIntl = () => { if (!intlInternal) { initializeIntl(DEFAULT_LOCALE); } @@ -70,16 +77,3 @@ export const formatRelativeTime: typeof intlInternal.formatRelativeTime = (value // eslint-disable-next-line max-len export const formatPlural: typeof intlInternal.formatPlural = (value, opts?): Intl.LDMLPluralRule => getIntl().formatPlural(value, opts); - -export const getIntlProviderProps = () => ({ - messages: getIntl().messages, - defaultLocal: getIntl().defaultLocale, - locale: getIntl().locale, - onError: (error) => { - if (error.code === 'MISSING_TRANSLATION' && getIntl().locale === DEFAULT_LOCALE) { - return; - } - - console.error(error); - }, -}); diff --git a/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts b/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts index 1836c258ca7..f28b5607562 100644 --- a/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts +++ b/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts @@ -36,6 +36,7 @@ export const { Types: TicketsCardTypes, Creators: TicketsCardActions } = createA resetState: [], setOverrides: ['overrides'], setUnsavedTicket: ['ticket'], + setTransformations: ['transformations'], }, { prefix: 'TICKETS_CARD/' }) as { Types: Constants; Creators: ITicketsCardActionCreators }; export interface ITicketsCardState { @@ -47,6 +48,7 @@ export interface ITicketsCardState { readOnly: boolean, overrides: OverridesDicts | null, unsavedTicket: EditableTicket | null, + transformations: any, } export const INITIAL_STATE: ITicketsCardState = { @@ -60,6 +62,7 @@ export const INITIAL_STATE: ITicketsCardState = { }, view: TicketsCardViews.List, overrides: null, + transformations: null, readOnly: false, unsavedTicket: null, }; @@ -156,5 +159,5 @@ export interface ITicketsCardActionCreators { setReadOnly: (readOnly: boolean) => SetReadOnlyAction, resetState: () => ResetStateAction, setOverrides: (overrides: OverridesDicts) => SetOverridesAction, - setUnsavedTicket: (ticket: EditableTicket) => SetUnsavedTicketAction, + setUnsavedTicket: (ticket: EditableTicket) => SetUnsavedTicketAction } diff --git a/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts b/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts index cc446984cc9..9959e1d0705 100644 --- a/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts +++ b/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts @@ -67,7 +67,7 @@ export const selectSelectedTicketPinId = createSelector( export const selectTicketOverridesDict = createSelector( selectTicketsCardDomain, - (ticketCardState) => ticketCardState.overrides || { overrides: {}, transparencies: {} }, + (ticketCardState) => ticketCardState.overrides || { overrides: {}, transparencies: {}, transformations: {} }, ); export const selectTicketOverrides = createSelector( diff --git a/frontend/src/v5/store/tickets/tickets.helpers.ts b/frontend/src/v5/store/tickets/tickets.helpers.ts index 05852c0f604..1d33b3b4477 100644 --- a/frontend/src/v5/store/tickets/tickets.helpers.ts +++ b/frontend/src/v5/store/tickets/tickets.helpers.ts @@ -273,6 +273,7 @@ const fillEmptyOverrides = (values: Partial) => { viewValue.state ||= {} as any; viewValue.state.colored ||= []; viewValue.state.hidden ||= []; + viewValue.state.transformed ||= []; } }); }; diff --git a/frontend/src/v5/store/tickets/tickets.selectors.ts b/frontend/src/v5/store/tickets/tickets.selectors.ts index aaa30c4d9b8..9996695d7a8 100644 --- a/frontend/src/v5/store/tickets/tickets.selectors.ts +++ b/frontend/src/v5/store/tickets/tickets.selectors.ts @@ -19,8 +19,8 @@ import { createSelector } from 'reselect'; import { orderBy } from 'lodash'; import { BaseProperties } from '@/v5/ui/routes/viewer/tickets/tickets.constants'; import { ITicketsState } from './tickets.redux'; -import { createPropertiesWithGroups } from './ticketsGroups.helpers'; -import { ITicket, Properties, TicketWithModelIdAndName } from './tickets.types'; +import { createPropertiesWithGroups, ticketWithGroups } from './ticketsGroups.helpers'; +import { ITicket, TicketWithModelIdAndName } from './tickets.types'; import { selectContainers } from '../containers/containers.selectors'; import { selectFederations } from '../federations/federations.selectors'; @@ -70,19 +70,19 @@ export const selectTicketsGroups = createSelector( (state) => state.groupsByGroupId, ); -export const selectTickets = createSelector( +const selectTicketsRaw = createSelector( selectTicketsDomain, (state, modelId) => modelId, + (state, modelId) => state.ticketsByModelId[modelId] || [], +); + +export const selectTickets = createSelector( + selectTicketsRaw, selectTicketsGroups, - (state, modelId, groups): ITicket[] => { + (ticketsList, groups): ITicket[] => { const tickets = []; - (state.ticketsByModelId[modelId] || []).forEach((ticket) => { - let { properties } = ticket; - properties = createPropertiesWithGroups(properties, groups); - tickets.push({ - ...ticket, - properties, - }); + ticketsList.forEach((ticket) => { + tickets.push({ ...ticket, properties: createPropertiesWithGroups(ticket.properties, groups) }); }); return orderBy(tickets, `properties.${BaseProperties.CREATED_AT}`, 'desc'); @@ -90,7 +90,7 @@ export const selectTickets = createSelector( ); export const selectTicketByIdRaw = createSelector( - selectTickets, + selectTicketsRaw, (_, modelId, ticketId) => ticketId, (tickets, ticketId) => tickets.find(({ _id }) => _id === ticketId) || null, ); @@ -102,25 +102,7 @@ export const selectTicketById = createSelector( if (!ticket) { return ticket; } - - let { properties } = ticket; - properties = createPropertiesWithGroups(properties, groups); - const finalTicket = { - ...ticket, - properties, - }; - - if (ticket.modules) { - let { modules } = ticket; - modules = Object.keys(modules).reduce((partialModules, key) => { - // eslint-disable-next-line no-param-reassign - partialModules[key] = createPropertiesWithGroups(modules[key], groups); - return partialModules; - }, {} as Record); - - finalTicket.modules = modules; - } - return finalTicket; + return ticketWithGroups(ticket, groups); }, ); diff --git a/frontend/src/v5/store/tickets/tickets.types.ts b/frontend/src/v5/store/tickets/tickets.types.ts index 49effdb2b85..4ed64a5a85d 100644 --- a/frontend/src/v5/store/tickets/tickets.types.ts +++ b/frontend/src/v5/store/tickets/tickets.types.ts @@ -153,12 +153,18 @@ export enum ViewpointGroupOverrideType { TRANSFORMED, } -type ColorAndOpacity = { +export type TransformMatrix = [number, number, number, number, + number, number, number, number, + number, number, number, number, + number, number, number, number]; + +type GroupProperties = { color?: [number, number, number], opacity?: number, + transformation?: TransformMatrix }; -export type GroupOverride = ColorAndOpacity & { +export type GroupOverride = GroupProperties & { prefix?: string[], group: string | Group, key?: number; @@ -182,10 +188,11 @@ export type IGroupSettingsForm = GroupOverride & { group: Group }; type MeshIdColorDict = Record; type MeshIdTransparencyDict = Record; +export type MeshIdTransformDict = Record; export type OverridesDicts = { overrides: MeshIdColorDict, - transparencies: MeshIdTransparencyDict + transparencies: MeshIdTransparencyDict, }; export type TicketWithModelIdAndName = ITicket & { modelId: string; modelName: string }; diff --git a/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts b/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts index 758f98533cf..1821e8f5c18 100644 --- a/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts +++ b/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts @@ -16,7 +16,7 @@ */ import { isString } from 'lodash'; -import { Group, GroupOverride, ViewpointState, Properties } from './tickets.types'; +import { Group, GroupOverride, ViewpointState, Properties, ITicket } from './tickets.types'; const overrideWithGroups = (groups: Record) => (override: GroupOverride) => { const overrideToReturn = { ...override }; @@ -57,6 +57,18 @@ export const createPropertiesWithGroups = (properties, groups) => Object.keys(pr return partialProps; }, {} as Properties); + +export const ticketWithGroups = (ticket: ITicket, groups: Record) => { + const properties = createPropertiesWithGroups(ticket.properties, groups); + + + const modules:Record = {}; + Object.keys(ticket.modules).forEach((moduleName) => + modules[moduleName] = createPropertiesWithGroups(ticket.modules[moduleName], groups), + ); + return { ...ticket, properties, modules }; +}; + /* eslint-enable no-param-reassign */ export const getSanitizedSmartGroup = (group) => { diff --git a/frontend/src/v5/ui/controls/inputs/datePicker/baseCalendarPicker/calendarActionBar/calendarActionBar.component.tsx b/frontend/src/v5/ui/controls/inputs/datePicker/baseCalendarPicker/calendarActionBar/calendarActionBar.component.tsx index 926d47c4430..b6da77ceb64 100644 --- a/frontend/src/v5/ui/controls/inputs/datePicker/baseCalendarPicker/calendarActionBar/calendarActionBar.component.tsx +++ b/frontend/src/v5/ui/controls/inputs/datePicker/baseCalendarPicker/calendarActionBar/calendarActionBar.component.tsx @@ -25,7 +25,7 @@ type ICalendarActionBar = { export const CalendarActionBar = ({ onClear, hidden }: ICalendarActionBar) => { if (hidden) return null; return ( -