Files
twenty/packages/twenty-front/src/modules/page-layout/components/PageLayoutGridOverlay.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

111 lines
4.1 KiB
TypeScript

import { type PageLayoutBreakpoint } from '@/page-layout/constants/PageLayoutBreakpoints';
import { PAGE_LAYOUT_GRID_OVERLAY_Z_INDEX } from '@/page-layout/constants/PageLayoutGridOverlayZIndex';
import { useCreateWidgetFromClick } from '@/page-layout/hooks/useCreateWidgetFromClick';
import { pageLayoutCurrentBreakpointComponentState } from '@/page-layout/states/pageLayoutCurrentBreakpointComponentState';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState';
import { calculateGridCellPosition } from '@/page-layout/utils/calculateGridCellPosition';
import { calculateTotalGridRows } from '@/page-layout/utils/calculateTotalGridRows';
import { generateCellId } from '@/page-layout/utils/generateCellId';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { styled } from '@linaria/react';
import { useMemo } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledGridOverlay = styled.div<{
isDragSelecting?: boolean;
breakpoint: PageLayoutBreakpoint;
}>`
bottom: ${themeCssVariables.spacing[2]};
display: grid;
gap: ${themeCssVariables.spacing[2]};
grid-auto-rows: 55px;
grid-template-columns: ${({ breakpoint }) =>
breakpoint === 'mobile' ? '1fr' : 'repeat(12, 1fr)'};
left: ${themeCssVariables.spacing[2]};
pointer-events: ${({ isDragSelecting }) =>
isDragSelecting ? 'auto' : 'none'};
position: absolute;
right: ${themeCssVariables.spacing[2]};
top: ${themeCssVariables.spacing[2]};
z-index: ${PAGE_LAYOUT_GRID_OVERLAY_Z_INDEX};
`;
const StyledGridCell = styled.div<{ isSelected?: boolean }>`
background: ${({ isSelected }) =>
isSelected ? themeCssVariables.color.blue3 : 'transparent'};
border: 1px solid
${({ isSelected }) =>
isSelected
? themeCssVariables.color.blue7
: themeCssVariables.border.color.light};
border-radius: ${themeCssVariables.border.radius.md};
cursor: pointer;
transition: background-color 0.3s ease;
&:hover {
background: ${themeCssVariables.background.transparent.lighter};
border-color: ${themeCssVariables.border.color.medium};
}
`;
export const PageLayoutGridOverlay = () => {
const pageLayoutCurrentBreakpoint = useAtomComponentStateValue(
pageLayoutCurrentBreakpointComponentState,
);
const pageLayoutSelectedCells = useAtomComponentStateValue(
pageLayoutSelectedCellsComponentState,
);
const pageLayoutCurrentLayouts = useAtomComponentStateValue(
pageLayoutCurrentLayoutsComponentState,
);
const activeTabId = useAtomComponentStateValue(activeTabIdComponentState);
const { createWidgetFromClick } = useCreateWidgetFromClick();
const numberOfRows = useMemo(() => {
const currentTabLayouts = pageLayoutCurrentLayouts[activeTabId ?? ''] ?? {
desktop: [],
mobile: [],
};
return calculateTotalGridRows(currentTabLayouts);
}, [pageLayoutCurrentLayouts, activeTabId]);
const isPageLayoutCurrentBreakpointMobile =
pageLayoutCurrentBreakpoint === 'mobile';
const numberOfColumns = pageLayoutCurrentBreakpoint === 'mobile' ? 1 : 12;
return (
<StyledGridOverlay
isDragSelecting={!isPageLayoutCurrentBreakpointMobile}
breakpoint={pageLayoutCurrentBreakpoint}
>
{Array.from(
{
length: numberOfColumns * numberOfRows,
},
(_, i) => {
const { column, row } = calculateGridCellPosition({
index: i,
numberOfColumns,
});
const cellId = generateCellId(column, row);
return (
<StyledGridCell
key={i}
data-selectable-id={cellId}
isSelected={pageLayoutSelectedCells.has(cellId)}
onClick={() => createWidgetFromClick(cellId)}
/>
);
},
)}
</StyledGridOverlay>
);
};