ef499b6d47
## Summary - Re-enable one lint rule that was temporarily disabled during the ESLint-to-Oxlint migration: - **`twenty/sort-css-properties-alphabetically`** in twenty-front — 578 violations auto-fixed across 390 files - Document why **`typescript/consistent-type-imports`** cannot be auto-fixed in twenty-server: NestJS relies on `emitDecoratorMetadata` for DI, so converting constructor parameter imports to `import type` erases them at compile time and breaks dependency injection at runtime - Right-size CI runners, reducing 8-core usage from 18 jobs to 3: | Change | Jobs | Rationale | |--------|------|-----------| | **Keep 8-core** | `ci-merge-queue/e2e-test`, `ci-front/front-sb-build`, `ci-front/front-build` | Heavy builds needing max CPU + memory (10GB NODE_OPTIONS, full Storybook webpack bundling) | | **8-core → 4-core** | `ci-server` (build, lint-typecheck, validation, test, integration-test), `ci-front/front-sb-test`, `ci-zapier/server-setup`, `ci-sdk/sdk-e2e-test` | Already sharded into 10-12 parallel instances, I/O-bound (DB/Redis), or moderate single builds | | **8-core → 2-core** | `ci-emails/emails-test` | Trivially lightweight (build + curl health check) | | **Removed** | `ci-front/front-chromatic-deployment` | Dead code — permanently disabled with `if: false` | - Fix merge queue CI issues: - **Concurrency**: Use `merge_group.base_ref` instead of unique merge group ref so new queue entries cancel previous runs - **Required status checks**: Add `merge_group` trigger to all 6 required CI workflows (front, server, shared, website, docker-compose, sdk) with `changed-files-check` auto-skipped for merge_group events — status check jobs auto-pass without re-running full CI - **Build caching**: Add Nx build cache restore/save to E2E test job with fallback to `main` branch cache for faster frontend and server builds ## Test plan - [ ] CI passes on this PR (verifies lint rule auto-fix works) - [ ] Verify 4-core runner jobs complete within their 30-minute timeouts - [ ] Verify merge queue status checks auto-pass (ci-front-status-check, ci-server-status-check, etc.) - [ ] Verify merge queue E2E concurrency cancels previous runs when a new PR enters the queue
167 lines
5.4 KiB
TypeScript
167 lines
5.4 KiB
TypeScript
import { styled } from '@linaria/react';
|
|
import { t } from '@lingui/core/macro';
|
|
|
|
import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell';
|
|
import { getActivitySummary } from '@/activities/utils/getActivitySummary';
|
|
import { beautifyExactDate, hasDatePassed } from '~/utils/date-utils';
|
|
|
|
import { ActivityRow } from '@/activities/components/ActivityRow';
|
|
import { useActivityTargetsComponentInstanceId } from '@/activities/inline-cell/hooks/useActivityTargetsComponentInstanceId';
|
|
import { type Task } from '@/activities/types/Task';
|
|
import { useOpenRecordInSidePanel } from '@/side-panel/hooks/useOpenRecordInSidePanel';
|
|
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
|
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
|
import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext';
|
|
import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider';
|
|
import { useContext } from 'react';
|
|
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
|
|
import { Checkbox, CheckboxShape } from 'twenty-ui/input';
|
|
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
|
import { useCompleteTask } from '@/activities/tasks/hooks/useCompleteTask';
|
|
|
|
const StyledTaskBody = styled.div`
|
|
color: ${themeCssVariables.font.color.tertiary};
|
|
display: flex;
|
|
max-width: calc(80% - ${themeCssVariables.spacing[2]});
|
|
overflow: hidden;
|
|
padding-bottom: 1px;
|
|
text-overflow: ellipsis;
|
|
`;
|
|
|
|
const StyledTaskTitle = styled.div<{
|
|
completed: boolean;
|
|
}>`
|
|
align-items: center;
|
|
color: ${themeCssVariables.font.color.primary};
|
|
font-weight: ${themeCssVariables.font.weight.medium};
|
|
overflow: hidden;
|
|
padding: 0 ${themeCssVariables.spacing[2]};
|
|
padding-bottom: 1px;
|
|
text-decoration: ${({ completed }) => (completed ? 'line-through' : 'none')};
|
|
text-overflow: ellipsis;
|
|
|
|
white-space: nowrap;
|
|
`;
|
|
|
|
const StyledDueDate = styled.div<{
|
|
isPast: boolean;
|
|
}>`
|
|
align-items: center;
|
|
color: ${({ isPast }) =>
|
|
isPast
|
|
? themeCssVariables.font.color.danger
|
|
: themeCssVariables.font.color.secondary};
|
|
display: flex;
|
|
gap: ${themeCssVariables.spacing[1]};
|
|
padding-left: ${themeCssVariables.spacing[1]};
|
|
white-space: nowrap;
|
|
`;
|
|
|
|
const StyledRightSideContainer = styled.div`
|
|
align-items: center;
|
|
display: inline-flex;
|
|
max-width: 50%;
|
|
`;
|
|
|
|
const StyledActivityTargetsContainer = styled.div`
|
|
overflow: clip;
|
|
width: 100%;
|
|
`;
|
|
|
|
const StyledPlaceholder = styled.div`
|
|
color: ${themeCssVariables.font.color.light};
|
|
`;
|
|
|
|
const StyledLeftSideContainer = styled.div`
|
|
align-items: center;
|
|
display: inline-flex;
|
|
display: flex;
|
|
flex: 1;
|
|
overflow: hidden;
|
|
`;
|
|
|
|
const StyledCheckboxContainer = styled.div`
|
|
display: flex;
|
|
`;
|
|
|
|
export const TaskRow = ({ task }: { task: Task }) => {
|
|
const { theme } = useContext(ThemeContext);
|
|
const { openRecordInSidePanel } = useOpenRecordInSidePanel();
|
|
|
|
const body = getActivitySummary(task?.bodyV2?.blocknote ?? null);
|
|
|
|
const { completeTask } = useCompleteTask(task);
|
|
|
|
const baseComponentInstanceId = `task-row-targets-${task.id}`;
|
|
const componentInstanceId = useActivityTargetsComponentInstanceId(
|
|
baseComponentInstanceId,
|
|
);
|
|
|
|
return (
|
|
<ActivityRow
|
|
onClick={() => {
|
|
openRecordInSidePanel({
|
|
recordId: task.id,
|
|
objectNameSingular: CoreObjectNameSingular.Task,
|
|
});
|
|
}}
|
|
>
|
|
<StyledLeftSideContainer>
|
|
<StyledCheckboxContainer
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
}}
|
|
>
|
|
<Checkbox
|
|
checked={task.status === 'DONE'}
|
|
shape={CheckboxShape.Rounded}
|
|
onCheckedChange={completeTask}
|
|
/>
|
|
</StyledCheckboxContainer>
|
|
<StyledTaskTitle completed={task.status === 'DONE'}>
|
|
{task.title || <StyledPlaceholder>{t`Task title`}</StyledPlaceholder>}
|
|
</StyledTaskTitle>
|
|
<StyledTaskBody>
|
|
<OverflowingTextWithTooltip text={body} />
|
|
</StyledTaskBody>
|
|
</StyledLeftSideContainer>
|
|
<StyledRightSideContainer>
|
|
{task.dueAt && (
|
|
<StyledDueDate
|
|
isPast={hasDatePassed(task.dueAt) && task.status === 'TODO'}
|
|
>
|
|
<IconCalendar size={theme.icon.size.md} />
|
|
{beautifyExactDate(task.dueAt)}
|
|
</StyledDueDate>
|
|
)}
|
|
{
|
|
<StyledActivityTargetsContainer>
|
|
<FieldContextProvider
|
|
objectNameSingular={CoreObjectNameSingular.Task}
|
|
objectRecordId={task.id}
|
|
fieldMetadataName="taskTargets"
|
|
fieldPosition={0}
|
|
>
|
|
<RecordFieldsScopeContextProvider
|
|
value={{
|
|
scopeInstanceId: task.id,
|
|
}}
|
|
>
|
|
<StopPropagationContainer>
|
|
<ActivityTargetsInlineCell
|
|
activityObjectNameSingular={CoreObjectNameSingular.Task}
|
|
activityRecordId={task.id}
|
|
showLabel={false}
|
|
maxWidth={200}
|
|
componentInstanceId={componentInstanceId}
|
|
/>
|
|
</StopPropagationContainer>
|
|
</RecordFieldsScopeContextProvider>
|
|
</FieldContextProvider>
|
|
</StyledActivityTargetsContainer>
|
|
}
|
|
</StyledRightSideContainer>
|
|
</ActivityRow>
|
|
);
|
|
};
|