Fix front data model edition + non nullable workspaceCustom application migration (#16016)
# Introduction Two things: - Enforcing non nullable workspace custom application Id for any workspace - Fixing front non editable data models following https://github.com/twentyhq/twenty/pull/15911 that associate any custom entities to an applicationId. The front was putting everything as readonly when under an app ( we will have to handle the twenty standard application in the future too ) ## Fallback ### Migration The non nullable migration will fail when released, that's why it's being swallowed and re-run in an upgrade command post workspace custom application creation for those that miss one. Allowing the migration to pass in the end The typeorm migration still need to exists for any new workspaces ### GetCurrentUser In order to dynamically display isReadOnly in data model settings we're fetching the workspaceCustomApplicationId through the `getCurrentUser` If not fallback this endpoint would throw until we're handling existing workspaces that do not have a custom workspace application The fallback should be removed post release
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { Tag } from 'twenty-ui/components';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { getItemTagInfo } from '@/settings/data-model/utils/getItemTagInfo';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Tag } from 'twenty-ui/components';
|
||||
|
||||
type SettingsItemTypeTagProps = {
|
||||
item: {
|
||||
@@ -14,7 +16,12 @@ export const SettingsItemTypeTag = ({
|
||||
className,
|
||||
item: { isCustom, isRemote, applicationId },
|
||||
}: SettingsItemTypeTagProps) => {
|
||||
const itemTagInfo = getItemTagInfo({ isCustom, isRemote, applicationId });
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const itemTagInfo = getItemTagInfo({
|
||||
objectMetadataItem: { isCustom, isRemote, applicationId },
|
||||
workspaceCustomApplicationId:
|
||||
currentWorkspace?.workspaceCustomApplication?.id,
|
||||
});
|
||||
|
||||
return (
|
||||
<Tag
|
||||
|
||||
+5
-1
@@ -1,3 +1,4 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useDeleteOneFieldMetadataItem } from '@/object-metadata/hooks/useDeleteOneFieldMetadataItem';
|
||||
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||
@@ -14,7 +15,7 @@ import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMe
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { FieldMetadataType, SettingsPath } from 'twenty-shared/types';
|
||||
import {
|
||||
getSettingsPath,
|
||||
@@ -66,8 +67,11 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
const { fieldMetadataItem, identifierType, objectMetadataItem } =
|
||||
settingsObjectDetailTableItem;
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const readonly = isObjectMetadataSettingsReadOnly({
|
||||
objectMetadataItem,
|
||||
workspaceCustomApplicationId:
|
||||
currentWorkspace?.workspaceCustomApplication?.id,
|
||||
});
|
||||
|
||||
const isRemoteObjectField = objectMetadataItem.isRemote;
|
||||
|
||||
+8
-2
@@ -1,3 +1,4 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SettingsDataModelObjectAboutForm } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm';
|
||||
@@ -7,7 +8,7 @@ import {
|
||||
} from '@/settings/data-model/validation-schemas/settingsDataModelObjectAboutFormSchema';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { updatedObjectNamePluralState } from '~/pages/settings/data-model/states/updatedObjectNamePluralState';
|
||||
@@ -20,7 +21,12 @@ type SettingsUpdateDataModelObjectAboutFormProps = {
|
||||
export const SettingsUpdateDataModelObjectAboutForm = ({
|
||||
objectMetadataItem,
|
||||
}: SettingsUpdateDataModelObjectAboutFormProps) => {
|
||||
const readonly = isObjectMetadataSettingsReadOnly({ objectMetadataItem });
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const readonly = isObjectMetadataSettingsReadOnly({
|
||||
objectMetadataItem,
|
||||
workspaceCustomApplicationId:
|
||||
currentWorkspace?.workspaceCustomApplication?.id,
|
||||
});
|
||||
const navigate = useNavigateSettings();
|
||||
const setUpdatedObjectNamePlural = useSetRecoilState(
|
||||
updatedObjectNamePluralState,
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { getSettingsPath } from 'twenty-shared/utils';
|
||||
@@ -10,6 +11,7 @@ import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { isObjectMetadataSettingsReadOnly } from '@/object-record/read-only/utils/isObjectMetadataSettingsReadOnly';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
display: flex;
|
||||
@@ -22,8 +24,11 @@ type ObjectFieldsProps = {
|
||||
};
|
||||
|
||||
export const ObjectFields = ({ objectMetadataItem }: ObjectFieldsProps) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const readonly = isObjectMetadataSettingsReadOnly({
|
||||
objectMetadataItem,
|
||||
workspaceCustomApplicationId:
|
||||
currentWorkspace?.workspaceCustomApplication?.id,
|
||||
});
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
+9
-2
@@ -1,16 +1,18 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||
import { isObjectMetadataSettingsReadOnly } from '@/object-record/read-only/utils/isObjectMetadataSettingsReadOnly';
|
||||
import { SettingsUpdateDataModelObjectAboutForm } from '@/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm';
|
||||
import { SettingsDataModelObjectSettingsFormCard } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectSettingsFormCard';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { H2Title, IconArchive } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { isObjectMetadataSettingsReadOnly } from '@/object-record/read-only/utils/isObjectMetadataSettingsReadOnly';
|
||||
|
||||
type ObjectSettingsProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
@@ -28,7 +30,12 @@ const StyledFormSection = styled(Section)`
|
||||
|
||||
export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => {
|
||||
const { t } = useLingui();
|
||||
const readonly = isObjectMetadataSettingsReadOnly({ objectMetadataItem });
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const readonly = isObjectMetadataSettingsReadOnly({
|
||||
objectMetadataItem,
|
||||
workspaceCustomApplicationId:
|
||||
currentWorkspace?.workspaceCustomApplication?.id,
|
||||
});
|
||||
const navigate = useNavigateSettings();
|
||||
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
|
||||
|
||||
|
||||
+8
-1
@@ -1,3 +1,4 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getActiveFieldMetadataItems } from '@/object-metadata/utils/getActiveFieldMetadataItems';
|
||||
@@ -9,6 +10,7 @@ import { t } from '@lingui/core/macro';
|
||||
import { useMemo } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isLabelIdentifierFieldMetadataTypes } from 'twenty-shared/utils';
|
||||
import { IconCircleOff, IconPlus, useIcons } from 'twenty-ui/display';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
@@ -42,7 +44,12 @@ const StyledContainer = styled.div`
|
||||
export const SettingsDataModelObjectIdentifiersForm = ({
|
||||
objectMetadataItem,
|
||||
}: SettingsDataModelObjectIdentifiersFormProps) => {
|
||||
const readonly = isObjectMetadataSettingsReadOnly({ objectMetadataItem });
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const readonly = isObjectMetadataSettingsReadOnly({
|
||||
objectMetadataItem,
|
||||
workspaceCustomApplicationId:
|
||||
currentWorkspace?.workspaceCustomApplication?.id,
|
||||
});
|
||||
const formConfig = useForm<SettingsDataModelObjectIdentifiersFormValues>({
|
||||
mode: 'onTouched',
|
||||
resolver: zodResolver(settingsDataModelObjectIdentifiersFormSchema),
|
||||
|
||||
@@ -27,15 +27,20 @@ type ManagedItemTagInfo = {
|
||||
};
|
||||
|
||||
export const getItemTagInfo = ({
|
||||
isCustom,
|
||||
isRemote,
|
||||
applicationId,
|
||||
objectMetadataItem: { isCustom, isRemote, applicationId },
|
||||
workspaceCustomApplicationId,
|
||||
}: {
|
||||
isCustom?: boolean;
|
||||
isRemote?: boolean;
|
||||
applicationId?: string | null;
|
||||
objectMetadataItem: {
|
||||
isCustom?: boolean;
|
||||
isRemote?: boolean;
|
||||
applicationId?: string | null;
|
||||
};
|
||||
workspaceCustomApplicationId?: string;
|
||||
}): ItemTagInfo => {
|
||||
if (isDefined(applicationId)) {
|
||||
if (
|
||||
isDefined(applicationId) &&
|
||||
applicationId !== workspaceCustomApplicationId
|
||||
) {
|
||||
return { labelText: 'Managed', labelColor: 'sky' };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user