Files
twenty/packages/twenty-front/src/modules/settings/components/SettingsDatabaseEventsForm.tsx
T
Charles Bochet ef499b6d47 Re-enable disabled lint rules and right-size CI runners (#18461)
## 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
2026-03-06 13:33:02 +00:00

104 lines
3.2 KiB
TypeScript

import { WebhookEntitySelect } from '@/settings/developers/components/WebhookEntitySelect';
import { Select } from '@/ui/input/components/Select';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { IconBox, IconNorthStar, IconPlus, IconTrash } from 'twenty-ui/display';
import { IconButton, type SelectOption } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const OBJECT_DROPDOWN_WIDTH = 240;
const ACTION_DROPDOWN_WIDTH = 240;
const OBJECT_MOBILE_WIDTH = 150;
const ACTION_MOBILE_WIDTH = 140;
const StyledFilterRow = styled.div<{ isMobile: boolean }>`
align-items: center;
display: grid;
gap: ${themeCssVariables.spacing[2]};
grid-template-columns: ${({ isMobile }) =>
isMobile
? `${OBJECT_MOBILE_WIDTH}px ${ACTION_MOBILE_WIDTH}px auto`
: `${OBJECT_DROPDOWN_WIDTH}px ${ACTION_DROPDOWN_WIDTH}px auto`};
margin-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledPlaceholder = styled.div`
height: ${themeCssVariables.spacing[8]};
width: ${themeCssVariables.spacing[8]};
`;
export const SettingsDatabaseEventsForm = ({
events,
updateOperation,
removeOperation,
disabled = false,
}: {
events: { object: string | null; action: string; updatedFields?: string[] }[];
updateOperation?: (
index: number,
field: 'object' | 'action',
value: string | null,
) => void;
removeOperation?: (index: number) => void;
disabled?: boolean;
}) => {
const isMobile = useIsMobile();
const getActionOptions = (
updatedFields?: string[],
): SelectOption<string>[] => {
const hasSpecificFields =
isDefined(updatedFields) && updatedFields.length > 0;
return [
{ label: t`All`, value: '*', Icon: IconNorthStar },
{ label: t`Created`, value: 'created', Icon: IconPlus },
{
label: hasSpecificFields ? t`Updated (on specific fields)` : t`Updated`,
value: 'updated',
Icon: IconBox,
},
{ label: t`Deleted`, value: 'deleted', Icon: IconTrash },
];
};
return (
<>
{events.map((operation, index) => (
<StyledFilterRow key={index} isMobile={isMobile}>
<WebhookEntitySelect
dropdownId={`object-webhook-type-select-${index}`}
value={operation.object}
onChange={(newValue) =>
updateOperation?.(index, 'object', newValue)
}
disabled={disabled}
/>
<Select
dropdownId={`operation-webhook-type-select-${index}`}
value={operation.action}
options={getActionOptions(operation.updatedFields)}
onChange={(newValue) =>
updateOperation?.(index, 'action', newValue)
}
fullWidth
disabled={disabled}
/>
{isDefined(operation.object) && !disabled ? (
<IconButton
Icon={IconTrash}
variant="tertiary"
size="medium"
onClick={() => removeOperation?.(index)}
/>
) : (
<StyledPlaceholder />
)}
</StyledFilterRow>
))}
</>
);
};