feat: add lingui/no-unlocalized-strings ESLint rule and fix translations (#16610)
## Summary
This PR adds the `lingui/no-unlocalized-strings` ESLint rule to detect
untranslated strings and fixes translation issues across multiple
components.
## Changes
### ESLint Configuration (`eslint.config.react.mjs`)
- Added comprehensive `ignore` patterns for non-translatable strings
(CSS values, HTML attributes, technical identifiers)
- Added `ignoreNames` for props that don't need translation (className,
data-*, aria-*, etc.)
- Added `ignoreFunctions` for console methods, URL APIs, and other
non-user-facing functions
- Disabled rule for debug files, storybook, and test files
### Components Fixed (~19 files)
- Object record components (field inputs, pickers, merge dialogs)
- Settings components (accounts, admin panel)
- Serverless function components
- Record table and title cell components
## Status
🚧 **Work in Progress** - ~124 files remaining to fix
This PR is being submitted as draft to allow progressive fixing of
remaining translation issues.
## Testing
- Run `npx eslint "src/**/*.tsx"` in `packages/twenty-front` to check
remaining issues
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { type ReactNode, useContext } from 'react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay';
|
||||
import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext';
|
||||
@@ -25,7 +26,7 @@ export const ActionModal = ({
|
||||
title,
|
||||
subtitle,
|
||||
onConfirmClick,
|
||||
confirmButtonText = 'Confirm',
|
||||
confirmButtonText = t`Confirm`,
|
||||
confirmButtonAccent = 'danger',
|
||||
isLoading = false,
|
||||
closeSidePanelOnShowPageOptionsActionExecution,
|
||||
|
||||
+1
-4
@@ -46,8 +46,5 @@ export const ActionDisplay = ({
|
||||
return <ActionDropdownItem action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
return assertUnreachable(
|
||||
displayType,
|
||||
`Unsupported display type: ${displayType}`,
|
||||
);
|
||||
return assertUnreachable(displayType, 'Unsupported display type');
|
||||
};
|
||||
|
||||
+3
-1
@@ -17,6 +17,7 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext } from 'react';
|
||||
import { IconLayoutSidebarRightExpand } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
@@ -32,6 +33,7 @@ const StyledDropdownMenuContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const RecordIndexActionMenuDropdown = () => {
|
||||
const { t } = useLingui();
|
||||
const { actions } = useContext(ActionMenuContext);
|
||||
|
||||
const recordIndexActions = actions.filter(
|
||||
@@ -103,7 +105,7 @@ export const RecordIndexActionMenuDropdown = () => {
|
||||
openCommandMenu();
|
||||
}}
|
||||
focused={selectedItemId === 'more-actions'}
|
||||
text="More actions"
|
||||
text={t`More actions`}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SelectableList>
|
||||
|
||||
+4
-2
@@ -1,5 +1,6 @@
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { CalendarEventParticipantsResponseStatus } from '@/activities/calendar/components/CalendarEventParticipantsResponseStatus';
|
||||
import { type CalendarEvent } from '@/activities/calendar/types/CalendarEvent';
|
||||
@@ -77,6 +78,7 @@ const StyledPropertyBox = styled(PropertyBox)`
|
||||
export const CalendarEventDetails = ({
|
||||
calendarEvent,
|
||||
}: CalendarEventDetailsProps) => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.CalendarEvent,
|
||||
@@ -155,14 +157,14 @@ export const CalendarEventDetails = ({
|
||||
variant={ChipVariant.Highlighted}
|
||||
clickable={false}
|
||||
leftComponent={<IconCalendarEvent size={theme.icon.size.md} />}
|
||||
label="Event"
|
||||
label={t`Event`}
|
||||
/>
|
||||
<StyledHeader>
|
||||
<StyledTitle canceled={calendarEvent.isCanceled}>
|
||||
{calendarEvent.title}
|
||||
</StyledTitle>
|
||||
<StyledCreatedAt>
|
||||
Created{' '}
|
||||
{t`Created`}{' '}
|
||||
{beautifyPastDateRelativeToNow(
|
||||
new Date(calendarEvent.externalCreatedAt),
|
||||
)}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ export const CalendarEventParticipantsResponseStatus = ({
|
||||
}
|
||||
});
|
||||
|
||||
const responseStatusOrder: ('Yes' | 'Maybe' | 'No')[] = [
|
||||
const responseStatusOrder: Array<'Yes' | 'Maybe' | 'No'> = [
|
||||
'Yes',
|
||||
'Maybe',
|
||||
'No',
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { format } from 'date-fns';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
@@ -93,7 +94,7 @@ export const CalendarEventRow = ({
|
||||
const hasEnded = hasCalendarEventEnded(calendarEvent);
|
||||
|
||||
const startTimeLabel = calendarEvent.isFullDay
|
||||
? 'All day'
|
||||
? t`All day`
|
||||
: format(startsAt, 'HH:mm');
|
||||
const endTimeLabel = calendarEvent.isFullDay ? '' : format(endsAt, 'HH:mm');
|
||||
|
||||
|
||||
+6
-3
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { format, getYear } from 'date-fns';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
@@ -46,6 +47,7 @@ const StyledTitleContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const CalendarEventsCard = () => {
|
||||
const { t } = useLingui();
|
||||
const targetRecord = useTargetRecord();
|
||||
const { localeCatalog } = useRecoilValue(dateLocaleState);
|
||||
|
||||
@@ -95,6 +97,8 @@ export const CalendarEventsCard = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const objectName = targetRecord.targetObjectNameSingular;
|
||||
|
||||
if (firstQueryLoading) {
|
||||
return <SkeletonLoader />;
|
||||
}
|
||||
@@ -109,11 +113,10 @@ export const CalendarEventsCard = () => {
|
||||
<AnimatedPlaceholder type="noMatchRecord" />
|
||||
<AnimatedPlaceholderEmptyTextContainer>
|
||||
<AnimatedPlaceholderEmptyTitle>
|
||||
No Events
|
||||
{t`No Events`}
|
||||
</AnimatedPlaceholderEmptyTitle>
|
||||
<AnimatedPlaceholderEmptySubTitle>
|
||||
No events have been scheduled with this{' '}
|
||||
{targetRecord.targetObjectNameSingular} yet.
|
||||
{t`No events have been scheduled with this ${objectName} yet.`}
|
||||
</AnimatedPlaceholderEmptySubTitle>
|
||||
</AnimatedPlaceholderEmptyTextContainer>
|
||||
</AnimatedPlaceholderEmptyContainer>
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
|
||||
type CustomResolverFetchMoreLoaderProps = {
|
||||
@@ -34,7 +35,7 @@ export const CustomResolverFetchMoreLoader = ({
|
||||
|
||||
return (
|
||||
<StyledContainer ref={tbodyRef}>
|
||||
{loading && <StyledText>Loading more...</StyledText>}
|
||||
{loading && <StyledText>{t`Loading more...`}</StyledText>}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
@@ -11,7 +12,7 @@ export const EmailLoader = ({ loadingText }: { loadingText?: string }) => (
|
||||
<AnimatedPlaceholder type="loadingMessages" />
|
||||
<AnimatedPlaceholderEmptyTextContainer>
|
||||
<AnimatedPlaceholderEmptyTitle>
|
||||
{loadingText || 'Loading emails'}
|
||||
{loadingText || t`Loading emails`}
|
||||
</AnimatedPlaceholderEmptyTitle>
|
||||
<Loader />
|
||||
</AnimatedPlaceholderEmptyTextContainer>
|
||||
|
||||
+4
-2
@@ -1,5 +1,6 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { AppTooltip, IconLock, TooltipDelay } from 'twenty-ui/display';
|
||||
import { MessageChannelVisibility } from '~/generated/graphql';
|
||||
|
||||
@@ -31,6 +32,7 @@ type EmailThreadNotSharedProps = {
|
||||
export const EmailThreadNotShared = ({
|
||||
visibility,
|
||||
}: EmailThreadNotSharedProps) => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const containerId = 'email-thread-not-shared';
|
||||
const isCompact = visibility === MessageChannelVisibility.SUBJECT;
|
||||
@@ -39,12 +41,12 @@ export const EmailThreadNotShared = ({
|
||||
<>
|
||||
<StyledContainer id={containerId} isCompact={isCompact}>
|
||||
<IconLock size={theme.icon.size.sm} />
|
||||
{'Not shared'}
|
||||
{t`Not shared`}
|
||||
</StyledContainer>
|
||||
{visibility === MessageChannelVisibility.SUBJECT && (
|
||||
<AppTooltip
|
||||
anchorSelect={`#${containerId}`}
|
||||
content="Only the subject is shared"
|
||||
content={t`Only the subject is shared`}
|
||||
delay={TooltipDelay.mediumDelay}
|
||||
noArrow
|
||||
place="bottom"
|
||||
|
||||
+5
-3
@@ -3,6 +3,7 @@ import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
IconDotsVertical,
|
||||
IconDownload,
|
||||
@@ -27,6 +28,7 @@ export const AttachmentDropdown = ({
|
||||
attachmentId,
|
||||
hasDownloadPermission,
|
||||
}: AttachmentDropdownProps) => {
|
||||
const { t } = useLingui();
|
||||
const dropdownId = `${attachmentId}-attachment-dropdown`;
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
@@ -57,18 +59,18 @@ export const AttachmentDropdown = ({
|
||||
<DropdownMenuItemsContainer>
|
||||
{hasDownloadPermission && (
|
||||
<MenuItem
|
||||
text="Download"
|
||||
text={t`Download`}
|
||||
LeftIcon={IconDownload}
|
||||
onClick={handleDownload}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
text="Rename"
|
||||
text={t`Rename`}
|
||||
LeftIcon={IconPencil}
|
||||
onClick={handleRename}
|
||||
/>
|
||||
<MenuItem
|
||||
text="Delete"
|
||||
text={t`Delete`}
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
onClick={handleDelete}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { lazy, type ReactElement, Suspense, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
@@ -240,7 +241,7 @@ export const AttachmentList = ({
|
||||
fallback={
|
||||
<StyledLoadingContainer>
|
||||
<StyledLoadingText>
|
||||
Loading document viewer...
|
||||
{t`Loading document viewer...`}
|
||||
</StyledLoadingText>
|
||||
</StyledLoadingContainer>
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import DocViewer, { DocViewerRenderers } from '@cyntler/react-doc-viewer';
|
||||
import '@cyntler/react-doc-viewer/dist/index.css';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconDownload } from 'twenty-ui/display';
|
||||
@@ -99,6 +99,7 @@ export const DocumentViewer = ({
|
||||
documentName,
|
||||
documentUrl,
|
||||
}: DocumentViewerProps) => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const [csvPreview, setCsvPreview] = useState<string | undefined>(undefined);
|
||||
|
||||
@@ -141,7 +142,7 @@ export const DocumentViewer = ({
|
||||
</StyledMessage>
|
||||
<Button
|
||||
Icon={IconDownload}
|
||||
title="Download File"
|
||||
title={t`Download File`}
|
||||
onClick={() => downloadFile(documentUrl, documentName)}
|
||||
variant="secondary"
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
|
||||
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||
@@ -47,6 +48,7 @@ export const DropZone = ({
|
||||
setIsDraggingFile,
|
||||
onUploadFiles,
|
||||
}: DropZoneProps) => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const { maxFileSize } = useSpreadsheetImportInternal();
|
||||
|
||||
@@ -85,9 +87,9 @@ export const DropZone = ({
|
||||
stroke={theme.icon.stroke.sm}
|
||||
size={theme.icon.size.lg}
|
||||
/>
|
||||
<StyledUploadDragTitle>Upload files</StyledUploadDragTitle>
|
||||
<StyledUploadDragTitle>{t`Upload files`}</StyledUploadDragTitle>
|
||||
<StyledUploadDragSubTitle>
|
||||
Drag and Drop Here
|
||||
{t`Drag and Drop Here`}
|
||||
</StyledUploadDragSubTitle>
|
||||
</>
|
||||
)}
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
@@ -95,7 +96,7 @@ export const ActivityTargetsInlineCell = ({
|
||||
}}
|
||||
/>
|
||||
),
|
||||
label: 'Relations',
|
||||
label: t`Relations`,
|
||||
displayModeContent: (
|
||||
<ActivityTargetChips
|
||||
activityTargetObjectRecords={activityTargetObjectRecords}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell';
|
||||
import { useActivityTargetsComponentInstanceId } from '@/activities/inline-cell/hooks/useActivityTargetsComponentInstanceId';
|
||||
@@ -88,7 +89,7 @@ export const NoteTile = ({
|
||||
})
|
||||
}
|
||||
>
|
||||
<StyledNoteTitle>{note.title ?? 'Task Title'}</StyledNoteTitle>
|
||||
<StyledNoteTitle>{note.title ?? t`Task Title`}</StyledNoteTitle>
|
||||
<StyledCardContent>{body}</StyledCardContent>
|
||||
</StyledCardDetailsContainer>
|
||||
<StyledFooter>
|
||||
|
||||
@@ -68,10 +68,10 @@ export const NotesCard = () => {
|
||||
<AnimatedPlaceholder type="noNote" />
|
||||
<AnimatedPlaceholderEmptyTextContainer>
|
||||
<AnimatedPlaceholderEmptyTitle>
|
||||
No notes
|
||||
{t`No notes`}
|
||||
</AnimatedPlaceholderEmptyTitle>
|
||||
<AnimatedPlaceholderEmptySubTitle>
|
||||
There are no associated notes with this record.
|
||||
{t`There are no associated notes with this record.`}
|
||||
</AnimatedPlaceholderEmptySubTitle>
|
||||
</AnimatedPlaceholderEmptyTextContainer>
|
||||
{hasObjectUpdatePermissions && (
|
||||
|
||||
@@ -78,10 +78,10 @@ export const TaskGroups = ({ targetableObject }: TaskGroupsProps) => {
|
||||
<AnimatedPlaceholder type="noTask" />
|
||||
<AnimatedPlaceholderEmptyTextContainer>
|
||||
<AnimatedPlaceholderEmptyTitle>
|
||||
Mission accomplished!
|
||||
{t`Mission accomplished!`}
|
||||
</AnimatedPlaceholderEmptyTitle>
|
||||
<AnimatedPlaceholderEmptySubTitle>
|
||||
All tasks addressed. Maintain the momentum.
|
||||
{t`All tasks addressed. Maintain the momentum.`}
|
||||
</AnimatedPlaceholderEmptySubTitle>
|
||||
</AnimatedPlaceholderEmptyTextContainer>
|
||||
{hasObjectUpdatePermissions && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell';
|
||||
import { getActivitySummary } from '@/activities/utils/getActivitySummary';
|
||||
@@ -114,7 +115,7 @@ export const TaskRow = ({ task }: { task: Task }) => {
|
||||
/>
|
||||
</StyledCheckboxContainer>
|
||||
<StyledTaskTitle completed={task.status === 'DONE'}>
|
||||
{task.title || <StyledPlaceholder>Task title</StyledPlaceholder>}
|
||||
{task.title || <StyledPlaceholder>{t`Task title`}</StyledPlaceholder>}
|
||||
</StyledTaskTitle>
|
||||
<StyledTaskBody>
|
||||
<OverflowingTextWithTooltip text={body} />
|
||||
|
||||
+2
-2
@@ -72,10 +72,10 @@ export const TimelineCard = () => {
|
||||
<AnimatedPlaceholder type="emptyTimeline" />
|
||||
<AnimatedPlaceholderEmptyTextContainer>
|
||||
<AnimatedPlaceholderEmptyTitle>
|
||||
No activity yet
|
||||
{t`No activity yet`}
|
||||
</AnimatedPlaceholderEmptyTitle>
|
||||
<AnimatedPlaceholderEmptySubTitle>
|
||||
There is no activity associated with this record.
|
||||
{t`There is no activity associated with this record.`}
|
||||
</AnimatedPlaceholderEmptySubTitle>
|
||||
</AnimatedPlaceholderEmptyTextContainer>
|
||||
</EmptyContainer>
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import {
|
||||
type EventRowDynamicComponentProps,
|
||||
@@ -91,7 +92,7 @@ export const EventRowActivity = ({
|
||||
return event.linkedRecordCachedName;
|
||||
}
|
||||
|
||||
return 'Untitled';
|
||||
return t`Untitled`;
|
||||
};
|
||||
const activityTitle = computeActivityTitle();
|
||||
|
||||
@@ -103,7 +104,7 @@ export const EventRowActivity = ({
|
||||
<StyledRow>
|
||||
<StyledEventRowItemColumn>{authorFullName}</StyledEventRowItemColumn>
|
||||
<StyledEventRowItemAction>
|
||||
{`${eventAction} a related ${eventObject}`}
|
||||
{t`${eventAction} a related ${eventObject}`}
|
||||
</StyledEventRowItemAction>
|
||||
<StyledLinkedActivity
|
||||
onClick={() =>
|
||||
|
||||
+4
-3
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isUndefined } from '@sniptt/guards';
|
||||
|
||||
import { CalendarEventNotSharedContent } from '@/activities/calendar/components/CalendarEventNotSharedContent';
|
||||
@@ -138,14 +139,14 @@ export const EventCardCalendarEvent = ({
|
||||
);
|
||||
|
||||
if (shouldHandleNotFound) {
|
||||
return <div>Calendar event not found</div>;
|
||||
return <div>{t`Calendar event not found`}</div>;
|
||||
}
|
||||
|
||||
return <div>Error loading calendar event</div>;
|
||||
return <div>{t`Error loading calendar event`}</div>;
|
||||
}
|
||||
|
||||
if (loading || isUndefined(calendarEvent)) {
|
||||
return <div>Loading...</div>;
|
||||
return <div>{t`Loading...`}</div>;
|
||||
}
|
||||
|
||||
const startsAtDate = calendarEvent?.startsAt;
|
||||
|
||||
+3
-1
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { EventCardCalendarEvent } from '@/activities/timeline-activities/rows/calendar/components/EventCardCalendarEvent';
|
||||
@@ -30,6 +31,7 @@ export const EventRowCalendarEvent = ({
|
||||
authorFullName,
|
||||
labelIdentifierValue,
|
||||
}: EventRowCalendarEventProps) => {
|
||||
const { t } = useLingui();
|
||||
const [, eventAction] = event.name.split('.');
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -42,7 +44,7 @@ export const EventRowCalendarEvent = ({
|
||||
<StyledRowContainer>
|
||||
<StyledEventRowItemColumn>{authorFullName}</StyledEventRowItemColumn>
|
||||
<StyledEventRowItemAction>
|
||||
linked a calendar event with {labelIdentifierValue}
|
||||
{t`linked a calendar event with ${labelIdentifierValue}`}
|
||||
</StyledEventRowItemAction>
|
||||
<EventCardToggleButton isOpen={isOpen} setIsOpen={setIsOpen} />
|
||||
</StyledRowContainer>
|
||||
|
||||
+7
-4
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { EventCard } from '@/activities/timeline-activities/rows/components/EventCard';
|
||||
@@ -54,6 +55,7 @@ export const EventRowMainObjectUpdated = ({
|
||||
mainObjectMetadataItem,
|
||||
createdAt,
|
||||
}: EventRowMainObjectUpdatedProps) => {
|
||||
const { t } = useLingui();
|
||||
const diff: Record<string, { before: any; after: any }> =
|
||||
event.properties?.diff;
|
||||
|
||||
@@ -70,12 +72,15 @@ export const EventRowMainObjectUpdated = ({
|
||||
throw new Error('Cannot render update description without changes');
|
||||
}
|
||||
|
||||
const fieldCount = diffEntries.length;
|
||||
const recordLabel = labelIdentifierValue;
|
||||
|
||||
return (
|
||||
<StyledEventRowMainObjectUpdatedContainer>
|
||||
<StyledRowContainer>
|
||||
<StyledRow>
|
||||
<StyledEventRowItemColumn>{authorFullName}</StyledEventRowItemColumn>
|
||||
updated
|
||||
{t`updated`}
|
||||
{diffEntries.length === 1 && (
|
||||
<EventFieldDiffContainer
|
||||
mainObjectMetadataItem={mainObjectMetadataItem}
|
||||
@@ -87,9 +92,7 @@ export const EventRowMainObjectUpdated = ({
|
||||
)}
|
||||
{diffEntries.length > 1 && (
|
||||
<>
|
||||
<span>
|
||||
{diffEntries.length} fields on {labelIdentifierValue}
|
||||
</span>
|
||||
<span>{t`${fieldCount} fields on ${recordLabel}`}</span>
|
||||
<EventCardToggleButton isOpen={isOpen} setIsOpen={setIsOpen} />
|
||||
</>
|
||||
)}
|
||||
|
||||
+3
-2
@@ -7,7 +7,7 @@ import { useOpenEmailThreadInCommandMenu } from '@/command-menu/hooks/useOpenEma
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
@@ -59,6 +59,7 @@ export const EventCardMessage = ({
|
||||
messageId: string;
|
||||
authorFullName: string;
|
||||
}) => {
|
||||
const { t } = useLingui();
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
const { openEmailThreadInCommandMenu } = useOpenEmailThreadInCommandMenu();
|
||||
|
||||
@@ -142,7 +143,7 @@ export const EventCardMessage = ({
|
||||
{message.subject !==
|
||||
FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED
|
||||
? message.subject
|
||||
: `Subject not shared`}
|
||||
: t`Subject not shared`}
|
||||
</StyledEmailTitle>
|
||||
<StyledEmailParticipants>
|
||||
<OverflowingTextWithTooltip text={messageParticipantHandles} />
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { EventCard } from '@/activities/timeline-activities/rows/components/EventCard';
|
||||
@@ -42,7 +43,7 @@ export const EventRowMessage = ({
|
||||
<StyledRowContainer>
|
||||
<StyledEventRowItemColumn>{authorFullName}</StyledEventRowItemColumn>
|
||||
<StyledEventRowItemAction>
|
||||
linked an email with
|
||||
{t`linked an email with`}
|
||||
</StyledEventRowItemAction>
|
||||
<StyledEventRowItemColumn>
|
||||
{labelIdentifierValue}
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
|
||||
import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type Editor } from '@tiptap/react';
|
||||
import { useId } from 'react';
|
||||
import { IconPilcrow } from 'twenty-ui/display';
|
||||
@@ -50,7 +51,7 @@ export const TurnIntoBlockDropdown = ({
|
||||
|
||||
const options = useTurnIntoBlockOptions(editor);
|
||||
const activeItem = options.find((option) => option.isActive());
|
||||
const { icon: ActiveIcon = IconPilcrow, title: activeTitle = 'Paragraph' } =
|
||||
const { icon: ActiveIcon = IconPilcrow, title: activeTitle = t`Paragraph` } =
|
||||
activeItem ?? {};
|
||||
|
||||
return (
|
||||
|
||||
+7
-4
@@ -1,3 +1,4 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { type Editor, useEditorState } from '@tiptap/react';
|
||||
import {
|
||||
type IconComponent,
|
||||
@@ -17,12 +18,14 @@ export type TurnIntoBlockOptions = {
|
||||
};
|
||||
|
||||
export const useTurnIntoBlockOptions = (editor: Editor) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
return useEditorState({
|
||||
editor,
|
||||
selector: ({ editor }): TurnIntoBlockOptions[] => [
|
||||
{
|
||||
id: 'paragraph',
|
||||
title: 'Paragraph',
|
||||
title: t`Paragraph`,
|
||||
icon: IconPilcrow,
|
||||
onClick: () => {
|
||||
return editor.chain().focus().setParagraph().run();
|
||||
@@ -36,7 +39,7 @@ export const useTurnIntoBlockOptions = (editor: Editor) => {
|
||||
},
|
||||
{
|
||||
id: 'heading1',
|
||||
title: 'Heading 1',
|
||||
title: t`Heading 1`,
|
||||
icon: IconH1,
|
||||
onClick: () => {
|
||||
return editor.chain().focus().setHeading({ level: 1 }).run();
|
||||
@@ -50,7 +53,7 @@ export const useTurnIntoBlockOptions = (editor: Editor) => {
|
||||
},
|
||||
{
|
||||
id: 'heading2',
|
||||
title: 'Heading 2',
|
||||
title: t`Heading 2`,
|
||||
icon: IconH2,
|
||||
onClick: () => {
|
||||
return editor.chain().focus().setHeading({ level: 2 }).run();
|
||||
@@ -64,7 +67,7 @@ export const useTurnIntoBlockOptions = (editor: Editor) => {
|
||||
},
|
||||
{
|
||||
id: 'heading3',
|
||||
title: 'Heading 3',
|
||||
title: t`Heading 3`,
|
||||
icon: IconH3,
|
||||
onClick: () => {
|
||||
return editor.chain().focus().setHeading({ level: 3 }).run();
|
||||
|
||||
+4
-2
@@ -51,16 +51,18 @@ export const useUploadWorkflowFile = () => {
|
||||
createdAt: uploadedFile.createdAt,
|
||||
};
|
||||
|
||||
const fileName = file.name;
|
||||
enqueueSuccessSnackBar({
|
||||
message: `File "${file.name}" uploaded successfully`,
|
||||
message: t`File "${fileName}" uploaded successfully`,
|
||||
});
|
||||
|
||||
return workflowFile;
|
||||
} catch (error) {
|
||||
logError(`Failed to upload workflow file "${file.name}": ${error}`);
|
||||
|
||||
const fileNameForError = file.name;
|
||||
enqueueErrorSnackBar({
|
||||
message: `Failed to upload "${file.name}"`,
|
||||
message: t`Failed to upload "${fileNameForError}"`,
|
||||
});
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { getLinkToShowPage } from '@/object-metadata/utils/getLinkToShowPage';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { AvatarChip, ChipVariant, LinkChip } from 'twenty-ui/components';
|
||||
|
||||
@@ -29,6 +30,7 @@ export const RecordLink = ({
|
||||
return (
|
||||
<LinkChip
|
||||
label={displayName}
|
||||
emptyLabel={t`Untitled`}
|
||||
to={linkToShowPage}
|
||||
variant={ChipVariant.Highlighted}
|
||||
leftComponent={
|
||||
|
||||
@@ -152,45 +152,51 @@ type TimingTabProps = {
|
||||
};
|
||||
|
||||
const TimingTab = ({ debug }: TimingTabProps) => {
|
||||
const { t } = useLingui();
|
||||
const totalTime =
|
||||
debug.agentExecutionStartTimeMs !== undefined
|
||||
? `${debug.agentExecutionStartTimeMs + (debug.agentExecutionTimeMs || 0)}ms`
|
||||
: undefined;
|
||||
|
||||
const totalCost =
|
||||
debug.totalCostInCredits !== undefined
|
||||
? formatNumber(debug.totalCostInCredits)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<StyledTimingSection>
|
||||
<TimingRow
|
||||
label="Routing decision"
|
||||
label={t`Routing decision`}
|
||||
value={debug.routingTimeMs && `${debug.routingTimeMs}ms`}
|
||||
/>
|
||||
<TimingRow
|
||||
label="Context building (routing)"
|
||||
label={t`Context building (routing)`}
|
||||
value={debug.contextBuildTimeMs && `${debug.contextBuildTimeMs}ms`}
|
||||
/>
|
||||
<TimingRow
|
||||
label="Context building (agent)"
|
||||
label={t`Context building (agent)`}
|
||||
value={
|
||||
debug.agentContextBuildTimeMs && `${debug.agentContextBuildTimeMs}ms`
|
||||
}
|
||||
/>
|
||||
<TimingRow
|
||||
label="Tool generation"
|
||||
label={t`Tool generation`}
|
||||
value={debug.toolGenerationTimeMs && `${debug.toolGenerationTimeMs}ms`}
|
||||
/>
|
||||
<TimingRow
|
||||
label="AI request prep"
|
||||
label={t`AI request prep`}
|
||||
value={debug.aiRequestPrepTimeMs && `${debug.aiRequestPrepTimeMs}ms`}
|
||||
/>
|
||||
<TimingRow
|
||||
label="Agent execution"
|
||||
label={t`Agent execution`}
|
||||
value={debug.agentExecutionTimeMs && `${debug.agentExecutionTimeMs}ms`}
|
||||
/>
|
||||
<TimingRow label="Total time" value={totalTime} />
|
||||
<TimingRow label="Available tools" value={debug.toolCount} />
|
||||
<TimingRow label="Tool calls made" value={debug.toolCallCount} />
|
||||
<TimingRow label="Context records" value={debug.contextRecordCount} />
|
||||
<TimingRow label={t`Total time`} value={totalTime} />
|
||||
<TimingRow label={t`Available tools`} value={debug.toolCount} />
|
||||
<TimingRow label={t`Tool calls made`} value={debug.toolCallCount} />
|
||||
<TimingRow label={t`Context records`} value={debug.contextRecordCount} />
|
||||
<TimingRow
|
||||
label="Context size"
|
||||
label={t`Context size`}
|
||||
value={
|
||||
debug.contextSizeBytes !== undefined
|
||||
? formatBytes(debug.contextSizeBytes)
|
||||
@@ -198,7 +204,7 @@ const TimingTab = ({ debug }: TimingTabProps) => {
|
||||
}
|
||||
/>
|
||||
<TimingRow
|
||||
label="Routing tokens"
|
||||
label={t`Routing tokens`}
|
||||
value={
|
||||
debug.routingTotalTokens !== undefined
|
||||
? formatTokenBreakdown(
|
||||
@@ -210,7 +216,7 @@ const TimingTab = ({ debug }: TimingTabProps) => {
|
||||
}
|
||||
/>
|
||||
<TimingRow
|
||||
label="Agent tokens"
|
||||
label={t`Agent tokens`}
|
||||
value={
|
||||
debug.agentTotalTokens !== undefined
|
||||
? formatTokenBreakdown(
|
||||
@@ -222,12 +228,8 @@ const TimingTab = ({ debug }: TimingTabProps) => {
|
||||
}
|
||||
/>
|
||||
<TimingRow
|
||||
label="Total cost"
|
||||
value={
|
||||
debug.totalCostInCredits !== undefined
|
||||
? `${formatNumber(debug.totalCostInCredits)} credits`
|
||||
: undefined
|
||||
}
|
||||
label={t`Total cost`}
|
||||
value={totalCost !== undefined ? t`${totalCost} credits` : undefined}
|
||||
/>
|
||||
</StyledTimingSection>
|
||||
);
|
||||
@@ -279,7 +281,7 @@ const ContextTab = ({ debug, copyToClipboard }: ContextTabProps) => {
|
||||
if (!debug.context) {
|
||||
return (
|
||||
<StyledTimingLabel>
|
||||
No context was provided for this request
|
||||
{t`No context was provided for this request`}
|
||||
</StyledTimingLabel>
|
||||
);
|
||||
}
|
||||
@@ -302,9 +304,10 @@ const ContextTab = ({ debug, copyToClipboard }: ContextTabProps) => {
|
||||
</StyledJsonTreeContainer>
|
||||
);
|
||||
} catch {
|
||||
const contextValue = debug.context;
|
||||
return (
|
||||
<StyledTimingLabel>
|
||||
Failed to parse context: {debug.context}
|
||||
{t`Failed to parse context: ${contextValue}`}
|
||||
</StyledTimingLabel>
|
||||
);
|
||||
}
|
||||
@@ -315,6 +318,7 @@ type RoutingDebugDisplayProps = {
|
||||
};
|
||||
|
||||
export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
@@ -323,7 +327,7 @@ export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledToggleButton onClick={() => setIsExpanded(!isExpanded)}>
|
||||
<StyledTimingLabel>Debug Info</StyledTimingLabel>
|
||||
<StyledTimingLabel>{t`Debug Info`}</StyledTimingLabel>
|
||||
{isExpanded ? (
|
||||
<IconChevronUp size={theme.icon.size.sm} />
|
||||
) : (
|
||||
@@ -338,20 +342,20 @@ export const RoutingDebugDisplay = ({ debug }: RoutingDebugDisplayProps) => {
|
||||
isActive={activeTab === 'timing'}
|
||||
onClick={() => setActiveTab('timing')}
|
||||
>
|
||||
Timing
|
||||
{t`Timing`}
|
||||
</StyledTab>
|
||||
<StyledTab
|
||||
isActive={activeTab === 'details'}
|
||||
onClick={() => setActiveTab('details')}
|
||||
>
|
||||
Details
|
||||
{t`Details`}
|
||||
</StyledTab>
|
||||
{debug.context && (
|
||||
<StyledTab
|
||||
isActive={activeTab === 'context'}
|
||||
onClick={() => setActiveTab('context')}
|
||||
>
|
||||
Context
|
||||
{t`Context`}
|
||||
</StyledTab>
|
||||
)}
|
||||
</StyledTabContainer>
|
||||
|
||||
@@ -190,7 +190,7 @@ export const ToolStepRenderer = ({ toolPart }: { toolPart: ToolUIPart }) => {
|
||||
}
|
||||
|
||||
const displayMessage = hasError
|
||||
? 'Tool execution failed'
|
||||
? t`Tool execution failed`
|
||||
: output &&
|
||||
typeof output === 'object' &&
|
||||
'message' in output &&
|
||||
@@ -240,13 +240,13 @@ export const ToolStepRenderer = ({ toolPart }: { toolPart: ToolUIPart }) => {
|
||||
isActive={activeTab === 'output'}
|
||||
onClick={() => setActiveTab('output')}
|
||||
>
|
||||
Output
|
||||
{t`Output`}
|
||||
</StyledTab>
|
||||
<StyledTab
|
||||
isActive={activeTab === 'input'}
|
||||
onClick={() => setActiveTab('input')}
|
||||
>
|
||||
Input
|
||||
{t`Input`}
|
||||
</StyledTab>
|
||||
</StyledTabContainer>
|
||||
|
||||
|
||||
+9
-5
@@ -1,5 +1,6 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ProgressBar } from 'twenty-ui/feedback';
|
||||
@@ -103,6 +104,7 @@ const formatTokenCount = (count: number): string => {
|
||||
};
|
||||
|
||||
export const AIChatContextUsageButton = () => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const agentChatUsage = useRecoilValue(agentChatUsageState);
|
||||
@@ -125,6 +127,8 @@ export const AIChatContextUsageButton = () => {
|
||||
const formattedPercentage = percentage.toFixed(1);
|
||||
const totalCredits =
|
||||
agentChatUsage.inputCredits + agentChatUsage.outputCredits;
|
||||
const inputCredits = agentChatUsage.inputCredits.toLocaleString();
|
||||
const outputCredits = agentChatUsage.outputCredits.toLocaleString();
|
||||
|
||||
return (
|
||||
<StyledContainer
|
||||
@@ -162,23 +166,23 @@ export const AIChatContextUsageButton = () => {
|
||||
|
||||
<StyledBody>
|
||||
<StyledRow>
|
||||
<StyledLabel>Input</StyledLabel>
|
||||
<StyledLabel>{t`Input`}</StyledLabel>
|
||||
<StyledValue>
|
||||
{formatTokenCount(agentChatUsage.inputTokens)} •{' '}
|
||||
{agentChatUsage.inputCredits.toLocaleString()} credits
|
||||
{t`${inputCredits} credits`}
|
||||
</StyledValue>
|
||||
</StyledRow>
|
||||
<StyledRow>
|
||||
<StyledLabel>Output</StyledLabel>
|
||||
<StyledLabel>{t`Output`}</StyledLabel>
|
||||
<StyledValue>
|
||||
{formatTokenCount(agentChatUsage.outputTokens)} •{' '}
|
||||
{agentChatUsage.outputCredits.toLocaleString()} credits
|
||||
{t`${outputCredits} credits`}
|
||||
</StyledValue>
|
||||
</StyledRow>
|
||||
</StyledBody>
|
||||
|
||||
<StyledFooter>
|
||||
<StyledLabel>Total credits</StyledLabel>
|
||||
<StyledLabel>{t`Total credits`}</StyledLabel>
|
||||
<StyledPercentage>{totalCredits.toLocaleString()}</StyledPercentage>
|
||||
</StyledFooter>
|
||||
</StyledHoverCard>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getFileType } from '@/activities/files/utils/getFileType';
|
||||
import { useFileCategoryColors } from '@/file/hooks/useFileCategoryColors';
|
||||
import { IconMapping } from '@/file/utils/fileIconMappings';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type FileUIPart } from 'ai';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { AvatarChip, Chip, ChipVariant, LinkChip } from 'twenty-ui/components';
|
||||
@@ -23,7 +24,7 @@ export const AgentChatFilePreview = ({
|
||||
useFileCategoryColors();
|
||||
|
||||
const fileName =
|
||||
file instanceof File ? file.name : (file.filename ?? 'Unknown file');
|
||||
file instanceof File ? file.name : (file.filename ?? t`Unknown file`);
|
||||
|
||||
const fileUrl = file instanceof File ? undefined : file.url;
|
||||
|
||||
@@ -54,6 +55,7 @@ export const AgentChatFilePreview = ({
|
||||
return (
|
||||
<LinkChip
|
||||
label={fileName}
|
||||
emptyLabel={t`Untitled`}
|
||||
variant={ChipVariant.Static}
|
||||
to={fileUrl}
|
||||
target="_blank"
|
||||
@@ -66,6 +68,7 @@ export const AgentChatFilePreview = ({
|
||||
return (
|
||||
<Chip
|
||||
label={fileName}
|
||||
emptyLabel={t`Untitled`}
|
||||
variant={ChipVariant.Static}
|
||||
clickable={false}
|
||||
leftComponent={leftComponent}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
|
||||
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreViewFromViewIdFamilySelector';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
export const useGetBrowsingContext = () => {
|
||||
@@ -103,7 +104,7 @@ export const useGetBrowsingContext = () => {
|
||||
const fieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === filter.fieldMetadataId,
|
||||
);
|
||||
const fieldLabel = fieldMetadataItem?.label ?? 'Unknown field';
|
||||
const fieldLabel = fieldMetadataItem?.label ?? t`Unknown field`;
|
||||
|
||||
return `${fieldLabel} ${filter.operand} "${filter.displayValue}"`;
|
||||
});
|
||||
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
import { type Form } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -20,6 +21,7 @@ export const SignInUpEmailField = ({
|
||||
showErrors: boolean;
|
||||
onInputChange?: (value: string) => void;
|
||||
}) => {
|
||||
const { t } = useLingui();
|
||||
const form = useFormContext<Form>();
|
||||
|
||||
return (
|
||||
@@ -45,7 +47,7 @@ export const SignInUpEmailField = ({
|
||||
autoComplete="email"
|
||||
autoFocus
|
||||
value={value}
|
||||
placeholder="Email"
|
||||
placeholder={t`Email`}
|
||||
onBlur={onBlur}
|
||||
onChange={(email: string) => {
|
||||
if (isDefined(onInputChange)) onInputChange(email);
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ export const SignInUpPasswordField = ({
|
||||
autoFocus
|
||||
value={value}
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
placeholder={t`Password`}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
error={showErrors ? error?.message : undefined}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
@@ -20,12 +21,12 @@ const makeValidationSchema = (signInUpStep: SignInUpStep) =>
|
||||
email: z
|
||||
.string()
|
||||
.trim()
|
||||
.pipe(z.email({ error: 'Email must be a valid email' })),
|
||||
.pipe(z.email({ error: t`Email must be a valid email` })),
|
||||
password:
|
||||
signInUpStep === SignInUpStep.Password
|
||||
? z
|
||||
.string()
|
||||
.regex(PASSWORD_REGEX, 'Password must be min. 8 characters')
|
||||
.regex(PASSWORD_REGEX, t`Password must be min. 8 characters`)
|
||||
: z.string().optional(),
|
||||
captchaToken: z.string().default(''),
|
||||
})
|
||||
|
||||
+12
-5
@@ -1,19 +1,26 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
const otpValidationSchema = z.object({
|
||||
otp: z.string().trim().length(6, 'OTP must be exactly 6 digits'),
|
||||
});
|
||||
const createOtpValidationSchema = () =>
|
||||
z.object({
|
||||
otp: z
|
||||
.string()
|
||||
.trim()
|
||||
.length(6, t`OTP must be exactly 6 digits`),
|
||||
});
|
||||
|
||||
export type OTPFormValues = z.infer<typeof otpValidationSchema>;
|
||||
export type OTPFormValues = z.infer<
|
||||
ReturnType<typeof createOtpValidationSchema>
|
||||
>;
|
||||
export const useTwoFactorAuthenticationForm = () => {
|
||||
const form = useForm<OTPFormValues>({
|
||||
mode: 'onSubmit',
|
||||
defaultValues: {
|
||||
otp: '',
|
||||
},
|
||||
resolver: zodResolver(otpValidationSchema),
|
||||
resolver: zodResolver(createOtpValidationSchema()),
|
||||
});
|
||||
|
||||
return { form };
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ export const SettingsBillingSubscriptionInfo = ({
|
||||
message:
|
||||
oppositPlan === BillingPlanKey.ENTERPRISE
|
||||
? t`Subscription has been switched to ${oppositPlan} Plan.`
|
||||
: `Subscription will be switched to ${oppositPlan} Plan the ${beautifiedRenewDate}.`,
|
||||
: t`Subscription will be switched to ${oppositPlan} Plan the ${beautifiedRenewDate}.`,
|
||||
});
|
||||
} catch {
|
||||
enqueueErrorSnackBar({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
type TrialCardProps = {
|
||||
duration: number;
|
||||
@@ -24,10 +25,13 @@ const StyledCreditCardRequirementContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const TrialCard = ({ duration, withCreditCard }: TrialCardProps) => {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<StyledTrialCardContainer>
|
||||
<StyledTrialDurationContainer>{`${duration} days trial`}</StyledTrialDurationContainer>
|
||||
<StyledCreditCardRequirementContainer>{`${withCreditCard ? 'With Credit Card' : 'Without Credit Card'}`}</StyledCreditCardRequirementContainer>
|
||||
<StyledTrialDurationContainer>{t`${duration} days trial`}</StyledTrialDurationContainer>
|
||||
<StyledCreditCardRequirementContainer>
|
||||
{withCreditCard ? t`With Credit Card` : t`Without Credit Card`}
|
||||
</StyledCreditCardRequirementContainer>
|
||||
</StyledTrialCardContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+5
-1
@@ -60,9 +60,13 @@ export const MeteredPriceSelector = ({
|
||||
|
||||
const toOption = (meteredBillingPrice: MeteredBillingPrice) => {
|
||||
const price = formatNumber(meteredBillingPrice.tiers[0].flatAmount / 100);
|
||||
const credits = formatNumber(meteredBillingPrice.tiers[0].upTo, {
|
||||
abbreviate: true,
|
||||
decimals: 2,
|
||||
});
|
||||
|
||||
return {
|
||||
label: `${formatNumber(meteredBillingPrice.tiers[0].upTo, { abbreviate: true, decimals: 2 })} Credits - $${price}`,
|
||||
label: t`${credits} Credits - $${price}`,
|
||||
value: meteredBillingPrice.stripePriceId,
|
||||
};
|
||||
};
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { Fragment } from 'react/jsx-runtime';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -84,7 +85,7 @@ export const CommandMenuContextChip = ({
|
||||
{text?.trim?.() ? (
|
||||
<OverflowingTextWithTooltip text={text} />
|
||||
) : !forceEmptyText ? (
|
||||
<StyledEmptyText>Untitled</StyledEmptyText>
|
||||
<StyledEmptyText>{t`Untitled`}</StyledEmptyText>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelect
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
|
||||
@@ -93,7 +94,7 @@ export const CommandMenuList = ({
|
||||
) : null,
|
||||
)}
|
||||
{noResults && !loading && (
|
||||
<StyledEmpty>No results found</StyledEmpty>
|
||||
<StyledEmpty>{t`No results found`}</StyledEmpty>
|
||||
)}
|
||||
</SelectableList>
|
||||
</StyledInnerList>
|
||||
|
||||
+4
-4
@@ -97,9 +97,9 @@ export const CommandMenuWorkflowStepInfo = ({
|
||||
? isTrigger
|
||||
? (stepDefinition.definition.name ??
|
||||
(stepDefinition.definition.type === 'MANUAL'
|
||||
? 'Launch manually'
|
||||
: 'Trigger'))
|
||||
: (stepDefinition.definition.name ?? 'Action')
|
||||
? t`Launch manually`
|
||||
: t`Trigger`))
|
||||
: (stepDefinition.definition.name ?? t`Action`)
|
||||
: '';
|
||||
|
||||
const [editedTitle, setEditedTitle] = useState<string | null>(null);
|
||||
@@ -135,7 +135,7 @@ export const CommandMenuWorkflowStepInfo = ({
|
||||
actionType: stepDefinition.definition.type,
|
||||
});
|
||||
|
||||
const headerType = isTrigger ? 'Trigger' : 'Action';
|
||||
const headerType = isTrigger ? t`Trigger` : t`Action`;
|
||||
|
||||
const Icon = getIcon(headerIcon ?? 'IconDefault');
|
||||
|
||||
|
||||
-1
@@ -104,7 +104,6 @@ const meta: Meta<typeof CommandMenu> = {
|
||||
ObjectMetadataItemsDecorator,
|
||||
SnackBarDecorator,
|
||||
ComponentWithRouterDecorator,
|
||||
I18nFrontDecorator,
|
||||
],
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyO
|
||||
import { emitSidePanelOpenEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelOpenEvent';
|
||||
import { isDragSelectionStartEnabledState } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useCallback } from 'react';
|
||||
import { IconDotsVertical } from 'twenty-ui/display';
|
||||
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
||||
@@ -50,7 +51,7 @@ export const useCommandMenu = () => {
|
||||
closeAnyOpenDropdown();
|
||||
navigateCommandMenu({
|
||||
page: CommandMenuPages.Root,
|
||||
pageTitle: 'Command Menu',
|
||||
pageTitle: t`Command Menu`,
|
||||
pageIcon: IconDotsVertical,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
|
||||
import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState';
|
||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
import { IconMail } from 'twenty-ui/display';
|
||||
@@ -47,7 +48,7 @@ export const useOpenEmailThreadInCommandMenu = () => {
|
||||
|
||||
navigateCommandMenu({
|
||||
page: CommandMenuPages.ViewEmailThread,
|
||||
pageTitle: 'Email Thread',
|
||||
pageTitle: t`Email Thread`,
|
||||
pageIcon: IconMail,
|
||||
pageId: pageComponentInstanceId,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { viewableRichTextComponentState } from '@/command-menu/pages/rich-text-page/states/viewableRichTextComponentState';
|
||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { IconPencil } from 'twenty-ui/display';
|
||||
@@ -25,7 +26,7 @@ export const useRichTextCommandMenu = () => {
|
||||
openCommandMenu();
|
||||
navigateCommandMenu({
|
||||
page: CommandMenuPages.EditRichText,
|
||||
pageTitle: 'Rich Text',
|
||||
pageTitle: t`Rich Text`,
|
||||
pageIcon: IconPencil,
|
||||
});
|
||||
},
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ export const CommandMenuMessageThreadPage = () => {
|
||||
<StyledWrapper>
|
||||
<StyledContainer>
|
||||
{threadLoading ? (
|
||||
<EmailLoader loadingText="Loading thread" />
|
||||
<EmailLoader loadingText={t`Loading thread`} />
|
||||
) : (
|
||||
<>
|
||||
<EmailThreadHeader
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ export const CommandMenuPageLayoutIframeSettings = () => {
|
||||
<StyledContainer>
|
||||
<FormTextFieldInput
|
||||
label={t`URL to Embed`}
|
||||
placeholder="https://example.com/embed"
|
||||
placeholder={t`https://example.com/embed`}
|
||||
defaultValue={url}
|
||||
onChange={handleUrlChange}
|
||||
error={urlError}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ export const CommandMenuPageLayoutWidgetTypeSelect = () => {
|
||||
|
||||
const handleNavigateToIframeSettings = () => {
|
||||
if (!isDefined(pageLayoutEditingWidgetId)) {
|
||||
const newWidget = createPageLayoutIframeWidget('Untitled iFrame', null);
|
||||
const newWidget = createPageLayoutIframeWidget(t`Untitled iFrame`, null);
|
||||
|
||||
setPageLayoutEditingWidgetId(newWidget.id);
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { ColorSample } from 'twenty-ui/display';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import { type ThemeColor } from 'twenty-ui/theme';
|
||||
@@ -57,7 +58,7 @@ export const ChartColorPaletteOption = ({
|
||||
}}
|
||||
>
|
||||
<MenuItemSelect
|
||||
text={'Palette'}
|
||||
text={t`Palette`}
|
||||
selected={false}
|
||||
focused={selectedItemId === 'auto' || currentColor === 'auto'}
|
||||
contextualText={colorSamples}
|
||||
|
||||
+4
-3
@@ -24,6 +24,7 @@ import { getWorkflowRunStepExecutionStatus } from '@/workflow/workflow-steps/uti
|
||||
import { WorkflowIteratorSubStepSwitcher } from '@/workflow/workflow-steps/workflow-actions/iterator-action/components/WorkflowIteratorSubStepSwitcher';
|
||||
import styled from '@emotion/styled';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconLogin2, IconLogout, IconStepInto } from 'twenty-ui/display';
|
||||
|
||||
@@ -96,18 +97,18 @@ export const CommandMenuWorkflowRunViewStepContent = () => {
|
||||
const tabs: SingleTabProps<TabId>[] = [
|
||||
{
|
||||
id: WorkflowRunTabId.OUTPUT,
|
||||
title: 'Output',
|
||||
title: t`Output`,
|
||||
Icon: IconLogout,
|
||||
disabled: isOutputTabDisabled,
|
||||
},
|
||||
{
|
||||
id: WorkflowRunTabId.NODE,
|
||||
title: 'Node',
|
||||
title: t`Node`,
|
||||
Icon: IconStepInto,
|
||||
},
|
||||
{
|
||||
id: WorkflowRunTabId.INPUT,
|
||||
title: 'Input',
|
||||
title: t`Input`,
|
||||
Icon: IconLogin2,
|
||||
disabled: isInputTabDisabled,
|
||||
},
|
||||
|
||||
+3
-2
@@ -15,6 +15,7 @@ import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/Other
|
||||
import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger';
|
||||
import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/getTriggerDefaultDefinition';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
@@ -76,7 +77,7 @@ export const CommandMenuWorkflowSelectTriggerTypeContent = () => {
|
||||
return (
|
||||
<RightDrawerStepListContainer>
|
||||
<RightDrawerWorkflowSelectStepTitle>
|
||||
Data
|
||||
{t`Data`}
|
||||
</RightDrawerWorkflowSelectStepTitle>
|
||||
{DATABASE_TRIGGER_TYPES.map((action) => {
|
||||
const Icon = getIcon(action.icon);
|
||||
@@ -92,7 +93,7 @@ export const CommandMenuWorkflowSelectTriggerTypeContent = () => {
|
||||
})}
|
||||
|
||||
<RightDrawerWorkflowSelectStepTitle>
|
||||
Others
|
||||
{t`Others`}
|
||||
</RightDrawerWorkflowSelectStepTitle>
|
||||
{OTHER_TRIGGER_TYPES.map((action) => {
|
||||
const Icon = getIcon(action.icon);
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { AppErrorDisplay } from '@/error-handler/components/internal/AppErrorDis
|
||||
import { type AppErrorDisplayProps } from '@/error-handler/types/AppErrorDisplayProps';
|
||||
import { PageBody } from '@/ui/layout/page/components/PageBody';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
type AppFullScreenErrorFallbackProps = AppErrorDisplayProps;
|
||||
|
||||
@@ -18,7 +19,7 @@ const StyledContainer = styled.div`
|
||||
export const AppFullScreenErrorFallback = ({
|
||||
error,
|
||||
resetErrorBoundary,
|
||||
title = 'Sorry, something went wrong',
|
||||
title = t`Sorry, something went wrong`,
|
||||
}: AppFullScreenErrorFallbackProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
|
||||
@@ -3,13 +3,14 @@ import { type AppErrorDisplayProps } from '@/error-handler/types/AppErrorDisplay
|
||||
import { PageBody } from '@/ui/layout/page/components/PageBody';
|
||||
import { PageContainer } from '@/ui/layout/page/components/PageContainer';
|
||||
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
type AppPageErrorFallbackProps = AppErrorDisplayProps;
|
||||
|
||||
export const AppPageErrorFallback = ({
|
||||
error,
|
||||
resetErrorBoundary,
|
||||
title = 'Sorry, something went wrong',
|
||||
title = t`Sorry, something went wrong`,
|
||||
}: AppPageErrorFallbackProps) => {
|
||||
return (
|
||||
<PageContainer>
|
||||
|
||||
@@ -109,11 +109,11 @@ export const AppRootErrorFallback = ({
|
||||
<StyledImageContainer>
|
||||
<StyledBackgroundImage
|
||||
src="/images/placeholders/background/error_index_bg.png"
|
||||
alt="Background"
|
||||
alt={t`Background`}
|
||||
/>
|
||||
<StyledInnerImage
|
||||
src="/images/placeholders/moving-image/error_index.png"
|
||||
alt="Inner"
|
||||
alt={t`Error illustration`}
|
||||
/>
|
||||
</StyledImageContainer>
|
||||
<StyledEmptyTextContainer>
|
||||
@@ -124,7 +124,7 @@ export const AppRootErrorFallback = ({
|
||||
</StyledEmptyTextContainer>
|
||||
<StyledButton onClick={resetErrorBoundary}>
|
||||
<StyledIcon size={16} />
|
||||
Reload
|
||||
{t`Reload`}
|
||||
</StyledButton>
|
||||
</StyledEmptyContainer>
|
||||
</StyledPanel>
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import {
|
||||
|
||||
export const AppErrorDisplay = ({
|
||||
resetErrorBoundary,
|
||||
title = 'Sorry, something went wrong',
|
||||
title = t`Sorry, something went wrong`,
|
||||
}: AppErrorDisplayProps) => {
|
||||
return (
|
||||
<AnimatedPlaceholderEmptyContainer>
|
||||
|
||||
+15
-3
@@ -25,6 +25,7 @@ import { currentFavoriteFolderIdState } from '@/ui/navigation/navigation-drawer/
|
||||
import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { Droppable } from '@hello-pangea/dnd';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
@@ -47,6 +48,7 @@ export const CurrentWorkspaceMemberFavorites = ({
|
||||
folder,
|
||||
isGroup,
|
||||
}: CurrentWorkspaceMemberFavoritesProps) => {
|
||||
const { t } = useLingui();
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const currentPath = useLocation().pathname;
|
||||
const currentViewPath = useLocation().pathname + useLocation().search;
|
||||
@@ -161,6 +163,8 @@ export const CurrentWorkspaceMemberFavorites = ({
|
||||
modalId,
|
||||
);
|
||||
|
||||
const favoriteCount = folder.favorites.length;
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavigationDrawerItemsCollapsableContainer
|
||||
@@ -252,10 +256,18 @@ export const CurrentWorkspaceMemberFavorites = ({
|
||||
createPortal(
|
||||
<ConfirmationModal
|
||||
modalId={modalId}
|
||||
title={`Remove ${folder.favorites.length} ${folder.favorites.length > 1 ? 'favorites' : 'favorite'}?`}
|
||||
subtitle={`This action will delete this favorite folder ${folder.favorites.length > 1 ? `and all ${folder.favorites.length} favorites` : 'and the favorite'} inside. Do you want to continue?`}
|
||||
title={
|
||||
folder.favorites.length > 1
|
||||
? t`Remove ${favoriteCount} favorites?`
|
||||
: t`Remove ${favoriteCount} favorite?`
|
||||
}
|
||||
subtitle={
|
||||
folder.favorites.length > 1
|
||||
? t`This action will delete this favorite folder and all ${favoriteCount} favorites inside. Do you want to continue?`
|
||||
: t`This action will delete this favorite folder and the favorite inside. Do you want to continue?`
|
||||
}
|
||||
onConfirmClick={handleConfirmDelete}
|
||||
confirmButtonText="Delete Folder"
|
||||
confirmButtonText={t`Delete Folder`}
|
||||
/>,
|
||||
document.body,
|
||||
)}
|
||||
|
||||
+4
-2
@@ -2,6 +2,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconDotsVertical, IconPencil, IconTrash } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
@@ -19,6 +20,7 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
||||
onDelete,
|
||||
closeDropdown,
|
||||
}: FavoriteFolderNavigationDrawerItemDropdownProps) => {
|
||||
const { t } = useLingui();
|
||||
const handleRename = () => {
|
||||
closeDropdown();
|
||||
onRename();
|
||||
@@ -44,13 +46,13 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
||||
LeftIcon={IconPencil}
|
||||
onClick={handleRename}
|
||||
accent="default"
|
||||
text="Rename"
|
||||
text={t`Rename`}
|
||||
/>
|
||||
<MenuItem
|
||||
LeftIcon={IconTrash}
|
||||
onClick={handleDelete}
|
||||
accent="danger"
|
||||
text="Delete"
|
||||
text={t`Delete`}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { IconPlus } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
@@ -13,6 +14,7 @@ export const FavoriteFolderPickerFooter = ({
|
||||
}: {
|
||||
dropdownId: string;
|
||||
}) => {
|
||||
const { t } = useLingui();
|
||||
const [, setIsFavoriteFolderCreating] = useRecoilState(
|
||||
isFavoriteFolderCreatingState,
|
||||
);
|
||||
@@ -33,7 +35,7 @@ export const FavoriteFolderPickerFooter = ({
|
||||
setIsFavoriteFolderCreating(true);
|
||||
closeDropdown(dropdownId);
|
||||
}}
|
||||
text="Add folder"
|
||||
text={t`Add folder`}
|
||||
LeftIcon={() => <IconPlus size={theme.icon.size.md} />}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
+4
-2
@@ -4,6 +4,7 @@ import { type FavoriteFolder } from '@/favorites/types/FavoriteFolder';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { MenuItem, MenuItemMultiSelect } from 'twenty-ui/navigation';
|
||||
|
||||
const StyledItemsContainer = styled.div`
|
||||
@@ -26,6 +27,7 @@ export const FavoriteFolderPickerList = ({
|
||||
folders,
|
||||
toggleFolderSelection,
|
||||
}: FavoriteFolderPickerListProps) => {
|
||||
const { t } = useLingui();
|
||||
const [favoriteFoldersSearchFilter] = useRecoilComponentState(
|
||||
favoriteFolderSearchFilterComponentState,
|
||||
);
|
||||
@@ -51,7 +53,7 @@ export const FavoriteFolderPickerList = ({
|
||||
key={`menu-${NO_FOLDER_ID}`}
|
||||
onSelectChange={() => toggleFolderSelection(NO_FOLDER_ID)}
|
||||
selected={favoriteFolderPickerChecked.includes(NO_FOLDER_ID)}
|
||||
text="No folder"
|
||||
text={t`No folder`}
|
||||
className="no-folder-menu-item-multi-select"
|
||||
/>
|
||||
)}
|
||||
@@ -68,7 +70,7 @@ export const FavoriteFolderPickerList = ({
|
||||
className="folder-menu-item-multi-select"
|
||||
/>
|
||||
))
|
||||
: !showNoFolderOption && <MenuItem text="No folders found" />}
|
||||
: !showNoFolderOption && <MenuItem text={t`No folders found`} />}
|
||||
</StyledItemsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { InformationBannerComponentInstanceContext } from '@/information-banner/
|
||||
import { informationBannerIsOpenComponentState } from '@/information-banner/states/informationBannerIsOpenComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
Banner,
|
||||
type BannerVariant,
|
||||
@@ -83,7 +84,7 @@ export const InformationBanner = ({
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
onClick={onClose}
|
||||
ariaLabel="Close banner"
|
||||
ariaLabel={t`Close banner`}
|
||||
/>
|
||||
)}
|
||||
</Banner>
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
import { InformationBanner } from '@/information-banner/components/InformationBanner';
|
||||
import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecords';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconRefresh } from 'twenty-ui/display';
|
||||
|
||||
const StyledInformationBannerDeletedRecord = styled.div`
|
||||
@@ -28,8 +29,8 @@ export const InformationBannerDeletedRecord = ({
|
||||
<InformationBanner
|
||||
componentInstanceId="information-banner-deleted-record"
|
||||
variant="danger"
|
||||
message={`This record has been deleted`}
|
||||
buttonTitle="Restore"
|
||||
message={t`This record has been deleted`}
|
||||
buttonTitle={t`Restore`}
|
||||
buttonIcon={IconRefresh}
|
||||
buttonOnClick={() => restoreManyRecords({ idsToRestore: [recordId] })}
|
||||
/>
|
||||
|
||||
+5
-2
@@ -3,6 +3,7 @@ import { useAccountToReconnect } from '@/information-banner/hooks/useAccountToRe
|
||||
import { useDismissReconnectAccountBanner } from '@/information-banner/hooks/useDismissReconnectAccountBanner';
|
||||
import { InformationBannerKeys } from '@/information-banner/types/InformationBannerKeys';
|
||||
import { useTriggerProviderReconnect } from '@/settings/accounts/hooks/useTriggerProviderReconnect';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconRefresh } from 'twenty-ui/display';
|
||||
|
||||
const COMPONENT_INSTANCE_ID =
|
||||
@@ -26,11 +27,13 @@ export const InformationBannerReconnectAccountEmailAliases = () => {
|
||||
await dismissReconnectAccountBanner(accountToReconnect.id);
|
||||
};
|
||||
|
||||
const mailboxHandle = accountToReconnect.handle;
|
||||
|
||||
return (
|
||||
<InformationBanner
|
||||
componentInstanceId={COMPONENT_INSTANCE_ID}
|
||||
message={`Please reconnect your mailbox ${accountToReconnect.handle} to update your email aliases:`}
|
||||
buttonTitle="Reconnect"
|
||||
message={t`Please reconnect your mailbox ${mailboxHandle} to update your email aliases:`}
|
||||
buttonTitle={t`Reconnect`}
|
||||
buttonIcon={IconRefresh}
|
||||
buttonOnClick={() =>
|
||||
triggerProviderReconnect(
|
||||
|
||||
+5
-3
@@ -3,6 +3,7 @@ import { useAccountToReconnect } from '@/information-banner/hooks/useAccountToRe
|
||||
import { useDismissReconnectAccountBanner } from '@/information-banner/hooks/useDismissReconnectAccountBanner';
|
||||
import { InformationBannerKeys } from '@/information-banner/types/InformationBannerKeys';
|
||||
import { useTriggerProviderReconnect } from '@/settings/accounts/hooks/useTriggerProviderReconnect';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconRefresh } from 'twenty-ui/display';
|
||||
|
||||
const COMPONENT_INSTANCE_ID =
|
||||
@@ -26,12 +27,13 @@ export const InformationBannerReconnectAccountInsufficientPermissions = () => {
|
||||
await dismissReconnectAccountBanner(accountToReconnect.id);
|
||||
};
|
||||
|
||||
const mailboxHandle = accountToReconnect.handle;
|
||||
|
||||
return (
|
||||
<InformationBanner
|
||||
componentInstanceId={COMPONENT_INSTANCE_ID}
|
||||
message={`Sync lost with mailbox ${accountToReconnect.handle}. Please
|
||||
reconnect for updates:`}
|
||||
buttonTitle="Reconnect"
|
||||
message={t`Sync lost with mailbox ${mailboxHandle}. Please reconnect for updates:`}
|
||||
buttonTitle={t`Reconnect`}
|
||||
buttonIcon={IconRefresh}
|
||||
buttonOnClick={() =>
|
||||
triggerProviderReconnect(
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ import {
|
||||
StyledDialog,
|
||||
StyledHeading,
|
||||
} from './KeyboardShortcutMenuStyles';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { IconX } from 'twenty-ui/display';
|
||||
|
||||
@@ -18,7 +19,7 @@ export const KeyboardMenuDialog = ({
|
||||
return (
|
||||
<StyledDialog>
|
||||
<StyledHeading>
|
||||
Keyboard shortcuts
|
||||
{t`Keyboard shortcuts`}
|
||||
<IconButton variant="tertiary" Icon={IconX} onClick={onClose} />
|
||||
</StyledHeading>
|
||||
<StyledContainer>{children}</StyledContainer>
|
||||
|
||||
+3
-2
@@ -9,6 +9,7 @@ import {
|
||||
} from '../hooks/useKeyboardShortcutMenu';
|
||||
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { KeyboardMenuDialog } from './KeyboardShortcutMenuDialog';
|
||||
import { KeyboardMenuGroup } from './KeyboardShortcutMenuGroup';
|
||||
import { KeyboardMenuItem } from './KeyboardShortcutMenuItem';
|
||||
@@ -29,12 +30,12 @@ export const KeyboardShortcutMenuOpenContent = () => {
|
||||
return (
|
||||
<>
|
||||
<KeyboardMenuDialog onClose={toggleKeyboardShortcutMenu}>
|
||||
<KeyboardMenuGroup heading="Table">
|
||||
<KeyboardMenuGroup heading={t`Table`}>
|
||||
{KEYBOARD_SHORTCUTS_TABLE.map((TableShortcut, index) => (
|
||||
<KeyboardMenuItem shortcut={TableShortcut} key={index} />
|
||||
))}
|
||||
</KeyboardMenuGroup>
|
||||
<KeyboardMenuGroup heading="General">
|
||||
<KeyboardMenuGroup heading={t`General`}>
|
||||
{KEYBOARD_SHORTCUTS_GENERAL.map((GeneralShortcut) => (
|
||||
<KeyboardMenuItem shortcut={GeneralShortcut} />
|
||||
))}
|
||||
|
||||
+2
@@ -6,12 +6,14 @@ import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
|
||||
import { useEffect } from 'react';
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { KeyboardShortcutMenu } from '../KeyboardShortcutMenu';
|
||||
|
||||
const meta: Meta<typeof KeyboardShortcutMenu> = {
|
||||
title: 'Modules/KeyboardShortcutMenu/KeyboardShortcutMenu',
|
||||
component: KeyboardShortcutMenu,
|
||||
decorators: [
|
||||
I18nFrontDecorator,
|
||||
(Story) => {
|
||||
const { openKeyboardShortcutMenu } = useKeyboardShortcutMenu();
|
||||
useEffect(() => {
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import { Select } from '@/ui/input/components/Select';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
@@ -38,7 +39,7 @@ export const AdvancedFilterCommandMenuLogicalOperatorCell = ({
|
||||
return (
|
||||
<StyledContainer>
|
||||
{index === 0 ? (
|
||||
<StyledText>Where</StyledText>
|
||||
<StyledText>{t`Where`}</StyledText>
|
||||
) : index === 1 ? (
|
||||
readonly ? (
|
||||
<Select
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -41,7 +42,7 @@ export const AdvancedFilterCommandMenuRecordFilterOperandSelect = ({
|
||||
selectedOption={{
|
||||
label: filter?.operand
|
||||
? getOperandLabel(filter.operand)
|
||||
: 'Select operand',
|
||||
: t`Select operand`,
|
||||
value: null,
|
||||
}}
|
||||
isDisabled
|
||||
|
||||
+7
-6
@@ -17,6 +17,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { useContext } from 'react';
|
||||
import { RecordFilterGroupLogicalOperator } from 'twenty-shared/types';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { getFilterTypeFromFieldType, isDefined } from 'twenty-shared/utils';
|
||||
import { IconLibraryPlus, IconPlus } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
@@ -149,8 +150,8 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
||||
<ActionButton
|
||||
action={{
|
||||
Icon: IconPlus,
|
||||
label: 'Add rule',
|
||||
shortLabel: 'Add rule',
|
||||
label: t`Add rule`,
|
||||
shortLabel: t`Add rule`,
|
||||
key: 'add-rule',
|
||||
}}
|
||||
onClick={handleAddFilter}
|
||||
@@ -165,8 +166,8 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
||||
<ActionButton
|
||||
action={{
|
||||
Icon: IconPlus,
|
||||
label: 'Add filter rule',
|
||||
shortLabel: 'Add filter rule',
|
||||
label: t`Add filter rule`,
|
||||
shortLabel: t`Add filter rule`,
|
||||
key: 'add-filter-rule',
|
||||
}}
|
||||
/>
|
||||
@@ -176,13 +177,13 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconPlus}
|
||||
text="Add rule"
|
||||
text={t`Add rule`}
|
||||
onClick={handleAddFilter}
|
||||
/>
|
||||
{isFilterRuleGroupOptionVisible && (
|
||||
<MenuItem
|
||||
LeftIcon={IconLibraryPlus}
|
||||
text="Add rule group"
|
||||
text={t`Add rule group`}
|
||||
onClick={handleAddFilterGroup}
|
||||
/>
|
||||
)}
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
export const AdvancedFilterDropdownNumberInput = () => {
|
||||
const selectedOperandInDropdown = useRecoilComponentValue(
|
||||
@@ -32,7 +33,7 @@ export const AdvancedFilterDropdownNumberInput = () => {
|
||||
<TextInput
|
||||
value={objectFilterDropdownFilterValue}
|
||||
onChange={handleChange}
|
||||
placeholder="Enter value"
|
||||
placeholder={t`Enter value`}
|
||||
fullWidth
|
||||
type="number"
|
||||
/>
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
type AdvancedFilterDropdownTextInputProps = {
|
||||
recordFilter: RecordFilter;
|
||||
@@ -20,7 +21,7 @@ export const AdvancedFilterDropdownTextInput = ({
|
||||
<TextInput
|
||||
value={recordFilter.value}
|
||||
onChange={handleChange}
|
||||
placeholder="Enter value"
|
||||
placeholder={t`Enter value`}
|
||||
fullWidth
|
||||
/>
|
||||
);
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { AdvancedFilterLogicalOperatorDropdown } from '@/object-record/advanced-
|
||||
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
const StyledText = styled.div`
|
||||
@@ -31,7 +32,7 @@ export const AdvancedFilterLogicalOperatorCell = ({
|
||||
return (
|
||||
<StyledContainer>
|
||||
{index === 0 ? (
|
||||
<StyledText>Where</StyledText>
|
||||
<StyledText>{t`Where`}</StyledText>
|
||||
) : index === 1 ? (
|
||||
<AdvancedFilterLogicalOperatorDropdown
|
||||
recordFilterGroup={recordFilterGroup}
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
@@ -48,7 +49,7 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
<IconButton
|
||||
aria-label="Filter group rule options"
|
||||
aria-label={t`Filter group rule options`}
|
||||
variant="tertiary"
|
||||
Icon={IconDotsVertical}
|
||||
/>
|
||||
@@ -57,7 +58,7 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule group"
|
||||
text={t`Remove rule group`}
|
||||
onClick={handleRemove}
|
||||
LeftIcon={IconTrash}
|
||||
accent="danger"
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import { getRecordFilterOperands } from '@/object-record/record-filter/utils/get
|
||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@@ -43,7 +44,7 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
|
||||
selectedOption={{
|
||||
label: filter?.operand
|
||||
? getOperandLabel(filter.operand)
|
||||
: 'Select operand',
|
||||
: t`Select operand`,
|
||||
value: null,
|
||||
}}
|
||||
isDisabled
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type ViewFilterOperand } from 'twenty-shared/types';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
@@ -54,7 +55,7 @@ export const AdvancedFilterRecordFilterOperandSelectContent = ({
|
||||
selectedOption={{
|
||||
label: filter?.operand
|
||||
? getOperandLabel(filter.operand)
|
||||
: 'Select operand',
|
||||
: t`Select operand`,
|
||||
value: null,
|
||||
}}
|
||||
/>
|
||||
|
||||
+3
-2
@@ -11,6 +11,7 @@ import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconDotsVertical, IconTrash } from 'twenty-ui/display';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
@@ -68,7 +69,7 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
<IconButton
|
||||
aria-label="Record filter rule options"
|
||||
aria-label={t`Record filter rule options`}
|
||||
variant="tertiary"
|
||||
Icon={IconDotsVertical}
|
||||
/>
|
||||
@@ -77,7 +78,7 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Remove rule"
|
||||
text={t`Remove rule`}
|
||||
onClick={handleRemove}
|
||||
LeftIcon={IconTrash}
|
||||
accent="danger"
|
||||
|
||||
+4
-1
@@ -23,6 +23,7 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconChevronLeft, useIcons } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
@@ -112,6 +113,8 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
...subFieldNames.map((subFieldName) => subFieldName),
|
||||
];
|
||||
|
||||
const fieldLabel = fieldMetadataItemUsedInDropdown?.label;
|
||||
|
||||
return (
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||
<DropdownMenuHeader
|
||||
@@ -146,7 +149,7 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
handleSelectFilter(fieldMetadataItemUsedInDropdown);
|
||||
}}
|
||||
LeftIcon={getIcon(fieldMetadataItemUsedInDropdown.icon)}
|
||||
text={`Any ${fieldMetadataItemUsedInDropdown.label} field`}
|
||||
text={t`Any ${fieldLabel} field`}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { useGetRecordFilterDisplayValue } from '@/object-record/record-filter/hooks/useGetRecordFilterDisplayValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
// TODO: factorize this with https://github.com/twentyhq/core-team-issues/issues/752
|
||||
@@ -57,7 +58,7 @@ export const AdvancedFilterValueInputDropdownButtonClickableSelect = ({
|
||||
|
||||
const placeholderText = isDefined(fieldMetadataItem)
|
||||
? getAdvancedFilterInputPlaceholderText(fieldMetadataItem)
|
||||
: 'Enter filter';
|
||||
: t`Enter filter`;
|
||||
|
||||
const recordFilterDisplayValue = getRecordFilterDisplayValue(recordFilter);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { recordIndexOpenRecordInState } from '@/object-record/record-index/state
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { canOpenObjectInSidePanel } from '@/object-record/utils/canOpenObjectInSidePanel';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type MouseEvent } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -81,6 +82,7 @@ export const RecordChip = ({
|
||||
return (
|
||||
<Chip
|
||||
label={recordChipData.name}
|
||||
emptyLabel={t`Untitled`}
|
||||
size={size}
|
||||
maxWidth={maxWidth}
|
||||
className={className}
|
||||
@@ -104,6 +106,7 @@ export const RecordChip = ({
|
||||
size={size}
|
||||
maxWidth={maxWidth}
|
||||
label={recordChipData.name}
|
||||
emptyLabel={t`Untitled`}
|
||||
isLabelHidden={isLabelHidden}
|
||||
leftComponent={
|
||||
isIconHidden ? null : (
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
|
||||
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
|
||||
@@ -45,7 +46,7 @@ export const ObjectFilterDropdownBooleanSelect = () => {
|
||||
const handleOptionSelect = (newValue: boolean) => {
|
||||
applyObjectFilterDropdownFilterValue(
|
||||
newValue.toString(),
|
||||
newValue ? 'True' : 'False',
|
||||
newValue ? t`True` : t`False`,
|
||||
);
|
||||
|
||||
closeDropdown();
|
||||
|
||||
+3
-1
@@ -8,6 +8,7 @@ import { type SelectableItem } from '@/object-record/select/types/SelectableItem
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -64,9 +65,10 @@ export const ObjectFilterDropdownSourceSelect = ({
|
||||
.filter((option) => newSelectedItemIds.includes(option.id))
|
||||
.map((option) => option.name);
|
||||
|
||||
const selectedCount = selectedItemNames.length;
|
||||
const filterDisplayValue =
|
||||
selectedItemNames.length > MAX_ITEMS_TO_DISPLAY
|
||||
? `${selectedItemNames.length} source types`
|
||||
? t`${selectedCount} source types`
|
||||
: selectedItemNames.join(', ');
|
||||
|
||||
const newFilterValue =
|
||||
|
||||
+4
-2
@@ -50,7 +50,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
? {
|
||||
...currentView,
|
||||
key: ViewKey.Custom,
|
||||
name: currentView.name || 'Custom View',
|
||||
name: currentView.name || t`Custom View`,
|
||||
}
|
||||
: null;
|
||||
|
||||
@@ -76,6 +76,8 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
const visibleFieldsCount = visibleBoardFields.length;
|
||||
|
||||
const { deleteViewFromCurrentState } = useDeleteViewFromCurrentState();
|
||||
const setViewPickerReferenceViewId = useSetRecoilComponentState(
|
||||
viewPickerReferenceViewIdComponentState,
|
||||
@@ -211,7 +213,7 @@ export const ObjectOptionsDropdownCustomView = ({
|
||||
onClick={() => onContentChange('fields')}
|
||||
LeftIcon={IconListDetails}
|
||||
text={t`Fields`}
|
||||
contextualText={`${visibleBoardFields.length} shown`}
|
||||
contextualText={t`${visibleFieldsCount} shown`}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
|
||||
+3
-1
@@ -35,6 +35,8 @@ export const ObjectOptionsDropdownDefaultView = () => {
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
const visibleFieldsCount = visibleBoardFields.length;
|
||||
|
||||
const selectableItemIdArray = [
|
||||
'Fields',
|
||||
'Copy link to view',
|
||||
@@ -86,7 +88,7 @@ export const ObjectOptionsDropdownDefaultView = () => {
|
||||
onClick={() => onContentChange('fields')}
|
||||
LeftIcon={IconListDetails}
|
||||
text={t`Fields`}
|
||||
contextualText={`${visibleBoardFields.length} shown`}
|
||||
contextualText={t`${visibleFieldsCount} shown`}
|
||||
contextualTextPosition="right"
|
||||
hasSubMenu
|
||||
/>
|
||||
|
||||
+2
-2
@@ -75,10 +75,10 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
Hidden {recordGroupFieldMetadata?.label}
|
||||
{t`Hidden`} {recordGroupFieldMetadata?.label}
|
||||
</DropdownMenuHeader>
|
||||
<RecordGroupsVisibilityDropdownSection
|
||||
title={`Hidden ${recordGroupFieldMetadata?.label}`}
|
||||
title={`${t`Hidden`} ${recordGroupFieldMetadata?.label}`}
|
||||
recordGroupIds={hiddenRecordGroupIds}
|
||||
onVisibilityChange={handleRecordGroupVisibilityChange}
|
||||
isDraggable={false}
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
IconChevronLeft,
|
||||
IconHandMove,
|
||||
@@ -67,7 +68,7 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
Sort
|
||||
{t`Sort`}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
|
||||
+2
-2
@@ -106,7 +106,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
Group
|
||||
{t`Group`}
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
@@ -187,7 +187,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
||||
<MenuItemNavigate
|
||||
onClick={() => onContentChange('hiddenRecordGroups')}
|
||||
LeftIcon={IconEyeOff}
|
||||
text={`Hidden ${recordGroupFieldMetadata?.label ?? ''}`}
|
||||
text={`${t`Hidden`} ${recordGroupFieldMetadata?.label ?? ''}`}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SelectableList>
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useC
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { IconPlus } from 'twenty-ui/display';
|
||||
|
||||
@@ -66,7 +67,7 @@ export const RecordBoardColumnNewRecordButton = () => {
|
||||
}}
|
||||
>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
New
|
||||
{t`New`}
|
||||
</StyledNewButton>
|
||||
);
|
||||
};
|
||||
|
||||
+3
-3
@@ -91,15 +91,15 @@ export const RecordCalendarTopBar = () => {
|
||||
selectSizeVariant="small"
|
||||
options={[
|
||||
{
|
||||
label: 'Month',
|
||||
label: t`Month`,
|
||||
value: ViewCalendarLayout.MONTH,
|
||||
},
|
||||
{
|
||||
label: 'Week',
|
||||
label: t`Week`,
|
||||
value: ViewCalendarLayout.WEEK,
|
||||
},
|
||||
{
|
||||
label: 'Timeline',
|
||||
label: t`Timeline`,
|
||||
value: ViewCalendarLayout.DAY,
|
||||
},
|
||||
]}
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import {
|
||||
combineFilters,
|
||||
@@ -56,7 +57,7 @@ export const useRecordCalendarQueryDateRangeFilter = (selectedDate: Date) => {
|
||||
value: `${firstDayOfFirstWeek.toISOString()}`,
|
||||
operand: RecordFilterOperand.IS_AFTER,
|
||||
type: 'DATE',
|
||||
label: 'After',
|
||||
label: t`After`,
|
||||
displayValue: `${firstDayOfFirstWeek.toISOString()}`,
|
||||
};
|
||||
|
||||
@@ -66,7 +67,7 @@ export const useRecordCalendarQueryDateRangeFilter = (selectedDate: Date) => {
|
||||
value: `${lastDayOfLastWeek.toISOString()}`,
|
||||
operand: RecordFilterOperand.IS_BEFORE,
|
||||
type: 'DATE',
|
||||
label: 'Before',
|
||||
label: t`Before`,
|
||||
displayValue: `${lastDayOfLastWeek.toISOString()}`,
|
||||
};
|
||||
|
||||
|
||||
+8
-6
@@ -35,6 +35,8 @@ import {
|
||||
computeMorphRelationFieldName,
|
||||
CustomError,
|
||||
} from 'twenty-shared/utils';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconDotsVertical,
|
||||
@@ -263,14 +265,14 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconUnlink}
|
||||
text="Detach"
|
||||
text={t`Detach`}
|
||||
onClick={handleDetach}
|
||||
/>
|
||||
{!isAccountOwnerRelation &&
|
||||
relationObjectPermissions.canSoftDeleteObjectRecords && (
|
||||
<MenuItem
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
text={t`Delete`}
|
||||
accent="danger"
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
@@ -295,17 +297,17 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
{createPortal(
|
||||
<ConfirmationModal
|
||||
modalId={getDeleteRelationModalId(relationRecord.id)}
|
||||
title={`Delete Related ${relationObjectTypeName}`}
|
||||
title={t`Delete Related ${relationObjectTypeName}`}
|
||||
subtitle={
|
||||
<>
|
||||
<Trans>
|
||||
Are you sure you want to delete this related{' '}
|
||||
{relationObjectMetadataNameSingular}?
|
||||
<br />
|
||||
This action will break all its relationships with other objects.
|
||||
</>
|
||||
</Trans>
|
||||
}
|
||||
onConfirmClick={handleConfirmDelete}
|
||||
confirmButtonText={`Delete ${relationObjectTypeName}`}
|
||||
confirmButtonText={t`Delete ${relationObjectTypeName}`}
|
||||
/>,
|
||||
document.body,
|
||||
)}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ export const LightCopyIconButton = ({ copyText }: LightCopyIconButtonProps) => {
|
||||
onClick={() => {
|
||||
copyToClipboard(copyText, t`Text copied to clipboard`);
|
||||
}}
|
||||
aria-label="Copy to Clipboard"
|
||||
aria-label={t`Copy to Clipboard`}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
);
|
||||
|
||||
+12
-11
@@ -6,6 +6,7 @@ import { type VariablePickerComponent } from '@/object-record/record-field/ui/fo
|
||||
import { type FieldAddressDraftValue } from '@/object-record/record-field/ui/types/FieldInputDraftValue';
|
||||
import { type FieldAddressValue } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
type FormAddressFieldInputProps = {
|
||||
label?: string;
|
||||
@@ -43,47 +44,47 @@ export const FormAddressFieldInput = ({
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
<FormNestedFieldInputContainer>
|
||||
<FormTextFieldInput
|
||||
label="Address 1"
|
||||
label={t`Address 1`}
|
||||
defaultValue={defaultValue?.addressStreet1 ?? ''}
|
||||
onChange={handleChange('addressStreet1')}
|
||||
readonly={readonly}
|
||||
VariablePicker={VariablePicker}
|
||||
placeholder="Street address"
|
||||
placeholder={t`Street address`}
|
||||
/>
|
||||
<FormTextFieldInput
|
||||
label="Address 2"
|
||||
label={t`Address 2`}
|
||||
defaultValue={defaultValue?.addressStreet2 ?? ''}
|
||||
onChange={handleChange('addressStreet2')}
|
||||
readonly={readonly}
|
||||
VariablePicker={VariablePicker}
|
||||
placeholder="Street address 2"
|
||||
placeholder={t`Street address 2`}
|
||||
/>
|
||||
<FormTextFieldInput
|
||||
label="City"
|
||||
label={t`City`}
|
||||
defaultValue={defaultValue?.addressCity ?? ''}
|
||||
onChange={handleChange('addressCity')}
|
||||
readonly={readonly}
|
||||
VariablePicker={VariablePicker}
|
||||
placeholder="City"
|
||||
placeholder={t`City`}
|
||||
/>
|
||||
<FormTextFieldInput
|
||||
label="State"
|
||||
label={t`State`}
|
||||
defaultValue={defaultValue?.addressState ?? ''}
|
||||
onChange={handleChange('addressState')}
|
||||
readonly={readonly}
|
||||
VariablePicker={VariablePicker}
|
||||
placeholder="State"
|
||||
placeholder={t`State`}
|
||||
/>
|
||||
<FormTextFieldInput
|
||||
label="Post Code"
|
||||
label={t`Post Code`}
|
||||
defaultValue={defaultValue?.addressPostcode ?? ''}
|
||||
onChange={handleChange('addressPostcode')}
|
||||
readonly={readonly}
|
||||
VariablePicker={VariablePicker}
|
||||
placeholder="Post Code"
|
||||
placeholder={t`Post Code`}
|
||||
/>
|
||||
<FormCountrySelectInput
|
||||
label="Country"
|
||||
label={t`Country`}
|
||||
selectedCountryName={defaultValue?.addressCountry ?? ''}
|
||||
onChange={handleChange('addressCountry')}
|
||||
readonly={readonly}
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ import { useMemo } from 'react';
|
||||
import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput';
|
||||
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
|
||||
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import type { CountryCode } from 'libphonenumber-js';
|
||||
import { IconCircleOff, type IconComponentProps } from 'twenty-ui/display';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
@@ -35,7 +36,7 @@ export const FormCountryCodeSelectInput = ({
|
||||
);
|
||||
return [
|
||||
{
|
||||
label: 'No country',
|
||||
label: t`No country`,
|
||||
value: '',
|
||||
Icon: IconCircleOff,
|
||||
},
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput';
|
||||
@@ -32,7 +33,7 @@ export const FormCountrySelectInput = ({
|
||||
);
|
||||
return [
|
||||
{
|
||||
label: 'No country',
|
||||
label: t`No country`,
|
||||
value: '',
|
||||
Icon: IconCircleOff,
|
||||
},
|
||||
|
||||
+5
-4
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
|
||||
import { FormNestedFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormNestedFieldInputContainer';
|
||||
import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types/components/FormNumberFieldInput';
|
||||
@@ -56,7 +57,7 @@ export const FormCurrencyFieldInput = ({
|
||||
const currencies = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
label: 'No currency',
|
||||
label: t`No currency`,
|
||||
value: '',
|
||||
Icon: IconCircleOff,
|
||||
},
|
||||
@@ -85,7 +86,7 @@ export const FormCurrencyFieldInput = ({
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
<FormNestedFieldInputContainer>
|
||||
<FormSelectFieldInput
|
||||
label="Currency Code"
|
||||
label={t`Currency Code`}
|
||||
defaultValue={defaultValue?.currencyCode ?? ''}
|
||||
onChange={handleCurrencyCodeChange}
|
||||
options={currencies}
|
||||
@@ -93,11 +94,11 @@ export const FormCurrencyFieldInput = ({
|
||||
readonly={readonly}
|
||||
/>
|
||||
<FormNumberFieldInput
|
||||
label="Amount"
|
||||
label={t`Amount`}
|
||||
defaultValue={formatMicrosToDisplayAmount(defaultValue?.amountMicros)}
|
||||
onChange={handleAmountMicrosChange}
|
||||
VariablePicker={VariablePicker}
|
||||
placeholder="Set 3.21 for $3.21"
|
||||
placeholder={t`Set 3.21 for $3.21`}
|
||||
readonly={readonly}
|
||||
/>
|
||||
</FormNestedFieldInputContainer>
|
||||
|
||||
+3
-2
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer';
|
||||
import { FormNestedFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormNestedFieldInputContainer';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
|
||||
@@ -41,7 +42,7 @@ export const FormFullNameFieldInput = ({
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
<FormNestedFieldInputContainer>
|
||||
<FormTextFieldInput
|
||||
label="First Name"
|
||||
label={t`First Name`}
|
||||
defaultValue={defaultValue?.firstName}
|
||||
onChange={handleFirstNameChange}
|
||||
placeholder={
|
||||
@@ -51,7 +52,7 @@ export const FormFullNameFieldInput = ({
|
||||
VariablePicker={VariablePicker}
|
||||
/>
|
||||
<FormTextFieldInput
|
||||
label="Last Name"
|
||||
label={t`Last Name`}
|
||||
defaultValue={defaultValue?.lastName}
|
||||
onChange={handleLastNameChange}
|
||||
placeholder={
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user