diff --git a/frontend/src/v5/ui/controls/chip/chip.types.tsx b/frontend/src/v5/ui/controls/chip/chip.types.tsx
index c81bab7480b..c44a9fe9b3f 100644
--- a/frontend/src/v5/ui/controls/chip/chip.types.tsx
+++ b/frontend/src/v5/ui/controls/chip/chip.types.tsx
@@ -176,7 +176,7 @@ export const STATUS_TYPE_MAP = {
},
};
-enum TicketStatusDefaultValues {
+export enum TicketStatusDefaultValues {
OPEN = 'Open',
IN_PROGRESS = 'In Progress',
FOR_APPROVAL = 'For Approval',
diff --git a/frontend/src/v5/ui/routes/viewer/defaultTicketFiltersSetter/defaultTicketFiltersSetter.component.tsx b/frontend/src/v5/ui/routes/viewer/defaultTicketFiltersSetter/defaultTicketFiltersSetter.component.tsx
new file mode 100644
index 00000000000..e554167bc0e
--- /dev/null
+++ b/frontend/src/v5/ui/routes/viewer/defaultTicketFiltersSetter/defaultTicketFiltersSetter.component.tsx
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2025 3D Repo Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import { TicketsCardActionsDispatchers, ViewerGuiActionsDispatchers } from '@/v5/services/actionsDispatchers';
+import { TicketsHooksSelectors } from '@/v5/services/selectorsHooks';
+import { isEmpty, uniq } from 'lodash';
+import { useParams } from 'react-router-dom';
+import { useEffect } from 'react';
+import { ViewerParams } from '../../routes.constants';
+import { Transformers, useSearchParam } from '../../useSearchParam';
+import { VIEWER_PANELS } from '@/v4/constants/viewerGui';
+import { CardFilter } from '@components/viewer/cards/cardFilters/cardFilters.types';
+import { StatusValue } from '@/v5/store/tickets/tickets.types';
+import { TicketStatusDefaultValues, TicketStatusTypes, TreatmentStatuses } from '@controls/chip/chip.types';
+import { selectStatusConfigByTemplateId } from '@/v5/store/tickets/tickets.selectors';
+import { getState } from '@/v5/helpers/redux.helpers';
+
+const TICKET_CODE_REGEX = /^[a-zA-Z]{3}:\d+$/;
+export const DefaultTicketFiltersSetter = () => {
+ const { containerOrFederation } = useParams();
+ const [ticketSearchParam, setTicketSearchParam] = useSearchParam('ticketSearch', Transformers.STRING_ARRAY);
+
+ const tickets = TicketsHooksSelectors.selectTickets(containerOrFederation);
+ const templates = TicketsHooksSelectors.selectTemplates(containerOrFederation);
+ const hasTicketData = !isEmpty(tickets) && !isEmpty(templates);
+
+ const getTicketFiltersFromURL = (values): CardFilter[] => [{
+ module: '',
+ property: 'Ticket ID',
+ type: 'ticketId',
+ filter: {
+ operator: 'eq',
+ values,
+ },
+ }];
+
+ const getNonCompletedTicketFiltersByStatus = (): CardFilter => {
+ const isCompletedValue = ({ type }: StatusValue) => [TicketStatusTypes.DONE, TicketStatusTypes.VOID].includes(type);
+ const getValuesByTemplate = ({ _id }) => selectStatusConfigByTemplateId(getState(), containerOrFederation, _id).values;
+
+ const completedValueNames = templates
+ .flatMap(getValuesByTemplate)
+ .filter(isCompletedValue)
+ .map((v) => v.name);
+
+ const values = uniq([
+ TicketStatusDefaultValues.CLOSED,
+ TicketStatusDefaultValues.VOID,
+ ...completedValueNames,
+ ]);
+
+ return {
+ module: '',
+ property: 'Status',
+ type: 'oneOf',
+ filter: {
+ operator: 'neq',
+ values,
+ },
+ };
+ };
+ const getNonCompletedTicketFiltersBySafetibase = (): CardFilter => ({
+ module: 'safetibase',
+ property: 'Treatment Status',
+ type: 'oneOf',
+ filter: {
+ operator: 'neq',
+ values: [
+ TreatmentStatuses.REJECTED,
+ TreatmentStatuses.VOID,
+ ],
+ },
+ });
+
+ const getNonCompletedTicketFilters = (): CardFilter[] => {
+ let filters = [getNonCompletedTicketFiltersByStatus()];
+ const hasSafetibase = templates.some((t) => t?.modules?.some((module) => module.type === 'safetibase'));
+ if (hasSafetibase) {
+ filters.push(getNonCompletedTicketFiltersBySafetibase());
+ }
+ return filters;
+ };
+
+ useEffect(() => {
+ if (hasTicketData) {
+ const ticketCodes = ticketSearchParam.filter((query) => TICKET_CODE_REGEX.test(query)).map((q) => q.toUpperCase());
+ const filters: CardFilter[] = ticketCodes.length ? getTicketFiltersFromURL(ticketCodes) : getNonCompletedTicketFilters();
+ filters.forEach(TicketsCardActionsDispatchers.upsertFilter);
+ ViewerGuiActionsDispatchers.setPanelVisibility(VIEWER_PANELS.TICKETS, true);
+ setTicketSearchParam();
+ }
+ }, [hasTicketData]);
+
+ return <>>;
+};
diff --git a/frontend/src/v5/ui/routes/viewer/handleTicketsCardSearchParams/handleTicketsCardSearchParams.component.tsx b/frontend/src/v5/ui/routes/viewer/handleTicketsCardSearchParams/handleTicketsCardSearchParams.component.tsx
deleted file mode 100644
index 7c28b41968d..00000000000
--- a/frontend/src/v5/ui/routes/viewer/handleTicketsCardSearchParams/handleTicketsCardSearchParams.component.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Copyright (C) 2023 3D Repo Ltd
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-import { DialogsActionsDispatchers, TicketsCardActionsDispatchers, ViewerGuiActionsDispatchers } from '@/v5/services/actionsDispatchers';
-import { TicketsHooksSelectors } from '@/v5/services/selectorsHooks';
-import { isEmpty } from 'lodash';
-import { useParams } from 'react-router-dom';
-import { useEffect } from 'react';
-import { formatMessage } from '@/v5/services/intl';
-import { ViewerParams } from '../../routes.constants';
-import { Transformers, useSearchParam } from '../../useSearchParam';
-import { VIEWER_PANELS } from '@/v4/constants/viewerGui';
-
-export const HandleTicketsCardSearchParams = () => {
- const { containerOrFederation } = useParams();
- const [ticketId, setTicketId] = useSearchParam('ticketId');
-
- const [ticketSearchParam, setTicketSearchParam] = useSearchParam('ticketSearch', Transformers.STRING_ARRAY);
- const [ticketTemplatesParam, setTicketTemplatesParam] = useSearchParam('ticketTemplates', Transformers.STRING_ARRAY);
- const [ticketCompletedParam, setTicketCompletedParam] = useSearchParam('ticketCompleted', Transformers.BOOLEAN);
-
- const tickets = TicketsHooksSelectors.selectTickets(containerOrFederation);
- const templates = TicketsHooksSelectors.selectTemplates(containerOrFederation);
- const hasTicketData = !isEmpty(tickets) && !isEmpty(templates);
-
- useEffect(() => {
- if (ticketId && hasTicketData) {
- if (!tickets.some(({ _id }) => _id === ticketId)) {
- DialogsActionsDispatchers.open('warning', {
- title: formatMessage({ id: 'openTicketFromUrl.invalidTicketId.title', defaultMessage: 'Ticket not found' }),
- message: formatMessage({ id: 'openTicketFromUrl.invalidTicketId.message', defaultMessage: 'A ticket with this ID could not be found. Ensure that you have the correct URL' }),
- });
- setTicketId('');
- return;
- }
- TicketsCardActionsDispatchers.openTicket(ticketId);
- }
- }, [hasTicketData]);
-
- useEffect(() => {
- if (!ticketTemplatesParam.length && !ticketSearchParam.length && !ticketCompletedParam) return;
- ViewerGuiActionsDispatchers.setPanelVisibility(VIEWER_PANELS.TICKETS, true);
- // TODO - waiting for refactor of URL params or a decision to create an adapter here
- // TODO - to use new filtering logic
- // TicketsCardActionsDispatchers.setTemplateFilters(ticketTemplatesParam);
- // if (ticketSearchParam.length) {
- // TicketsCardActionsDispatchers.setQueryFilters(ticketSearchParam);
- // }
- // if (ticketCompletedParam) {
- // TicketsCardActionsDispatchers.toggleCompleteFilter();
- // }
-
- setTicketSearchParam();
- setTicketCompletedParam();
- setTicketTemplatesParam();
- }, [ticketTemplatesParam, ticketSearchParam, ticketCompletedParam]);
-
- return <>>;
-};
diff --git a/frontend/src/v5/ui/routes/viewer/viewer.tsx b/frontend/src/v5/ui/routes/viewer/viewer.tsx
index f48f9636c9a..ec9036961ec 100644
--- a/frontend/src/v5/ui/routes/viewer/viewer.tsx
+++ b/frontend/src/v5/ui/routes/viewer/viewer.tsx
@@ -24,7 +24,7 @@ import { VIEWER_EVENTS } from '@/v4/constants/viewer';
import { CheckLatestRevisionReadiness } from './checkLatestRevisionReadiness/checkLatestRevisionReadiness.container';
import { ViewerParams } from '../routes.constants';
import { InvalidContainerOverlay, InvalidFederationOverlay } from './invalidViewerOverlay';
-import { HandleTicketsCardSearchParams } from './handleTicketsCardSearchParams/handleTicketsCardSearchParams.component';
+import { DefaultTicketFiltersSetter } from './defaultTicketFiltersSetter/defaultTicketFiltersSetter.component';
import { SpinnerLoader } from '@controls/spinnerLoader';
import { CentredContainer } from '@controls/centredContainer';
import { TicketsCardViews } from './tickets/tickets.constants';
@@ -111,7 +111,7 @@ export const Viewer = () => {
return (
<>
-
+