f7421c5fc0
Adds a comprehensive queue management interface to the admin panel for viewing and managing background jobs. **Features:** - Queue detail pages showing paginated job lists (50 per page) - Filter jobs by state: completed, failed, active, waiting, delayed, paused - Checkbox selection with bulk actions (delete jobs, retry failed jobs) - Per-job dropdown menu for individual retry/delete - Expandable rows showing error messages, stack traces, and job data - Relative timestamps with hover tooltips - Display attempt counts on failed jobs - Dynamic retention policy info from backend **Changes:** - Backend: New AdminPanelQueueService with GraphQL endpoints for job listing, retry, and delete - Frontend: Queue detail page with QueueJobsTable component - Updated retention policy: completed jobs kept 4 hours, failed jobs kept 7 days (max 1000 each) - Added JobState enum for type safety <img width="634" height="696" alt="Screenshot_2025-10-20_at_11 45 25" src="https://github.com/user-attachments/assets/c67bcd27-26cf-47f5-9575-3cd5684d006b" /> <img width="484" height="680" alt="Screenshot_2025-10-20_at_11 45 14" src="https://github.com/user-attachments/assets/68725cc6-b3ec-4098-99ca-f9a717d6f8f1" /> <img width="490" height="643" alt="Screenshot_2025-10-20_at_11 45 05" src="https://github.com/user-attachments/assets/b68a5809-33ff-4452-b48b-741aff7f1dd6" /> <img width="685" height="662" alt="Screenshot 2025-10-20 at 13 15 01" src="https://github.com/user-attachments/assets/eeb5207b-de5c-4b18-bdde-392892053dab" />
52 lines
1.6 KiB
TypeScript
52 lines
1.6 KiB
TypeScript
import { ForbiddenFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/ForbiddenFieldDisplay';
|
|
import { type WidgetAccessDenialInfo } from '@/page-layout/widgets/types/WidgetAccessDenialInfo';
|
|
import { plural, t } from '@lingui/core/macro';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
import { AppTooltip } from 'twenty-ui/display';
|
|
|
|
type PageLayoutWidgetForbiddenDisplayProps = {
|
|
widgetId: string;
|
|
restriction: WidgetAccessDenialInfo;
|
|
};
|
|
|
|
export const PageLayoutWidgetForbiddenDisplay = ({
|
|
widgetId,
|
|
restriction,
|
|
}: PageLayoutWidgetForbiddenDisplayProps) => {
|
|
const tooltipId = `widget-forbidden-tooltip-${widgetId}`;
|
|
|
|
const getTooltipContent = () => {
|
|
if (restriction.type === 'object' && isDefined(restriction.objectName)) {
|
|
const objectName = restriction.objectName;
|
|
return t`You do not have permission to access the ${objectName} object`;
|
|
}
|
|
|
|
if (
|
|
restriction.type === 'field' &&
|
|
isDefined(restriction.fieldNames) &&
|
|
restriction.fieldNames.length > 0
|
|
) {
|
|
const fieldsList = restriction.fieldNames.join(', ');
|
|
return plural(restriction.fieldNames.length, {
|
|
one: `You do not have permission to access the ${fieldsList} field`,
|
|
other: `You do not have permission to access the ${fieldsList} fields`,
|
|
});
|
|
}
|
|
|
|
return t`You do not have permission to view this widget`;
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div id={tooltipId}>
|
|
<ForbiddenFieldDisplay />
|
|
</div>
|
|
<AppTooltip
|
|
anchorSelect={`#${tooltipId}`}
|
|
content={getTooltipContent()}
|
|
place="top"
|
|
/>
|
|
</>
|
|
);
|
|
};
|