+
@@ -112,12 +116,12 @@ enum Placement {
const StyledModalBody = styled(Modal.Body)`
display: flex;
flex-direction: column;
- row-gap: 16px;
+ row-gap: var(--a-spacing-4);
`;
const StyledHeading = styled(Heading)`
display: flex;
flex-direction: row;
- column-gap: 8px;
+ column-gap: var(--a-spacing-2);
align-items: center;
`;
diff --git a/frontend/src/plate/toolbar/toolbars/saksbehandler-toolbar.tsx b/frontend/src/plate/toolbar/toolbars/saksbehandler-toolbar.tsx
index 65f2e8e4a..c7609d152 100644
--- a/frontend/src/plate/toolbar/toolbars/saksbehandler-toolbar.tsx
+++ b/frontend/src/plate/toolbar/toolbars/saksbehandler-toolbar.tsx
@@ -1,5 +1,3 @@
-import { ClockDashedIcon, InboxUpIcon, LightBulbIcon } from '@navikt/aksel-icons';
-import { useContext } from 'react';
import { SmartEditorContext } from '@app/components/smart-editor/context';
import { useBehandlingEnabled } from '@app/hooks/settings/use-setting';
import { MOD_KEY } from '@app/keys';
@@ -9,6 +7,8 @@ import { ToolbarSeparator } from '@app/plate/toolbar/separator';
import { FirstRow, StyledToolbar } from '@app/plate/toolbar/styled-components';
import { ToolbarIconButton } from '@app/plate/toolbar/toolbarbutton';
import { SaksbehandlerSettings } from '@app/plate/toolbar/toolbars/saksbehandler-settings';
+import { ClockDashedIcon, InboxUpIcon, LightBulbIcon } from '@navikt/aksel-icons';
+import { useContext } from 'react';
interface Props {
showHistory: boolean;
diff --git a/frontend/src/plate/toolbar/toolbars/table-toolbar.tsx b/frontend/src/plate/toolbar/toolbars/table-toolbar.tsx
index 241e3c235..ee35816c7 100644
--- a/frontend/src/plate/toolbar/toolbars/table-toolbar.tsx
+++ b/frontend/src/plate/toolbar/toolbars/table-toolbar.tsx
@@ -1,13 +1,13 @@
-import { findNode, isEditorFocused, toDOMNode } from '@udecode/plate-common';
-import { ELEMENT_TABLE } from '@udecode/plate-table';
-import { useEffect, useState } from 'react';
import { useScaleState } from '@app/components/smart-editor/hooks/use-scale';
import { CommentsButton } from '@app/plate/toolbar/add-comment';
import { InsertPlaceholder } from '@app/plate/toolbar/insert-placeholder';
import { Marks } from '@app/plate/toolbar/marks';
import { ToolbarSeparator } from '@app/plate/toolbar/separator';
import { TableButtons } from '@app/plate/toolbar/table/table';
-import { TableElement, useMyPlateEditorRef, useMyPlateEditorState } from '@app/plate/types';
+import { type TableElement, useMyPlateEditorRef, useMyPlateEditorState } from '@app/plate/types';
+import { findNode, isEditorFocused, toDOMNode } from '@udecode/plate-common';
+import { ELEMENT_TABLE } from '@udecode/plate-table';
+import { useEffect, useState } from 'react';
import { StyledFloatingToolbar } from './floating-toolbar';
interface Props {
diff --git a/frontend/src/plate/toolbar/use-is-in-table.tsx b/frontend/src/plate/toolbar/use-is-in-table.tsx
index 58939fec3..6a0ac8316 100644
--- a/frontend/src/plate/toolbar/use-is-in-table.tsx
+++ b/frontend/src/plate/toolbar/use-is-in-table.tsx
@@ -1,6 +1,6 @@
+import { useMyPlateEditorState } from '@app/plate/types';
import { someNode } from '@udecode/plate-common';
import { ELEMENT_TABLE } from '@udecode/plate-table';
-import { useMyPlateEditorState } from '@app/plate/types';
export const useIsInTable = () => {
const editor = useMyPlateEditorState();
diff --git a/frontend/src/plate/types.ts b/frontend/src/plate/types.ts
index cde46207c..327bf630e 100644
--- a/frontend/src/plate/types.ts
+++ b/frontend/src/plate/types.ts
@@ -1,28 +1,4 @@
-/* eslint-disable max-lines */
-/* eslint-disable import/no-unused-modules */
-import { AutoformatRule } from '@udecode/plate-autoformat';
-import {
- PlateEditor,
- PlateId,
- PlatePlugin,
- PluginOptions,
- TElement,
- TText,
- useEditorRef,
- useEditorState,
-} from '@udecode/plate-common';
-import { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading';
-import { ELEMENT_LI, ELEMENT_LIC, ELEMENT_OL, ELEMENT_UL } from '@udecode/plate-list';
-import { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph';
-import {
- ELEMENT_TABLE,
- ELEMENT_TD,
- ELEMENT_TR,
- TTableCellElement,
- TTableElement,
- TTableRowElement,
-} from '@udecode/plate-table';
-import {
+import type {
ELEMENT_CURRENT_DATE,
ELEMENT_EMPTY_VOID,
ELEMENT_FOOTER,
@@ -37,8 +13,34 @@ import {
ELEMENT_REGELVERK_CONTAINER,
ELEMENT_SIGNATURE,
} from '@app/plate/plugins/element-types';
-import { Language } from '@app/types/texts/language';
-import { TemplateSections } from './template-sections';
+import type { Language } from '@app/types/texts/language';
+/* eslint-disable max-lines */
+/* eslint-disable import/no-unused-modules */
+import type { CursorEditor, YjsEditor } from '@slate-yjs/core';
+import type { AutoformatRule } from '@udecode/plate-autoformat';
+import {
+ type PlateEditor,
+ type PlateId,
+ type PlatePlugin,
+ type PluginOptions,
+ type TElement,
+ type TText,
+ useEditorRef,
+ useEditorState,
+} from '@udecode/plate-common';
+import type { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading';
+import type { ELEMENT_LI, ELEMENT_LIC, ELEMENT_OL, ELEMENT_UL } from '@udecode/plate-list';
+import type { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph';
+import type {
+ ELEMENT_TABLE,
+ ELEMENT_TD,
+ ELEMENT_TR,
+ TTableCellElement,
+ TTableElement,
+ TTableRowElement,
+} from '@udecode/plate-table';
+import type { CursorEditorProps, PlateYjsEditorProps } from '@udecode/plate-yjs';
+import type { TemplateSections } from './template-sections';
export enum TextAlign {
LEFT = 'left',
@@ -275,5 +277,6 @@ export type EditorPlatePlugin = PlatePlugin
;
export type EditorAutoformatRule = AutoformatRule;
-export const useMyPlateEditorRef = (id?: PlateId) => useEditorRef(id);
+export const useMyPlateEditorRef = (id?: PlateId) =>
+ useEditorRef(id);
export const useMyPlateEditorState = (id?: PlateId) => useEditorState(id);
diff --git a/frontend/src/plate/utils/queries.ts b/frontend/src/plate/utils/queries.ts
index 5badc5bcb..0b6a50104 100644
--- a/frontend/src/plate/utils/queries.ts
+++ b/frontend/src/plate/utils/queries.ts
@@ -1,23 +1,6 @@
-import {
- PlateEditor,
- TDescendant,
- TElement,
- TNode,
- TText,
- findNode,
- findNodePath,
- getNodeAncestors,
- isCollapsed,
- isElement,
- isText,
- someNode,
-} from '@udecode/plate-common';
-import { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading';
-import { ELEMENT_OL, ELEMENT_UL } from '@udecode/plate-list';
-import { ELEMENT_TABLE } from '@udecode/plate-table';
import { ELEMENT_PLACEHOLDER, ELEMENT_REGELVERK, ELEMENT_REGELVERK_CONTAINER } from '@app/plate/plugins/element-types';
import { isInRegelverk, isInUnchangeableElement } from '@app/plate/plugins/prohibit-deletion/helpers';
-import {
+import type {
BulletListElement,
H1Element,
H2Element,
@@ -28,6 +11,23 @@ import {
RichTextEditor,
RichTextEditorElement,
} from '@app/plate/types';
+import {
+ type PlateEditor,
+ type TDescendant,
+ type TElement,
+ type TNode,
+ type TText,
+ findNode,
+ findNodePath,
+ getNodeAncestors,
+ isCollapsed,
+ isElement,
+ isText,
+ someNode,
+} from '@udecode/plate-common';
+import { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3 } from '@udecode/plate-heading';
+import { ELEMENT_OL, ELEMENT_UL } from '@udecode/plate-list';
+import { ELEMENT_TABLE } from '@udecode/plate-table';
// Ensures a next-path even though original path is at end
export const nextPath = (path: number[]) => {
diff --git a/frontend/src/plate/utils/transforms.ts b/frontend/src/plate/utils/transforms.ts
index 6586061dd..b16a87248 100644
--- a/frontend/src/plate/utils/transforms.ts
+++ b/frontend/src/plate/utils/transforms.ts
@@ -1,5 +1,9 @@
+import { ELEMENT_PLACEHOLDER } from '@app/plate/plugins/element-types';
+import { createPageBreak, createPlaceHolder, createSimpleParagraph } from '@app/plate/templates/helpers';
+import type { PlaceholderElement, RichTextEditor } from '@app/plate/types';
+import { isInTable, isPlaceholderActive } from '@app/plate/utils/queries';
import {
- PlateEditor,
+ type PlateEditor,
findNode,
insertElements,
insertNodes,
@@ -10,10 +14,6 @@ import {
withoutNormalizing,
} from '@udecode/plate-common';
import { Range } from 'slate';
-import { ELEMENT_PLACEHOLDER } from '@app/plate/plugins/element-types';
-import { createPageBreak, createPlaceHolder, createSimpleParagraph } from '@app/plate/templates/helpers';
-import { PlaceholderElement, RichTextEditor } from '@app/plate/types';
-import { isInTable, isPlaceholderActive } from '@app/plate/utils/queries';
export const insertPageBreak = (editor: RichTextEditor): boolean => {
if (isInTable(editor)) {
diff --git a/frontend/src/redux-api/access-rights.ts b/frontend/src/redux-api/access-rights.ts
index 57475f084..381d7ec6c 100644
--- a/frontend/src/redux-api/access-rights.ts
+++ b/frontend/src/redux-api/access-rights.ts
@@ -1,5 +1,5 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
import { toast } from '@app/components/toast/store';
+import { createApi } from '@reduxjs/toolkit/query/react';
import { INNSTILLINGER_BASE_QUERY } from './common';
export interface SaksbehandlerAccessRights {
diff --git a/frontend/src/redux-api/bruker.tsx b/frontend/src/redux-api/bruker.tsx
index 11913f351..63e42fd3b 100644
--- a/frontend/src/redux-api/bruker.tsx
+++ b/frontend/src/redux-api/bruker.tsx
@@ -1,9 +1,9 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { ABBREVIATIONS } from '@app/custom-data/abbreviations';
-import { CustomAbbrevation, ISetCustomInfoParams, ISettings, ISignatureResponse } from '@app/types/bruker';
+import type { CustomAbbrevation, ISetCustomInfoParams, ISettings, ISignatureResponse } from '@app/types/bruker';
import { isApiRejectionError } from '@app/types/errors';
+import { createApi } from '@reduxjs/toolkit/query/react';
import { INNSTILLINGER_BASE_QUERY } from './common';
export const brukerApi = createApi({
diff --git a/frontend/src/redux-api/collaboration.ts b/frontend/src/redux-api/collaboration.ts
new file mode 100644
index 000000000..d48fbe27b
--- /dev/null
+++ b/frontend/src/redux-api/collaboration.ts
@@ -0,0 +1,47 @@
+import { toast } from '@app/components/toast/store';
+import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
+import { PROXY_BASE_QUERY } from '@app/redux-api/common';
+import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
+import type { ISmartDocument } from '@app/types/documents/documents';
+import { isApiRejectionError } from '@app/types/errors';
+import type { ICreateSmartDocumentParams } from '@app/types/smart-editor/params';
+import { createApi } from '@reduxjs/toolkit/query/react';
+
+export const collaborationApi = createApi({
+ reducerPath: 'collaborationApi',
+ baseQuery: PROXY_BASE_QUERY,
+ endpoints: (builder) => ({
+ createSmartDocument: builder.mutation({
+ query: ({ oppgaveId, richText: content, dokumentTypeId, templateId, tittel, parentId }) => ({
+ url: `/collaboration/behandlinger/${oppgaveId}/dokumenter`,
+ method: 'POST',
+ body: { content, dokumentTypeId, templateId, tittel, parentId },
+ }),
+ onQueryStarted: async ({ oppgaveId }, { dispatch, queryFulfilled }) => {
+ try {
+ const { data } = await queryFulfilled;
+
+ dispatch(
+ documentsQuerySlice.util.updateQueryData('getDocuments', oppgaveId, (draft) =>
+ draft.some((e) => e.id === data.id) ? draft : [data, ...draft],
+ ),
+ );
+
+ dispatch(
+ documentsQuerySlice.util.updateQueryData('getDocument', { dokumentId: data.id, oppgaveId }, () => data),
+ );
+ } catch (e) {
+ const message = 'Kunne ikke opprette dokument.';
+
+ if (isApiRejectionError(e)) {
+ apiErrorToast(message, e.error);
+ } else {
+ toast.error(message);
+ }
+ }
+ },
+ }),
+ }),
+});
+
+export const { useCreateSmartDocumentMutation } = collaborationApi;
diff --git a/frontend/src/redux-api/common.ts b/frontend/src/redux-api/common.ts
index faca136b9..2b713013a 100644
--- a/frontend/src/redux-api/common.ts
+++ b/frontend/src/redux-api/common.ts
@@ -1,6 +1,6 @@
-import { FetchArgs, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react';
import { queryStringify } from '@app/functions/query-string';
import { setHeaders } from '@app/headers';
+import { type FetchArgs, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react';
export const IS_LOCALHOST = window.location.hostname === 'localhost';
@@ -50,6 +50,9 @@ const staggeredBaseQuery = (baseUrl: string) => {
);
};
+const PROXY_PATH = '';
+export const PROXY_BASE_QUERY = staggeredBaseQuery(PROXY_PATH);
+
const API_PATH = '/api';
export const API_BASE_QUERY = staggeredBaseQuery(API_PATH);
diff --git a/frontend/src/redux-api/journalposter.ts b/frontend/src/redux-api/journalposter.ts
index 71ac80aa2..fcd6ed3df 100644
--- a/frontend/src/redux-api/journalposter.ts
+++ b/frontend/src/redux-api/journalposter.ts
@@ -1,8 +1,8 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { DocumentTypeEnum } from '@app/types/documents/documents';
import { isApiRejectionError } from '@app/types/errors';
+import { createApi } from '@reduxjs/toolkit/query/react';
import { KABAL_API_BASE_QUERY } from './common';
import { documentsQuerySlice } from './oppgaver/queries/documents';
diff --git a/frontend/src/redux-api/kaka-kvalitetsvurdering/v1.ts b/frontend/src/redux-api/kaka-kvalitetsvurdering/v1.ts
index eba410f6e..863138fad 100644
--- a/frontend/src/redux-api/kaka-kvalitetsvurdering/v1.ts
+++ b/frontend/src/redux-api/kaka-kvalitetsvurdering/v1.ts
@@ -1,11 +1,11 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
-import {
+import type {
IKvalitetsvurderingBooleans,
IKvalitetsvurderingRadio,
IKvalitetsvurderingRadioExtended,
IKvalitetsvurderingTexts,
IKvalitetsvurderingV1,
} from '@app/types/kaka-kvalitetsvurdering/v1';
+import { createApi } from '@reduxjs/toolkit/query/react';
import { KAKA_KVALITETSVURDERING_BASE_QUERY } from '../common';
type WithId = Pick;
diff --git a/frontend/src/redux-api/kaka-kvalitetsvurdering/v2.ts b/frontend/src/redux-api/kaka-kvalitetsvurdering/v2.ts
index f95418999..30997cade 100644
--- a/frontend/src/redux-api/kaka-kvalitetsvurdering/v2.ts
+++ b/frontend/src/redux-api/kaka-kvalitetsvurdering/v2.ts
@@ -1,5 +1,5 @@
+import type { IKvalitetsvurdering, IKvalitetsvurderingData } from '@app/types/kaka-kvalitetsvurdering/v2';
import { createApi } from '@reduxjs/toolkit/query/react';
-import { IKvalitetsvurdering, IKvalitetsvurderingData } from '@app/types/kaka-kvalitetsvurdering/v2';
import { KAKA_KVALITETSVURDERING_BASE_QUERY } from '../common';
type Argument = Partial & Pick;
diff --git a/frontend/src/redux-api/logiske-vedlegg.ts b/frontend/src/redux-api/logiske-vedlegg.ts
index 864fa8556..9dfc628b4 100644
--- a/frontend/src/redux-api/logiske-vedlegg.ts
+++ b/frontend/src/redux-api/logiske-vedlegg.ts
@@ -1,8 +1,8 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
-import { format } from 'date-fns';
import { ISO_DATETIME_FORMAT } from '@app/components/date-picker/constants';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { LogiskVedlegg } from '@app/types/arkiverte-documents';
+import type { LogiskVedlegg } from '@app/types/arkiverte-documents';
+import { createApi } from '@reduxjs/toolkit/query/react';
+import { format } from 'date-fns';
import { KABAL_API_BASE_QUERY } from './common';
const OPTIMISTIC_LOGISK_VEDLEGG_ID_PREFIX = 'optimistic-logisk-vedlegg';
diff --git a/frontend/src/redux-api/maltekstseksjoner/consumer.ts b/frontend/src/redux-api/maltekstseksjoner/consumer.ts
index 423b0db0a..a14f4187d 100644
--- a/frontend/src/redux-api/maltekstseksjoner/consumer.ts
+++ b/frontend/src/redux-api/maltekstseksjoner/consumer.ts
@@ -1,8 +1,8 @@
+import type { IGetTextsParams } from '@app/types/common-text-types';
+import type { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
+import type { IConsumerRichText } from '@app/types/texts/consumer';
+import type { Language } from '@app/types/texts/language';
import { createApi } from '@reduxjs/toolkit/query/react';
-import { IGetTextsParams } from '@app/types/common-text-types';
-import { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
-import { IConsumerRichText } from '@app/types/texts/consumer';
-import { Language } from '@app/types/texts/language';
import { KABAL_TEXT_TEMPLATES_BASE_QUERY } from '../common';
export enum ConsumerMaltekstseksjonerTagTypes {
diff --git a/frontend/src/redux-api/maltekstseksjoner/mutations.ts b/frontend/src/redux-api/maltekstseksjoner/mutations.ts
index 43c8a6b66..52011668a 100644
--- a/frontend/src/redux-api/maltekstseksjoner/mutations.ts
+++ b/frontend/src/redux-api/maltekstseksjoner/mutations.ts
@@ -1,11 +1,7 @@
-/* eslint-disable max-lines */
-import { formatISO } from 'date-fns';
-import { Patch } from 'immer';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { createSimpleParagraph } from '@app/plate/templates/helpers';
-import { EditorValue } from '@app/plate/types';
-import { reduxStore } from '@app/redux/configure-store';
+import type { EditorValue } from '@app/plate/types';
import {
ConsumerMaltekstseksjonerTagTypes,
consumerMaltekstseksjonerApi,
@@ -14,9 +10,10 @@ import { maltekstseksjonerApi } from '@app/redux-api/maltekstseksjoner/malteksts
import { maltekstseksjonerQuerySlice } from '@app/redux-api/maltekstseksjoner/queries';
import { getLastPublishedAndVersionToShowInTrash } from '@app/redux-api/redaktoer-helpers';
import { ConsumerTextsTagTypes, consumerTextsApi } from '@app/redux-api/texts/consumer';
-import { IGetMaltekstseksjonParams, PublishedTextReadOnlyMetadata } from '@app/types/common-text-types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { IGetMaltekstseksjonParams, PublishedTextReadOnlyMetadata } from '@app/types/common-text-types';
import { isApiRejectionError } from '@app/types/errors';
-import {
+import type {
ICreateDraftFromMaltekstseksjonVersionParams,
IDeleteMaltekstDraftParams,
INewMaltekstseksjonParams,
@@ -28,13 +25,16 @@ import {
IUpdateMaltekstseksjonUtfallParams,
IUpdateMaltekstseksjonYtelseHjemmelParams,
} from '@app/types/maltekstseksjoner/params';
-import {
+import type {
IDraftMaltekstseksjon,
IPublishWithTextsResponse,
IPublishedMaltekstseksjon,
} from '@app/types/maltekstseksjoner/responses';
-import { INewRichTextParams } from '@app/types/texts/common';
-import { LANGUAGES, Language } from '@app/types/texts/language';
+import type { INewRichTextParams } from '@app/types/texts/common';
+import { LANGUAGES, type Language } from '@app/types/texts/language';
+/* eslint-disable max-lines */
+import { formatISO } from 'date-fns';
+import type { Patch } from 'immer';
import { textsQuerySlice } from '../texts/queries';
const maltekstseksjonerMutationSlice = maltekstseksjonerApi.injectEndpoints({
diff --git a/frontend/src/redux-api/maltekstseksjoner/queries.ts b/frontend/src/redux-api/maltekstseksjoner/queries.ts
index e8389e9dc..c0403dcd9 100644
--- a/frontend/src/redux-api/maltekstseksjoner/queries.ts
+++ b/frontend/src/redux-api/maltekstseksjoner/queries.ts
@@ -1,6 +1,6 @@
import { MaltekstseksjonTagTypes, maltekstseksjonerApi } from '@app/redux-api/maltekstseksjoner/maltekstseksjoner';
-import { IGetMaltekstseksjonParams } from '@app/types/common-text-types';
-import { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
+import type { IGetMaltekstseksjonParams } from '@app/types/common-text-types';
+import type { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
import { IS_LOCALHOST } from '../common';
export const maltekstseksjonerQuerySlice = maltekstseksjonerApi.injectEndpoints({
diff --git a/frontend/src/redux-api/messages.ts b/frontend/src/redux-api/messages.ts
index 3e4c5fbc4..85dd27f6c 100644
--- a/frontend/src/redux-api/messages.ts
+++ b/frontend/src/redux-api/messages.ts
@@ -1,8 +1,8 @@
+import { ISO_DATETIME_FORMAT } from '@app/components/date-picker/constants';
+import type { INavEmployee } from '@app/types/bruker';
+import type { IOppgavebehandlingBaseParams } from '@app/types/oppgavebehandling/params';
import { createApi } from '@reduxjs/toolkit/query/react';
import { format } from 'date-fns';
-import { ISO_DATETIME_FORMAT } from '@app/components/date-picker/constants';
-import { INavEmployee } from '@app/types/bruker';
-import { IOppgavebehandlingBaseParams } from '@app/types/oppgavebehandling/params';
import { KABAL_BEHANDLINGER_BASE_QUERY } from './common';
import { ListTagTypes } from './tag-types';
diff --git a/frontend/src/redux-api/oppgaver/mutations/behandling-dates.ts b/frontend/src/redux-api/oppgaver/mutations/behandling-dates.ts
index 401cf6b4b..a7d1a6c78 100644
--- a/frontend/src/redux-api/oppgaver/mutations/behandling-dates.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/behandling-dates.ts
@@ -1,16 +1,16 @@
-import { reduxStore } from '@app/redux/configure-store';
import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-data';
+import { reduxStore } from '@app/redux/configure-store';
import { isApiError } from '@app/types/errors';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
-import {
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type {
IFristParams,
IKjennelseMottattParams,
IMottattKlageinstansParams,
IMottattVedtaksinstansParams,
ISendtTilTrygderettenParams,
} from '@app/types/oppgavebehandling/params';
-import { IModifiedResponse } from '@app/types/oppgavebehandling/response';
-import { IOppgave } from '@app/types/oppgaver';
+import type { IModifiedResponse } from '@app/types/oppgavebehandling/response';
+import type { IOppgave } from '@app/types/oppgaver';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
@@ -140,9 +140,9 @@ const updateBehandling = (
) => {
const patchResult = reduxStore.dispatch(
behandlingerQuerySlice.util.updateQueryData('getOppgavebehandling', oppgaveId, (draft) => {
- values.forEach(([key, value]) => {
+ for (const [key, value] of values) {
draft[key] = value;
- });
+ }
}),
);
@@ -152,9 +152,9 @@ const updateBehandling = (
const updateOppgaveData = (oppgaveId: string, values: [K, IOppgave[K]][]) => {
const patchResult = reduxStore.dispatch(
oppgaveDataQuerySlice.util.updateQueryData('getOppgave', oppgaveId, (draft) => {
- values.forEach(([key, value]) => {
+ for (const [key, value] of values) {
draft[key] = value;
- });
+ }
}),
);
diff --git a/frontend/src/redux-api/oppgaver/mutations/behandling.ts b/frontend/src/redux-api/oppgaver/mutations/behandling.ts
index fcb437a2d..8174377f8 100644
--- a/frontend/src/redux-api/oppgaver/mutations/behandling.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/behandling.ts
@@ -1,24 +1,24 @@
-import { format } from 'date-fns';
import { ISO_FORMAT } from '@app/components/date-picker/constants';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { formatIdNumber } from '@app/functions/format-id';
-import { reduxStore } from '@app/redux/configure-store';
import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-data';
+import { reduxStore } from '@app/redux/configure-store';
import { isApiRejectionError } from '@app/types/errors';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
-import {
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type {
IFinishOppgavebehandlingParams,
IOppgavebehandlingBaseParams,
ISetFeilregistrertParams,
ISetFullmektigParams,
ISetKlagerParams,
} from '@app/types/oppgavebehandling/params';
-import {
+import type {
IModifiedResponse,
ISetFeilregistrertResponse,
IVedtakFullfoertResponse,
} from '@app/types/oppgavebehandling/response';
+import { format } from 'date-fns';
import { IS_LOCALHOST } from '../../common';
import { kvalitetsvurderingV1Api } from '../../kaka-kvalitetsvurdering/v1';
import { kvalitetsvurderingV2Api } from '../../kaka-kvalitetsvurdering/v2';
diff --git a/frontend/src/redux-api/oppgaver/mutations/documents.ts b/frontend/src/redux-api/oppgaver/mutations/documents.ts
index 98798cf22..a05190981 100644
--- a/frontend/src/redux-api/oppgaver/mutations/documents.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/documents.ts
@@ -4,32 +4,32 @@ import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { getIsIncomingDocument } from '@app/functions/is-incoming-document';
import { reduxStore } from '@app/redux/configure-store';
-import { IArkivertDocument, IArkivertDocumentVedlegg, Journalposttype } from '@app/types/arkiverte-documents';
-import { IDocumentParams } from '@app/types/documents/common-params';
+import { type IArkivertDocument, type IArkivertDocumentVedlegg, Journalposttype } from '@app/types/arkiverte-documents';
+import type { IDocumentParams } from '@app/types/documents/common-params';
import {
DistribusjonsType,
DocumentTypeEnum,
- IFileDocument,
- IMainDocument,
- InngaaendeKanal,
+ type IFileDocument,
+ type IMainDocument,
+ type InngaaendeKanal,
} from '@app/types/documents/documents';
import {
- ICreateFileDocumentParams,
- ICreateVedleggFromJournalfoertDocumentParams,
- IFinishDocumentParams,
- ISetMottakerListParams,
- ISetNameParams,
- ISetParentParams,
- ISetTypeParams,
+ type ICreateFileDocumentParams,
+ type ICreateVedleggFromJournalfoertDocumentParams,
+ type IFinishDocumentParams,
+ type ISetMottakerListParams,
+ type ISetNameParams,
+ type ISetParentParams,
+ type ISetTypeParams,
mottakerToInputMottaker,
} from '@app/types/documents/params';
-import {
+import type {
ICreateVedleggFromJournalfoertDocumentResponse,
IModifiedDocumentResponse,
ISetParentResponse,
} from '@app/types/documents/response';
import { isApiRejectionError } from '@app/types/errors';
-import { IPart } from '@app/types/oppgave-common';
+import type { IPart } from '@app/types/oppgave-common';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { documentsQuerySlice } from '../queries/documents';
@@ -400,6 +400,7 @@ const documentsMutationSlice = oppgaverApi.injectEndpoints({
const dokumenter = new Array(dokumenterCount);
for (let i = dokumenterCount - 1; i >= 0; i--) {
+ // biome-ignore lint/style/noNonNullAssertion: Guaranteed to be defined.
const doc = draft.dokumenter[i]!;
const journalpostDocuments = journalfoerteDokumenter.filter((j) => j.journalpostId === doc.journalpostId);
@@ -412,6 +413,7 @@ const documentsMutationSlice = oppgaverApi.injectEndpoints({
const vedlegg = new Array(vedleggCount);
for (let ii = vedleggCount - 1; ii >= 0; ii--) {
+ // biome-ignore lint/style/noNonNullAssertion: Guaranteed to be defined.
const v = doc.vedlegg[ii]!;
const vedleggInList =
v.valgt || journalpostDocuments.some((j) => j.dokumentInfoId === v.dokumentInfoId);
diff --git a/frontend/src/redux-api/oppgaver/mutations/remove-tilknytt-document.ts b/frontend/src/redux-api/oppgaver/mutations/remove-tilknytt-document.ts
index 2a2d4a633..9360d7803 100644
--- a/frontend/src/redux-api/oppgaver/mutations/remove-tilknytt-document.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/remove-tilknytt-document.ts
@@ -1,7 +1,7 @@
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { isApiRejectionError } from '@app/types/errors';
-import { ICheckDocumentParams } from '@app/types/oppgavebehandling/params';
+import type { ICheckDocumentParams } from '@app/types/oppgavebehandling/params';
import { IS_LOCALHOST } from '../../common';
import { ListTagTypes } from '../../tag-types';
import { DokumenterListTagTypes, oppgaverApi } from '../oppgaver';
diff --git a/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver-flowstate.ts b/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver-flowstate.ts
index 15a4b4413..967a8be2f 100644
--- a/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver-flowstate.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver-flowstate.ts
@@ -3,8 +3,8 @@ import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-t
import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-data';
import { isApiRejectionError } from '@app/types/errors';
import { FlowState } from '@app/types/oppgave-common';
-import { ISetFlowStateParams } from '@app/types/oppgavebehandling/params';
-import { ISetFlowStateResponse } from '@app/types/oppgavebehandling/response';
+import type { ISetFlowStateParams } from '@app/types/oppgavebehandling/params';
+import type { ISetFlowStateResponse } from '@app/types/oppgavebehandling/response';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver.ts b/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver.ts
index e71a20b3f..7c0e459c0 100644
--- a/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/set-medunderskriver.ts
@@ -3,8 +3,8 @@ import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-t
import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-data';
import { isApiRejectionError } from '@app/types/errors';
import { FlowState } from '@app/types/oppgave-common';
-import { ISetMedunderskriverParams } from '@app/types/oppgavebehandling/params';
-import { ISetMedunderskriverResponse } from '@app/types/oppgavebehandling/response';
+import type { ISetMedunderskriverParams } from '@app/types/oppgavebehandling/params';
+import type { ISetMedunderskriverResponse } from '@app/types/oppgavebehandling/response';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/mutations/set-registreringshjemler.ts b/frontend/src/redux-api/oppgaver/mutations/set-registreringshjemler.ts
index c95fa778d..8aecb7a97 100644
--- a/frontend/src/redux-api/oppgaver/mutations/set-registreringshjemler.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/set-registreringshjemler.ts
@@ -1,7 +1,7 @@
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { isApiRejectionError } from '@app/types/errors';
-import { IOppgavebehandlingHjemlerUpdateParams } from '@app/types/oppgavebehandling/params';
+import type { IOppgavebehandlingHjemlerUpdateParams } from '@app/types/oppgavebehandling/params';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/mutations/set-rol-flowstate.ts b/frontend/src/redux-api/oppgaver/mutations/set-rol-flowstate.ts
index e81fca4da..34479d05e 100644
--- a/frontend/src/redux-api/oppgaver/mutations/set-rol-flowstate.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/set-rol-flowstate.ts
@@ -4,8 +4,8 @@ import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-d
import { isApiRejectionError } from '@app/types/errors';
import { SaksTypeEnum } from '@app/types/kodeverk';
import { FlowState } from '@app/types/oppgave-common';
-import { ISetFlowStateParams } from '@app/types/oppgavebehandling/params';
-import { ISetFlowStateResponse } from '@app/types/oppgavebehandling/response';
+import type { ISetFlowStateParams } from '@app/types/oppgavebehandling/params';
+import type { ISetFlowStateResponse } from '@app/types/oppgavebehandling/response';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/mutations/set-rol.ts b/frontend/src/redux-api/oppgaver/mutations/set-rol.ts
index 7092c1998..b74b6a1d8 100644
--- a/frontend/src/redux-api/oppgaver/mutations/set-rol.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/set-rol.ts
@@ -4,8 +4,8 @@ import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-d
import { isApiRejectionError } from '@app/types/errors';
import { SaksTypeEnum } from '@app/types/kodeverk';
import { FlowState } from '@app/types/oppgave-common';
-import { ISetRolParams } from '@app/types/oppgavebehandling/params';
-import { ISetRolResponse } from '@app/types/oppgavebehandling/response';
+import type { ISetRolParams } from '@app/types/oppgavebehandling/params';
+import type { ISetRolResponse } from '@app/types/oppgavebehandling/response';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/mutations/set-utfall.ts b/frontend/src/redux-api/oppgaver/mutations/set-utfall.ts
index cc905f9db..a5eb22264 100644
--- a/frontend/src/redux-api/oppgaver/mutations/set-utfall.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/set-utfall.ts
@@ -2,11 +2,11 @@ import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-data';
import { isApiRejectionError } from '@app/types/errors';
-import {
+import type {
IOppgavebehandlingUtfallSetUpdateParams,
IOppgavebehandlingUtfallUpdateParams,
} from '@app/types/oppgavebehandling/params';
-import { ISetExtraUtfallResponse, ISetUtfallResponse } from '@app/types/oppgavebehandling/response';
+import type { ISetExtraUtfallResponse, ISetUtfallResponse } from '@app/types/oppgavebehandling/response';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/mutations/smart-document.ts b/frontend/src/redux-api/oppgaver/mutations/smart-document.ts
index 9f7ee67f5..13aead894 100644
--- a/frontend/src/redux-api/oppgaver/mutations/smart-document.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/smart-document.ts
@@ -1,12 +1,6 @@
import { toast } from '@app/components/toast/store';
-import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
-import { user } from '@app/static-data/static-data';
-import { IDocumentParams } from '@app/types/documents/common-params';
-import { ISmartDocument } from '@app/types/documents/documents';
-import { IModifiedSmartDocumentResponse } from '@app/types/documents/response';
-import { isApiRejectionError } from '@app/types/errors';
-import { ICreateSmartDocumentParams, IUpdateSmartDocumentParams } from '@app/types/smart-editor/params';
-import { Language } from '@app/types/texts/language';
+import type { IDocumentParams } from '@app/types/documents/common-params';
+import type { Language } from '@app/types/texts/language';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { documentsQuerySlice } from '../queries/documents';
@@ -14,94 +8,6 @@ import { documentsQuerySlice } from '../queries/documents';
const smartDocumentsMutationSlice = oppgaverApi.injectEndpoints({
overrideExisting: IS_LOCALHOST,
endpoints: (builder) => ({
- createSmartDocument: builder.mutation({
- query: ({ oppgaveId, richText: content, dokumentTypeId, templateId, tittel, parentId }) => ({
- url: `/kabal-api/behandlinger/${oppgaveId}/smartdokumenter`,
- method: 'POST',
- body: { content, dokumentTypeId, templateId, tittel, parentId },
- }),
- onQueryStarted: async ({ oppgaveId }, { dispatch, queryFulfilled }) => {
- try {
- const { data } = await queryFulfilled;
-
- dispatch(
- documentsQuerySlice.util.updateQueryData('getDocuments', oppgaveId, (draft) =>
- draft.some((e) => e.id === data.id) ? draft : [data, ...draft],
- ),
- );
-
- dispatch(
- documentsQuerySlice.util.updateQueryData('getDocument', { dokumentId: data.id, oppgaveId }, () => data),
- );
- } catch (e) {
- const message = 'Kunne ikke opprette dokument.';
-
- if (isApiRejectionError(e)) {
- apiErrorToast(message, e.error);
- } else {
- toast.error(message);
- }
- }
- },
- }),
-
- updateSmartDocument: builder.mutation({
- query: ({ oppgaveId, dokumentId, ...body }) => ({
- url: `/kabal-api/behandlinger/${oppgaveId}/smartdokumenter/${dokumentId}`,
- method: 'PATCH',
- body,
- timeout: 10_000,
- }),
- onQueryStarted: async ({ dokumentId, oppgaveId, content }, { dispatch, queryFulfilled }) => {
- try {
- const { navIdent, navn } = await user;
- const { data } = await queryFulfilled;
- const { modified, version } = data;
-
- dispatch(
- documentsQuerySlice.util.updateQueryData('getDocument', { dokumentId, oppgaveId }, (draft) => {
- if (draft !== null && draft.isSmartDokument) {
- return { ...draft, content, modified, version };
- }
- }),
- );
-
- dispatch(
- documentsQuerySlice.util.updateQueryData('getDocuments', oppgaveId, (draft) =>
- draft.map((d) => (d.isSmartDokument && d.id === dokumentId ? { ...d, modified, version, content } : d)),
- ),
- );
-
- dispatch(
- documentsQuerySlice.util.updateQueryData(
- 'getSmartDocumentVersion',
- { dokumentId, oppgaveId, versionId: version },
- () => content,
- ),
- );
-
- dispatch(
- documentsQuerySlice.util.updateQueryData('getSmartDocumentVersions', { dokumentId, oppgaveId }, (draft) => [
- {
- version,
- timestamp: modified,
- author: { navIdent, navn },
- },
- ...draft,
- ]),
- );
- } catch (e: unknown) {
- const message = 'Feil ved lagring av dokument.';
-
- if (isApiRejectionError(e)) {
- apiErrorToast(message, e.error);
- } else {
- toast.error(message);
- }
- }
- },
- }),
-
setLanguage: builder.mutation({
query: ({ dokumentId, oppgaveId, language }) => ({
url: `/kabal-api/behandlinger/${oppgaveId}/dokumenter/${dokumentId}/language`,
@@ -135,5 +41,4 @@ const smartDocumentsMutationSlice = oppgaverApi.injectEndpoints({
}),
});
-export const { useCreateSmartDocumentMutation, useUpdateSmartDocumentMutation, useSetLanguageMutation } =
- smartDocumentsMutationSlice;
+export const { useSetLanguageMutation } = smartDocumentsMutationSlice;
diff --git a/frontend/src/redux-api/oppgaver/mutations/tildeling.ts b/frontend/src/redux-api/oppgaver/mutations/tildeling.ts
index 7ed30d399..2bf419ec1 100644
--- a/frontend/src/redux-api/oppgaver/mutations/tildeling.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/tildeling.ts
@@ -1,4 +1,3 @@
-import { format } from 'date-fns';
import { ISO_DATETIME_FORMAT } from '@app/components/date-picker/constants';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
@@ -8,11 +7,12 @@ import { isApiRejectionError } from '@app/types/errors';
import { HistoryEventTypes } from '@app/types/oppgavebehandling/response';
import {
FradelReason,
- FradelSaksbehandlerParams,
- IFradelingResponse,
- ITildelingResponse,
- TildelSaksbehandlerParams,
+ type FradelSaksbehandlerParams,
+ type IFradelingResponse,
+ type ITildelingResponse,
+ type TildelSaksbehandlerParams,
} from '@app/types/oppgaver';
+import { format } from 'date-fns';
import { IS_LOCALHOST } from '../../common';
import { OppgaveListTagTypes, oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/mutations/tilknytt-document.ts b/frontend/src/redux-api/oppgaver/mutations/tilknytt-document.ts
index 3e0b2e996..c3ba841e3 100644
--- a/frontend/src/redux-api/oppgaver/mutations/tilknytt-document.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/tilknytt-document.ts
@@ -1,8 +1,8 @@
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { isApiRejectionError } from '@app/types/errors';
-import { ICheckDocumentParams } from '@app/types/oppgavebehandling/params';
-import { ITilknyttDocumentResponse } from '@app/types/oppgavebehandling/response';
+import type { ICheckDocumentParams } from '@app/types/oppgavebehandling/params';
+import type { ITilknyttDocumentResponse } from '@app/types/oppgavebehandling/response';
import { IS_LOCALHOST } from '../../common';
import { ListTagTypes } from '../../tag-types';
import { DokumenterListTagTypes, oppgaverApi } from '../oppgaver';
diff --git a/frontend/src/redux-api/oppgaver/mutations/vent.ts b/frontend/src/redux-api/oppgaver/mutations/vent.ts
index 76e9be6c1..b21b1d00d 100644
--- a/frontend/src/redux-api/oppgaver/mutations/vent.ts
+++ b/frontend/src/redux-api/oppgaver/mutations/vent.ts
@@ -1,11 +1,11 @@
-import { format } from 'date-fns';
import { ISO_FORMAT } from '@app/components/date-picker/constants';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { oppgaveDataQuerySlice } from '@app/redux-api/oppgaver/queries/oppgave-data';
import { isApiRejectionError } from '@app/types/errors';
-import { ISettPaaVentParams } from '@app/types/oppgavebehandling/params';
-import { IModifiedResponse } from '@app/types/oppgavebehandling/response';
+import type { ISettPaaVentParams } from '@app/types/oppgavebehandling/params';
+import type { IModifiedResponse } from '@app/types/oppgavebehandling/response';
+import { format } from 'date-fns';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
import { behandlingerQuerySlice } from '../queries/behandling/behandling';
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/behandling.ts b/frontend/src/redux-api/oppgaver/queries/behandling/behandling.ts
index 0d1e3a805..aeaf99ec6 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/behandling.ts
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/behandling.ts
@@ -1,7 +1,7 @@
/* eslint-disable max-lines */
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
-import { IApiValidationResponse } from '@app/functions/error-type-guard';
+import type { IApiValidationResponse } from '@app/functions/error-type-guard';
import { IS_LOCALHOST, KABAL_BEHANDLINGER_BASE_PATH } from '@app/redux-api/common';
import { handleDocumentFinishedEvent } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/document-finished';
import { handleDocumentsAddedEvent } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/documents-added';
@@ -30,15 +30,15 @@ import { handleUtfallEvent } from '@app/redux-api/oppgaver/queries/behandling/ev
import { ServerSentEventManager, ServerSentEventType } from '@app/redux-api/server-sent-events/server-sent-events';
import { user } from '@app/static-data/static-data';
import { isApiRejectionError } from '@app/types/errors';
-import { ISakenGjelder } from '@app/types/oppgave-common';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
-import { IValidationParams } from '@app/types/oppgavebehandling/params';
-import {
+import type { ISakenGjelder } from '@app/types/oppgave-common';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { IValidationParams } from '@app/types/oppgavebehandling/params';
+import type {
IMedunderskrivereResponse,
ISaksbehandlerResponse,
ITildelingEvent,
} from '@app/types/oppgavebehandling/response';
-import { IRols, ISaksbehandlere } from '@app/types/oppgaver';
+import type { IRols, ISaksbehandlere } from '@app/types/oppgaver';
import { OppgaveTagTypes, oppgaverApi } from '../../oppgaver';
import { handleMedunderskriverEvent } from './event-handlers/medunderskriver';
import { handleMessageEvent } from './event-handlers/message';
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/common.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/common.tsx
index 5ae1d336a..671a71e70 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/common.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/common.tsx
@@ -1,6 +1,6 @@
import { formatEmployeeNameAndIdFallback } from '@app/domain/employee-name';
import { formatIdNumber } from '@app/functions/format-id';
-import { INavEmployee } from '@app/types/bruker';
+import type { INavEmployee } from '@app/types/bruker';
interface Props {
id: string;
@@ -13,7 +13,7 @@ export const FormatName = ({ id, name = 'Navn mangler' }: Props) => (
);
-export const employeeName = (employee: INavEmployee | null, fallback: string = 'ingen / felles kø') => (
+export const employeeName = (employee: INavEmployee | null, fallback = 'ingen / felles kø') => (
{formatEmployeeNameAndIdFallback(employee, fallback)}
);
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/document-finished.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/document-finished.tsx
index 1d54cb2d1..51118361a 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/document-finished.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/document-finished.tsx
@@ -1,7 +1,7 @@
-import { reduxStore } from '@app/redux/configure-store';
import { handleJournalpostAddedEvent } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/journalpost-added';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { DocumentFinishedEvent } from '@app/redux-api/server-sent-events/types';
+import type { DocumentFinishedEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
export const handleDocumentFinishedEvent = (oppgaveId: string, userId: string) => (event: DocumentFinishedEvent) => {
handleJournalpostAddedEvent(oppgaveId, userId)(event);
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-added.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-added.tsx
index 61bb5c130..38ab5b3d1 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-added.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-added.tsx
@@ -1,17 +1,17 @@
-import { Tag } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { DocumentsAddedEvent } from '@app/redux-api/server-sent-events/types';
-import { INavEmployee } from '@app/types/bruker';
+import type { DocumentsAddedEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { INavEmployee } from '@app/types/bruker';
import {
DISTRIBUTION_TYPE_NAMES,
DOCUMENT_TYPE_NAMES,
DocumentTypeEnum,
- IMainDocument,
+ type IMainDocument,
} from '@app/types/documents/documents';
+import { Tag } from '@navikt/ds-react';
export const handleDocumentsAddedEvent = (oppgaveId: string, userId: string) => (event: DocumentsAddedEvent) => {
const { actor, documents } = event;
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-changed.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-changed.tsx
index 00792bae3..b091fa793 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-changed.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-changed.tsx
@@ -1,11 +1,11 @@
-import { Tag } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { DocumentsChangedEvent } from '@app/redux-api/server-sent-events/types';
-import { DISTRIBUTION_TYPE_NAMES, DocumentTypeEnum, IMainDocument } from '@app/types/documents/documents';
+import type { DocumentsChangedEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import { DISTRIBUTION_TYPE_NAMES, DocumentTypeEnum, type IMainDocument } from '@app/types/documents/documents';
+import { Tag } from '@navikt/ds-react';
export const handleDocumentsChangedEvent = (oppgaveId: string, userId: string) => (event: DocumentsChangedEvent) => {
reduxStore.dispatch(
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-removed.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-removed.tsx
index 9f40474ae..962820387 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-removed.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/documents-removed.tsx
@@ -1,18 +1,18 @@
-import { Tag } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { DocumentsRemovedEvent } from '@app/redux-api/server-sent-events/types';
-import { INavEmployee } from '@app/types/bruker';
+import type { DocumentsRemovedEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { INavEmployee } from '@app/types/bruker';
import {
DISTRIBUTION_TYPE_NAMES,
DOCUMENT_TYPE_NAMES,
DocumentTypeEnum,
- IMainDocument,
- IParentDocument,
+ type IMainDocument,
+ type IParentDocument,
} from '@app/types/documents/documents';
+import { Tag } from '@navikt/ds-react';
export const handleDocumentsRemovedEvent = (oppgaveId: string, userId: string) => (event: DocumentsRemovedEvent) => {
reduxStore.dispatch(
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/extra-utfall.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/extra-utfall.tsx
index 13f69acdd..c52679849 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/extra-utfall.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/extra-utfall.tsx
@@ -1,13 +1,13 @@
-import { Tag } from '@navikt/ds-react';
-import { styled } from 'styled-components';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
import { useUtfallNameOrLoading } from '@app/hooks/use-utfall-name';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
-import { ExtraUtfallEvent } from '@app/redux-api/server-sent-events/types';
-import { UtfallEnum } from '@app/types/kodeverk';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { ExtraUtfallEvent } from '@app/redux-api/server-sent-events/types';
+import type { UtfallEnum } from '@app/types/kodeverk';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import { Tag } from '@navikt/ds-react';
+import { styled } from 'styled-components';
export const handleExtraUtfallEvent =
(_: string, userId: string, updateCachedData: UpdateFn) =>
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/feilregistrering.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/feilregistrering.tsx
index 3cadc4ffa..26d942a04 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/feilregistrering.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/feilregistrering.tsx
@@ -1,11 +1,11 @@
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { FeilregistreringEvent } from '@app/redux-api/server-sent-events/types';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { FeilregistreringEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
import { HistoryEventTypes } from '@app/types/oppgavebehandling/response';
export const handleFeilregistreringEvent =
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/ferdigstilt.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/ferdigstilt.tsx
index f6ec7933c..91dee952a 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/ferdigstilt.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/ferdigstilt.tsx
@@ -1,11 +1,11 @@
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { FerdigstiltEvent } from '@app/redux-api/server-sent-events/types';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { FerdigstiltEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
import { HistoryEventTypes } from '@app/types/oppgavebehandling/response';
export const handleFerdigstiltEvent =
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/fullmektig.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/fullmektig.tsx
index 2a9e4db9b..20effab62 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/fullmektig.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/fullmektig.tsx
@@ -1,17 +1,17 @@
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { FormatName } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/common';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { FullmektigEvent } from '@app/redux-api/server-sent-events/types';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { FullmektigEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
import {
- BaseEvent,
- FullmektigEvent as FullmektigHistoryEvent,
+ type BaseEvent,
+ type FullmektigEvent as FullmektigHistoryEvent,
HistoryEventTypes,
- IFullmektigEvent,
+ type IFullmektigEvent,
} from '@app/types/oppgavebehandling/response';
export const handlefullmektigEvent =
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/hjemler.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/hjemler.tsx
index c43bd3825..7fab9c9ef 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/hjemler.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/hjemler.tsx
@@ -1,13 +1,13 @@
-import { Tag } from '@navikt/ds-react';
-import { styled } from 'styled-components';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
-import { HjemlerEvent } from '@app/redux-api/server-sent-events/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { HjemlerEvent } from '@app/redux-api/server-sent-events/types';
import { useRegistreringshjemlerMap } from '@app/simple-api-state/use-kodeverk';
-import { INavEmployee } from '@app/types/bruker';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { INavEmployee } from '@app/types/bruker';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import { Tag } from '@navikt/ds-react';
+import { styled } from 'styled-components';
export const handleRegistreringshjemlerEvent =
(userId: string, updateCachedData: UpdateFn) =>
@@ -111,5 +111,5 @@ const HjemmelList = ({ hjemler }: { hjemler: string[] }) => {
const TagContainer = styled.span`
display: flex;
flex-wrap: wrap;
- gap: 4px;
+ gap: var(--a-spacing-1);
`;
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalfoert-document-modified.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalfoert-document-modified.tsx
index 4968e5bd5..3c96e8fab 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalfoert-document-modified.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalfoert-document-modified.tsx
@@ -1,9 +1,9 @@
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { JournalfoertDocumentModifiedEvent } from '@app/redux-api/server-sent-events/types';
+import type { JournalfoertDocumentModifiedEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
import { DocumentTypeEnum } from '@app/types/documents/documents';
export const handleJournalfoertDocumentModified =
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalpost-added.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalpost-added.tsx
index 752e27620..058149951 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalpost-added.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/journalpost-added.tsx
@@ -1,10 +1,10 @@
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { JournalpostAddedEvent } from '@app/redux-api/server-sent-events/types';
-import { AvsenderMottaker, Journalposttype, Sak } from '@app/types/arkiverte-documents';
+import type { JournalpostAddedEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import { type AvsenderMottaker, Journalposttype, type Sak } from '@app/types/arkiverte-documents';
export const handleJournalpostAddedEvent = (oppgaveId: string, userId: string) => (event: JournalpostAddedEvent) => {
const { journalpostList, actor } = event;
@@ -51,23 +51,23 @@ export const handleJournalpostAddedEvent = (oppgaveId: string, userId: string) =
(list, { avsenderMottaker }) =>
avsenderMottaker === null || list.some((a) => a.id === avsenderMottaker.id)
? list
- : [...list, avsenderMottaker],
+ : list.concat(avsenderMottaker),
[],
);
const journalposttypeList = journalpostList.reduce(
(list, { journalposttype }) =>
- journalposttype === null || list.includes(journalposttype) ? list : [...list, journalposttype],
+ journalposttype === null || list.includes(journalposttype) ? list : list.concat(journalposttype),
[],
);
const sakList = journalpostList.reduce(
- (list, { sak }) => (sak === null || list.some((s) => isSakEqual(s, sak)) ? list : [...list, sak]),
+ (list, { sak }) => (sak === null || list.some((s) => isSakEqual(s, sak)) ? list : list.concat(sak)),
[],
);
const temaIdList = journalpostList.reduce(
- (list, { tema }) => (tema === null || list.includes(tema) ? list : [...list, tema]),
+ (list, { tema }) => (tema === null || list.includes(tema) ? list : list.concat(tema)),
[],
);
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/klager.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/klager.tsx
index 195e01fe3..4a6aa0237 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/klager.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/klager.tsx
@@ -1,17 +1,17 @@
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { FormatName } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/common';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { KlagerEvent } from '@app/redux-api/server-sent-events/types';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { KlagerEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
import {
- BaseEvent,
+ type BaseEvent,
HistoryEventTypes,
- IKlagerEvent,
- KlagerEvent as KlagerHistoryEvent,
+ type IKlagerEvent,
+ type KlagerEvent as KlagerHistoryEvent,
} from '@app/types/oppgavebehandling/response';
export const handleKlagerEvent =
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver-toast.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver-toast.tsx
index 500c6bd30..4410716da 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver-toast.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver-toast.tsx
@@ -1,6 +1,6 @@
import { formatEmployeeName } from '@app/domain/employee-name';
import { employeeName } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/common';
-import { INavEmployee } from '@app/types/bruker';
+import type { INavEmployee } from '@app/types/bruker';
import { FlowState } from '@app/types/oppgave-common';
interface Params {
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver.tsx
index 9767ec8e8..ddeeb320f 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver.tsx
@@ -1,14 +1,14 @@
-import { BodyLong } from '@navikt/ds-react';
import { toast } from '@app/components/toast/store';
-import { reduxStore } from '@app/redux/configure-store';
import { getMedunderskriverToastContent } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/medunderskriver-toast';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { MedunderskriverEvent } from '@app/redux-api/server-sent-events/types';
-import { INavEmployee } from '@app/types/bruker';
+import type { MedunderskriverEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { INavEmployee } from '@app/types/bruker';
import { FlowState } from '@app/types/oppgave-common';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
import { HistoryEventTypes } from '@app/types/oppgavebehandling/response';
+import { BodyLong } from '@navikt/ds-react';
export const handleMedunderskriverEvent =
(oppgaveId: string, userId: string, updateCachedData: UpdateFn) =>
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/message.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/message.tsx
index 032a44166..3e5a9e29f 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/message.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/message.tsx
@@ -1,10 +1,10 @@
-import { BodyShort } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
+import { type IMessage, OPTIMISTIC_MESSAGE_ID_PREFIX, messagesApi } from '@app/redux-api/messages';
+import type { NewMessageEvent } from '@app/redux-api/server-sent-events/types';
import { reduxStore } from '@app/redux/configure-store';
-import { IMessage, OPTIMISTIC_MESSAGE_ID_PREFIX, messagesApi } from '@app/redux-api/messages';
-import { NewMessageEvent } from '@app/redux-api/server-sent-events/types';
+import { BodyShort } from '@navikt/ds-react';
export const handleMessageEvent = (oppgaveId: string, userId: string) => (event: NewMessageEvent) => {
if (event.actor.navIdent !== userId) {
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/mottatt-vedtaksinstans.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/mottatt-vedtaksinstans.tsx
index 7410fc4b5..e94803829 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/mottatt-vedtaksinstans.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/mottatt-vedtaksinstans.tsx
@@ -2,9 +2,9 @@ import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { isoDateToPretty } from '@app/domain/date';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
-import { MottattVedtaksinstansEvent } from '@app/redux-api/server-sent-events/types';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { MottattVedtaksinstansEvent } from '@app/redux-api/server-sent-events/types';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
export const handleMottattVedtaksinstansEvent =
(userId: string, updateCachedData: UpdateFn) =>
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol-toast.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol-toast.tsx
index c87a6e01e..1dbc298cd 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol-toast.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol-toast.tsx
@@ -1,8 +1,8 @@
-import { useContext } from 'react';
import { StaticDataContext } from '@app/components/app/static-data-context';
import { employeeName } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/common';
-import { INavEmployee } from '@app/types/bruker';
+import type { INavEmployee } from '@app/types/bruker';
import { FlowState } from '@app/types/oppgave-common';
+import { useContext } from 'react';
interface Params {
flowState: FlowState;
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol.tsx
index 08d228c95..2f8b48b6b 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/rol.tsx
@@ -1,14 +1,14 @@
-import { BodyLong } from '@navikt/ds-react';
import { toast } from '@app/components/toast/store';
-import { reduxStore } from '@app/redux/configure-store';
import { getRolToastContent } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/rol-toast';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { RolEvent } from '@app/redux-api/server-sent-events/types';
+import type { RolEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
import { SaksTypeEnum } from '@app/types/kodeverk';
import { FlowState } from '@app/types/oppgave-common';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
import { HistoryEventTypes } from '@app/types/oppgavebehandling/response';
+import { BodyLong } from '@navikt/ds-react';
export const handleRolEvent =
(oppgaveId: string, userId: string, updateCachedData: UpdateFn) =>
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/satt-paa-vent.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/satt-paa-vent.tsx
index 538edbd8e..afdacbbe1 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/satt-paa-vent.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/satt-paa-vent.tsx
@@ -1,15 +1,15 @@
-import { Label } from '@navikt/ds-react';
-import { styled } from 'styled-components';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { isoDateToPretty } from '@app/domain/date';
-import { reduxStore } from '@app/redux/configure-store';
import { employeeName } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/common';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { SattPaaVentEvent } from '@app/redux-api/server-sent-events/types';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { SattPaaVentEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
import { HistoryEventTypes } from '@app/types/oppgavebehandling/response';
+import { Label } from '@navikt/ds-react';
+import { styled } from 'styled-components';
export const handleSattPaaVentEvent =
(oppgaveId: string, userId: string, updateCachedData: UpdateFn) =>
@@ -83,6 +83,6 @@ const Reason = styled.p`
margin: 0;
padding: 0;
font-style: italic;
- padding-left: 4px;
- border-left: 4px solid var(--a-border-subtle);
+ padding-left: var(--a-spacing-1);
+ border-left: var(--a-spacing-1) solid var(--a-border-subtle);
`;
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-added.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-added.tsx
index 39c09c7dc..776f8eb74 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-added.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-added.tsx
@@ -1,11 +1,11 @@
-import { BodyShort } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
-import { SmartDocumentCommentEvent } from '@app/redux-api/server-sent-events/types';
+import type { SmartDocumentCommentEvent } from '@app/redux-api/server-sent-events/types';
import { smartEditorCommentsApi } from '@app/redux-api/smart-editor-comments';
-import { ISmartEditorComment } from '@app/types/smart-editor/comments';
+import { reduxStore } from '@app/redux/configure-store';
+import type { ISmartEditorComment } from '@app/types/smart-editor/comments';
+import { BodyShort } from '@navikt/ds-react';
export const handleSmartDocumentCommentAddedEvent =
(oppgaveId: string, userId: string) => (event: SmartDocumentCommentEvent) => {
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-changed.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-changed.tsx
index b0ca49f4d..888e23451 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-changed.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-changed.tsx
@@ -1,11 +1,11 @@
-import { BodyShort } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
-import { SmartDocumentCommentEvent } from '@app/redux-api/server-sent-events/types';
+import type { SmartDocumentCommentEvent } from '@app/redux-api/server-sent-events/types';
import { smartEditorCommentsApi } from '@app/redux-api/smart-editor-comments';
-import { ISmartEditorComment } from '@app/types/smart-editor/comments';
+import { reduxStore } from '@app/redux/configure-store';
+import type { ISmartEditorComment } from '@app/types/smart-editor/comments';
+import { BodyShort } from '@navikt/ds-react';
export const handleSmartDocumentCommentChangedEvent =
(oppgaveId: string, userId: string) => (event: SmartDocumentCommentEvent) => {
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-removed.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-removed.tsx
index 6afd96764..cc6ebb39d 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-removed.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/comment-removed.tsx
@@ -1,6 +1,6 @@
-import { reduxStore } from '@app/redux/configure-store';
-import { SmartDocumentCommentEvent } from '@app/redux-api/server-sent-events/types';
+import type { SmartDocumentCommentEvent } from '@app/redux-api/server-sent-events/types';
import { smartEditorCommentsApi } from '@app/redux-api/smart-editor-comments';
+import { reduxStore } from '@app/redux/configure-store';
export const handleSmartDocumentCommentRemovedEvent = (oppgaveId: string) => (event: SmartDocumentCommentEvent) => {
reduxStore.dispatch(
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/language-changed.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/language-changed.tsx
index fc83132b7..d6e8e1e8f 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/language-changed.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/language-changed.tsx
@@ -1,11 +1,11 @@
-import { Tag } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
-import { reduxStore } from '@app/redux/configure-store';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { SmartDocumentLanguageEvent } from '@app/redux-api/server-sent-events/types';
+import type { SmartDocumentLanguageEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
import { LANGUAGE_NAMES } from '@app/types/texts/language';
+import { Tag } from '@navikt/ds-react';
export const handleSmartDocumentLanguageChangedEvent =
(oppgaveId: string, userId: string) => (event: SmartDocumentLanguageEvent) => {
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/versioned.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/versioned.tsx
index 45768eef2..a88860928 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/versioned.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/smart-document/versioned.tsx
@@ -1,6 +1,6 @@
-import { reduxStore } from '@app/redux/configure-store';
import { documentsQuerySlice } from '@app/redux-api/oppgaver/queries/documents';
-import { SmartDocumentVersionedEvent } from '@app/redux-api/server-sent-events/types';
+import type { SmartDocumentVersionedEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
export const handleSmartDocumentVersionedEvent =
(oppgaveId: string) =>
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/tildeling.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/tildeling.tsx
index 91b9ecf25..594aa379d 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/tildeling.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/tildeling.tsx
@@ -1,17 +1,17 @@
-/* eslint-disable max-lines */
-import { Label, Tag } from '@navikt/ds-react';
-import { styled } from 'styled-components';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
-import { reduxStore } from '@app/redux/configure-store';
import { QUEUE, SELF, employeeName } from '@app/redux-api/oppgaver/queries/behandling/event-handlers/common';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
import { historyQuerySlice } from '@app/redux-api/oppgaver/queries/history';
-import { TildelingEvent } from '@app/redux-api/server-sent-events/types';
-import { INavEmployee } from '@app/types/bruker';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
-import { HistoryEventTypes, ITildelingEvent } from '@app/types/oppgavebehandling/response';
+import type { TildelingEvent } from '@app/redux-api/server-sent-events/types';
+import { reduxStore } from '@app/redux/configure-store';
+import type { INavEmployee } from '@app/types/bruker';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import { HistoryEventTypes, type ITildelingEvent } from '@app/types/oppgavebehandling/response';
import { FradelReason } from '@app/types/oppgaver';
+/* eslint-disable max-lines */
+import { Label, Tag } from '@navikt/ds-react';
+import { styled } from 'styled-components';
export const handleTildelingEvent =
(oppgaveId: string, userId: string, updateCachedData: UpdateFn) =>
@@ -248,7 +248,7 @@ const getToastContent = (
const FlexRowContainer = styled.div`
display: flex;
align-items: center;
- gap: 4px;
+ gap: var(--a-spacing-1);
`;
const getFromText = (userId: string, employee: INavEmployee | null) => {
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/utfall.tsx b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/utfall.tsx
index 4477cf627..7229b2a96 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/utfall.tsx
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/event-handlers/utfall.tsx
@@ -1,12 +1,12 @@
-import { Tag } from '@navikt/ds-react';
import { InfoToast } from '@app/components/toast/info-toast';
import { toast } from '@app/components/toast/store';
import { formatEmployeeName } from '@app/domain/employee-name';
import { useUtfallNameOrLoading } from '@app/hooks/use-utfall-name';
-import { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
-import { UtfallEvent } from '@app/redux-api/server-sent-events/types';
-import { UtfallEnum } from '@app/types/kodeverk';
-import { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { UpdateFn } from '@app/redux-api/oppgaver/queries/behandling/types';
+import type { UtfallEvent } from '@app/redux-api/server-sent-events/types';
+import type { UtfallEnum } from '@app/types/kodeverk';
+import type { IOppgavebehandling } from '@app/types/oppgavebehandling/oppgavebehandling';
+import { Tag } from '@navikt/ds-react';
export const handleUtfallEvent =
(userId: string, updateCachedData: UpdateFn) =>
diff --git a/frontend/src/redux-api/oppgaver/queries/behandling/types.ts b/frontend/src/redux-api/oppgaver/queries/behandling/types.ts
index e52ac0c04..b0e15a71d 100644
--- a/frontend/src/redux-api/oppgaver/queries/behandling/types.ts
+++ b/frontend/src/redux-api/oppgaver/queries/behandling/types.ts
@@ -1,7 +1,7 @@
-import { Draft } from '@reduxjs/toolkit';
+import type { Draft } from '@reduxjs/toolkit';
type MaybeDrafted = T | Draft;
-type Recipe = (data: MaybeDrafted) => void | MaybeDrafted;
+type Recipe = (data: MaybeDrafted) => undefined | MaybeDrafted;
export type UpdateFn = (recipe: Recipe) => PatchCollection;
interface Patch {
diff --git a/frontend/src/redux-api/oppgaver/queries/documents.ts b/frontend/src/redux-api/oppgaver/queries/documents.ts
index ada0dae6b..07ec71a3e 100644
--- a/frontend/src/redux-api/oppgaver/queries/documents.ts
+++ b/frontend/src/redux-api/oppgaver/queries/documents.ts
@@ -1,17 +1,17 @@
-import { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph';
-import { IShownArchivedDocument } from '@app/components/view-pdf/types';
+import type { IShownArchivedDocument } from '@app/components/view-pdf/types';
import { ELEMENT_LABEL_CONTENT } from '@app/plate/plugins/element-types';
-import { EditorValue, TextAlign } from '@app/plate/types';
-import { IArkiverteDocumentsResponse } from '@app/types/arkiverte-documents';
-import { IDocumentParams } from '@app/types/documents/common-params';
-import {
+import { type EditorValue, TextAlign } from '@app/plate/types';
+import type { IArkiverteDocumentsResponse } from '@app/types/arkiverte-documents';
+import type { IDocumentParams } from '@app/types/documents/common-params';
+import type {
IMainDocument,
IMergedDocumentsResponse,
ISmartDocument,
ISmartDocumentVersion,
} from '@app/types/documents/documents';
-import { IGetVersionParams } from '@app/types/documents/params';
-import { IValidateDocumentResponse } from '@app/types/documents/validation';
+import type { IGetVersionParams } from '@app/types/documents/params';
+import type { IValidateDocumentResponse } from '@app/types/documents/validation';
+import { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph';
import { IS_LOCALHOST } from '../../common';
import { ListTagTypes } from '../../tag-types';
import { DokumenterListTagTypes, oppgaverApi } from '../oppgaver';
@@ -83,7 +83,7 @@ export const documentsQuerySlice = oppgaverApi.injectEndpoints({
}),
mergedDocumentsReference: builder.query({
query: (body) => ({
- url: `/kabal-api/journalposter/mergedocuments`,
+ url: '/kabal-api/journalposter/mergedocuments',
method: 'POST',
body,
}),
diff --git a/frontend/src/redux-api/oppgaver/queries/history.ts b/frontend/src/redux-api/oppgaver/queries/history.ts
index 8ab80e2bf..eeb69beea 100644
--- a/frontend/src/redux-api/oppgaver/queries/history.ts
+++ b/frontend/src/redux-api/oppgaver/queries/history.ts
@@ -1,4 +1,4 @@
-import { IHistoryResponse } from '@app/types/oppgavebehandling/response';
+import type { IHistoryResponse } from '@app/types/oppgavebehandling/response';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
diff --git a/frontend/src/redux-api/oppgaver/queries/oppgave-data.ts b/frontend/src/redux-api/oppgaver/queries/oppgave-data.ts
index e7bc4c0bd..7c8c7f102 100644
--- a/frontend/src/redux-api/oppgaver/queries/oppgave-data.ts
+++ b/frontend/src/redux-api/oppgaver/queries/oppgave-data.ts
@@ -1,4 +1,4 @@
-import { IOppgave } from '@app/types/oppgaver';
+import type { IOppgave } from '@app/types/oppgaver';
import { IS_LOCALHOST } from '../../common';
import { oppgaverApi } from '../oppgaver';
diff --git a/frontend/src/redux-api/oppgaver/queries/oppgaver.ts b/frontend/src/redux-api/oppgaver/queries/oppgaver.ts
index dd5fd4819..603239965 100644
--- a/frontend/src/redux-api/oppgaver/queries/oppgaver.ts
+++ b/frontend/src/redux-api/oppgaver/queries/oppgaver.ts
@@ -1,5 +1,5 @@
-import { IPartBase } from '@app/types/oppgave-common';
-import {
+import type { IPartBase } from '@app/types/oppgave-common';
+import type {
ApiResponse,
CommonOppgaverParams,
EnhetensOppgaverParams,
@@ -19,19 +19,19 @@ const oppgaverQuerySlice = oppgaverApi.injectEndpoints({
overrideExisting: IS_LOCALHOST,
endpoints: (builder) => ({
getMineFerdigstilteOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-api/oppgaver/ferdigstilte`, params }),
+ query: (params) => ({ url: '/kabal-api/oppgaver/ferdigstilte', params }),
providesTags: [OppgaveListTagTypes.MINE_FERDIGE],
}),
getMineUferdigeOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/oppgaver/uferdige`, params }),
+ query: (params) => ({ url: '/kabal-search/oppgaver/uferdige', params }),
providesTags: [OppgaveListTagTypes.MINE_UFERDIGE],
}),
getMineVentendeOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/oppgaver/paavent`, params }),
+ query: (params) => ({ url: '/kabal-search/oppgaver/paavent', params }),
providesTags: [OppgaveListTagTypes.MINE_VENTENDE],
}),
getLedigeOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/oppgaver/ledige`, params }),
+ query: (params) => ({ url: '/kabal-search/oppgaver/ledige', params }),
providesTags: [OppgaveListTagTypes.LEDIGE],
}),
getEnhetensFerdigstilteOppgaver: builder.query({
@@ -50,21 +50,21 @@ const oppgaverQuerySlice = oppgaverApi.injectEndpoints({
providesTags: [OppgaveListTagTypes.ENHETENS_VENTENDE],
}),
getAntallLedigeOppgaverMedUtgaatteFrister: builder.query({
- query: (params) => ({ url: `/kabal-search/antalloppgavermedutgaattefrister`, params }),
+ query: (params) => ({ url: '/kabal-search/antalloppgavermedutgaattefrister', params }),
}),
searchPeopleByName: builder.query({
- query: (body) => ({ url: `/kabal-search/search/name`, method: 'POST', body }),
+ query: (body) => ({ url: '/kabal-search/search/name', method: 'POST', body }),
}),
searchOppgaverByFnr: builder.query({
query: (query) => ({
- url: `/kabal-search/search/oppgaver`,
+ url: '/kabal-search/search/oppgaver',
method: 'POST', // Søk POST for å ikke sende fnr inn i URLen, som blir logget.
body: { query },
}),
}),
searchPersonByFnr: builder.query({
query: (identifikator) => ({
- url: `/kabal-api/searchperson`,
+ url: '/kabal-api/searchperson',
method: 'POST', // Søk POST for å ikke sende fnr inn i URLen, som blir logget.
body: { identifikator },
}),
@@ -76,26 +76,26 @@ const oppgaverQuerySlice = oppgaverApi.injectEndpoints({
query: (enhet) => `/kabal-search/enheter/${enhet}/medunderskrivere`,
}),
getRolsInEnhet: builder.query({
- query: () => `/kabal-search/rol-list`,
+ query: () => '/kabal-search/rol-list',
}),
getLedigeRolOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/roloppgaver/ledige`, params }),
+ query: (params) => ({ url: '/kabal-search/roloppgaver/ledige', params }),
providesTags: [OppgaveListTagTypes.ROL_LEDIGE],
}),
getUferdigeRolOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/roloppgaver/uferdige`, params }),
+ query: (params) => ({ url: '/kabal-search/roloppgaver/uferdige', params }),
providesTags: [OppgaveListTagTypes.ROL_UFERDIGE],
}),
getReturnerteRolOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/roloppgaver/returnerte`, params }),
+ query: (params) => ({ url: '/kabal-search/roloppgaver/returnerte', params }),
providesTags: [OppgaveListTagTypes.ROL_FERDIGE],
}),
getRolReturnerteOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/kroloppgaver/tildelte/returnerte`, params }),
+ query: (params) => ({ url: '/kabal-search/kroloppgaver/tildelte/returnerte', params }),
providesTags: [OppgaveListTagTypes.KROLS_FERDIGE],
}),
getRolUferdigeOppgaver: builder.query({
- query: (params) => ({ url: `/kabal-search/kroloppgaver/tildelte/uferdige`, params }),
+ query: (params) => ({ url: '/kabal-search/kroloppgaver/tildelte/uferdige', params }),
providesTags: [OppgaveListTagTypes.KROLS_UFERDIGE],
}),
getRelevantOppgaver: builder.query({
diff --git a/frontend/src/redux-api/redaktoer-helpers.test.ts b/frontend/src/redux-api/redaktoer-helpers.test.ts
index e051a45b8..268fa99a2 100644
--- a/frontend/src/redux-api/redaktoer-helpers.test.ts
+++ b/frontend/src/redux-api/redaktoer-helpers.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'bun:test';
import { getLastPublishedAndVersionToShowInTrash } from '@app/redux-api/redaktoer-helpers';
-import { IText } from '@app/types/texts/responses';
+import type { IText } from '@app/types/texts/responses';
const createIText = (published: boolean, publishedDateTime: string | null): IText =>
({
diff --git a/frontend/src/redux-api/redaktoer-helpers.ts b/frontend/src/redux-api/redaktoer-helpers.ts
index 5daf75011..9d88ae235 100644
--- a/frontend/src/redux-api/redaktoer-helpers.ts
+++ b/frontend/src/redux-api/redaktoer-helpers.ts
@@ -1,5 +1,5 @@
-import { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
-import { IText } from '@app/types/texts/responses';
+import type { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
+import type { IText } from '@app/types/texts/responses';
export const getLastPublishedAndVersionToShowInTrash = (
versions: T[],
diff --git a/frontend/src/redux-api/search.ts b/frontend/src/redux-api/search.ts
index 51a8e23c1..e4fa0263e 100644
--- a/frontend/src/redux-api/search.ts
+++ b/frontend/src/redux-api/search.ts
@@ -1,6 +1,6 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
import { KABAL_API_BASE_QUERY } from '@app/redux-api/common';
-import { IPart } from '@app/types/oppgave-common';
+import type { IPart } from '@app/types/oppgave-common';
+import { createApi } from '@reduxjs/toolkit/query/react';
interface SearchPartWithUtsendingskanalParams {
identifikator: string;
@@ -13,7 +13,7 @@ export const searchApi = createApi({
baseQuery: KABAL_API_BASE_QUERY,
endpoints: (builder) => ({
searchpartwithutsendingskanal: builder.query({
- query: (body) => ({ url: `/searchpartwithutsendingskanal`, method: 'POST', body }),
+ query: (body) => ({ url: '/searchpartwithutsendingskanal', method: 'POST', body }),
}),
}),
});
diff --git a/frontend/src/redux-api/server-sent-events/types.ts b/frontend/src/redux-api/server-sent-events/types.ts
index 064dec57a..f5b7dc590 100644
--- a/frontend/src/redux-api/server-sent-events/types.ts
+++ b/frontend/src/redux-api/server-sent-events/types.ts
@@ -1,11 +1,11 @@
-import { IArkivertDocument } from '@app/types/arkiverte-documents';
-import { INavEmployee } from '@app/types/bruker';
-import { DistribusjonsType, IMainDocument } from '@app/types/documents/documents';
-import { UtfallEnum } from '@app/types/kodeverk';
-import { FlowState, IOrganizationPart, IPersonPart, IVenteperiode } from '@app/types/oppgave-common';
-import { IFeilregistrering } from '@app/types/oppgavebehandling/oppgavebehandling';
-import { FradelReason } from '@app/types/oppgaver';
-import { Language } from '@app/types/texts/language';
+import type { IArkivertDocument } from '@app/types/arkiverte-documents';
+import type { INavEmployee } from '@app/types/bruker';
+import type { DistribusjonsType, IMainDocument } from '@app/types/documents/documents';
+import type { UtfallEnum } from '@app/types/kodeverk';
+import type { FlowState, IOrganizationPart, IPersonPart, IVenteperiode } from '@app/types/oppgave-common';
+import type { IFeilregistrering } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { FradelReason } from '@app/types/oppgaver';
+import type { Language } from '@app/types/texts/language';
interface BaseEvent {
actor: INavEmployee;
diff --git a/frontend/src/redux-api/smart-editor-comments.ts b/frontend/src/redux-api/smart-editor-comments.ts
index e3a92d0d6..ffd36beda 100644
--- a/frontend/src/redux-api/smart-editor-comments.ts
+++ b/frontend/src/redux-api/smart-editor-comments.ts
@@ -1,12 +1,12 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
-import { IDocumentParams } from '@app/types/documents/common-params';
-import {
+import type { IDocumentParams } from '@app/types/documents/common-params';
+import type {
IDeleteCommentOrReplyParams,
IPatchCommentOrReplyParams,
IPostCommentParams,
IPostReplyParams,
ISmartEditorComment,
} from '@app/types/smart-editor/comments';
+import { createApi } from '@reduxjs/toolkit/query/react';
import { KABAL_BEHANDLINGER_BASE_QUERY } from './common';
export const smartEditorCommentsApi = createApi({
diff --git a/frontend/src/redux-api/svarbrev.ts b/frontend/src/redux-api/svarbrev.ts
index 685d9339b..f36de50b8 100644
--- a/frontend/src/redux-api/svarbrev.ts
+++ b/frontend/src/redux-api/svarbrev.ts
@@ -1,18 +1,18 @@
-import { createApi } from '@reduxjs/toolkit/query/react';
-import { format } from 'date-fns';
import { ISO_DATETIME_FORMAT } from '@app/components/date-picker/constants';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { KABAL_API_BASE_QUERY } from '@app/redux-api/common';
import { isApiRejectionError } from '@app/types/errors';
-import { SvarbrevSetting, UpdateSvarbrevSettingParams } from '@app/types/svarbrev';
+import type { SvarbrevSetting, UpdateSvarbrevSettingParams } from '@app/types/svarbrev';
+import { createApi } from '@reduxjs/toolkit/query/react';
+import { format } from 'date-fns';
export const svarbrevApi = createApi({
reducerPath: 'svarbrevApi',
baseQuery: KABAL_API_BASE_QUERY,
endpoints: (builder) => ({
getSvarbrevSettings: builder.query({
- query: () => ({ url: `/svarbrev-settings`, method: 'GET' }),
+ query: () => ({ url: '/svarbrev-settings', method: 'GET' }),
}),
getSvarbrevSettingHistory: builder.query({
query: (id) => ({ url: `/svarbrev-settings/${id}/history`, method: 'GET' }),
diff --git a/frontend/src/redux-api/texts/consumer.ts b/frontend/src/redux-api/texts/consumer.ts
index 9352b2a4e..7dd0843ed 100644
--- a/frontend/src/redux-api/texts/consumer.ts
+++ b/frontend/src/redux-api/texts/consumer.ts
@@ -1,6 +1,6 @@
+import type { IGetConsumerTextParams, IGetConsumerTextsParams } from '@app/types/common-text-types';
+import type { IConsumerText } from '@app/types/texts/consumer';
import { createApi } from '@reduxjs/toolkit/query/react';
-import { IGetConsumerTextParams, IGetConsumerTextsParams } from '@app/types/common-text-types';
-import { IConsumerText } from '@app/types/texts/consumer';
import { KABAL_TEXT_TEMPLATES_BASE_QUERY } from '../common';
export enum ConsumerTextsTagTypes {
diff --git a/frontend/src/redux-api/texts/mutations.ts b/frontend/src/redux-api/texts/mutations.ts
index 9912a4d02..f66a1f023 100644
--- a/frontend/src/redux-api/texts/mutations.ts
+++ b/frontend/src/redux-api/texts/mutations.ts
@@ -1,16 +1,14 @@
-/* eslint-disable max-lines */
-import { formatISO } from 'date-fns';
import { toast } from '@app/components/toast/store';
import { apiErrorToast } from '@app/components/toast/toast-content/fetch-error-toast';
import { isGodFormulering, isPlainText, isRegelverk, isRichText } from '@app/functions/is-rich-plain-text';
-import { reduxStore } from '@app/redux/configure-store';
import { getLastPublishedAndVersionToShowInTrash } from '@app/redux-api/redaktoer-helpers';
import { ConsumerTextsTagTypes, consumerTextsApi } from '@app/redux-api/texts/consumer';
import { textsApi } from '@app/redux-api/texts/texts';
+import { reduxStore } from '@app/redux/configure-store';
import { user } from '@app/static-data/static-data';
import { isApiRejectionError } from '@app/types/errors';
import { LANGUAGES, UNTRANSLATED, isLanguage } from '@app/types/texts/language';
-import {
+import type {
ICreateDraftFromVersionParams,
IDeleteTextDraftParams,
IGetTextsParams,
@@ -25,7 +23,7 @@ import {
IUpdateTextUtfallIdListParams,
IUpdateTextYtelseHjemmelIdListParams,
} from '@app/types/texts/params';
-import {
+import type {
IDraftRichText,
IGodFormulering,
IPlainText,
@@ -34,7 +32,9 @@ import {
IRichText,
IText,
} from '@app/types/texts/responses';
-import { IDeleteDraftOrUnpublishTextResponse } from '@app/types/texts/responses-maltekster';
+import type { IDeleteDraftOrUnpublishTextResponse } from '@app/types/texts/responses-maltekster';
+/* eslint-disable max-lines */
+import { formatISO } from 'date-fns';
import { maltekstseksjonerQuerySlice } from '../maltekstseksjoner/queries';
import { textsQuerySlice } from './queries';
@@ -134,8 +134,6 @@ const textsMutationSlice = textsApi.injectEndpoints({
for (const version of draft) {
if (version.publishedDateTime === null && !isPlainText(version)) {
version.textType = newTextType;
-
- continue;
}
}
}),
diff --git a/frontend/src/redux-api/texts/queries.ts b/frontend/src/redux-api/texts/queries.ts
index dcbb618cc..e608c6299 100644
--- a/frontend/src/redux-api/texts/queries.ts
+++ b/frontend/src/redux-api/texts/queries.ts
@@ -1,7 +1,7 @@
import { IS_LOCALHOST } from '@app/redux-api/common';
import { TextsTagTypes, textsApi } from '@app/redux-api/texts/texts';
-import { IGetTextsParams } from '@app/types/texts/params';
-import { IText } from '@app/types/texts/responses';
+import type { IGetTextsParams } from '@app/types/texts/params';
+import type { IText } from '@app/types/texts/responses';
import { ListTagTypes } from '../tag-types';
const textsListTags = (texts: IText[] | undefined) =>
diff --git a/frontend/src/redux/configure-store.ts b/frontend/src/redux/configure-store.ts
index 647279af3..ac1746789 100644
--- a/frontend/src/redux/configure-store.ts
+++ b/frontend/src/redux/configure-store.ts
@@ -1,8 +1,6 @@
-/* eslint-disable import/no-unused-modules */
-import { configureStore } from '@reduxjs/toolkit';
-import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { accessRightsApi } from '@app/redux-api/access-rights';
import { brukerApi } from '@app/redux-api/bruker';
+import { collaborationApi } from '@app/redux-api/collaboration';
import { kabalInternalApi } from '@app/redux-api/internal';
import { journalposterApi } from '@app/redux-api/journalposter';
import { kvalitetsvurderingV1Api } from '@app/redux-api/kaka-kvalitetsvurdering/v1';
@@ -17,7 +15,10 @@ import { smartEditorCommentsApi } from '@app/redux-api/smart-editor-comments';
import { svarbrevApi } from '@app/redux-api/svarbrev';
import { consumerTextsApi } from '@app/redux-api/texts/consumer';
import { textsApi } from '@app/redux-api/texts/texts';
-import { RootState, rootReducer } from './root';
+/* eslint-disable import/no-unused-modules */
+import { configureStore } from '@reduxjs/toolkit';
+import { type TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
+import { type RootState, rootReducer } from './root';
export const reduxStore = configureStore({
reducer: rootReducer,
@@ -43,6 +44,7 @@ export const reduxStore = configureStore({
searchApi.middleware,
logiskeVedleggApi.middleware,
svarbrevApi.middleware,
+ collaborationApi.middleware,
]),
});
diff --git a/frontend/src/redux/root.ts b/frontend/src/redux/root.ts
index 93a58cf70..8639c6424 100644
--- a/frontend/src/redux/root.ts
+++ b/frontend/src/redux/root.ts
@@ -1,6 +1,6 @@
-import { combineReducers } from 'redux';
import { accessRightsApi } from '@app/redux-api/access-rights';
import { brukerApi } from '@app/redux-api/bruker';
+import { collaborationApi } from '@app/redux-api/collaboration';
import { kabalInternalApi } from '@app/redux-api/internal';
import { journalposterApi } from '@app/redux-api/journalposter';
import { kvalitetsvurderingV1Api } from '@app/redux-api/kaka-kvalitetsvurdering/v1';
@@ -15,6 +15,7 @@ import { smartEditorCommentsApi } from '@app/redux-api/smart-editor-comments';
import { svarbrevApi } from '@app/redux-api/svarbrev';
import { consumerTextsApi } from '@app/redux-api/texts/consumer';
import { textsApi } from '@app/redux-api/texts/texts';
+import { combineReducers } from 'redux';
export const rootReducer = combineReducers({
[oppgaverApi.reducerPath]: oppgaverApi.reducer,
@@ -33,6 +34,7 @@ export const rootReducer = combineReducers({
[searchApi.reducerPath]: searchApi.reducer,
[logiskeVedleggApi.reducerPath]: logiskeVedleggApi.reducer,
[svarbrevApi.reducerPath]: svarbrevApi.reducer,
+ [collaborationApi.reducerPath]: collaborationApi.reducer,
});
export type RootState = ReturnType;
diff --git a/frontend/src/simple-api-state/simple-api-state.ts b/frontend/src/simple-api-state/simple-api-state.ts
index 51b92e484..32d49a432 100644
--- a/frontend/src/simple-api-state/simple-api-state.ts
+++ b/frontend/src/simple-api-state/simple-api-state.ts
@@ -1,6 +1,6 @@
+import { getHeaders } from '@app/headers';
import { skipToken } from '@reduxjs/toolkit/query';
import { useEffect, useState } from 'react';
-import { getHeaders } from '@app/headers';
interface State {
data: T | undefined;
@@ -57,7 +57,7 @@ export class SimpleApiState {
const contentType = response.headers.get('content-type');
- if (contentType !== null && contentType.includes('application/json')) {
+ if (contentType?.includes('application/json')) {
this.data = (await response.json()) as T;
}
} catch (e) {
@@ -100,7 +100,7 @@ export class SimpleApiState {
listener(this.getState());
}
- if (!this.isLoading && !this.isError && typeof this.data === 'undefined') {
+ if (!(this.isLoading || this.isError) && typeof this.data === 'undefined') {
this.fetchData();
}
};
diff --git a/frontend/src/simple-api-state/use-kodeverk.ts b/frontend/src/simple-api-state/use-kodeverk.ts
index 37aced011..fc1f833f8 100644
--- a/frontend/src/simple-api-state/use-kodeverk.ts
+++ b/frontend/src/simple-api-state/use-kodeverk.ts
@@ -1,4 +1,4 @@
-import {
+import type {
IKabalYtelse,
IKlageenhet,
IKodeverkSimpleValue,
diff --git a/frontend/src/static-data/loader.ts b/frontend/src/static-data/loader.ts
index e9de1c186..83062d516 100644
--- a/frontend/src/static-data/loader.ts
+++ b/frontend/src/static-data/loader.ts
@@ -1,6 +1,6 @@
import { getHeaders } from '@app/headers';
-export const loadStaticData = async (url: string, name: string, attempt: number = 0): Promise => {
+export const loadStaticData = async (url: string, name: string, attempt = 0): Promise => {
const res = await fetch(url, {
method: 'GET',
headers: getHeaders(),
diff --git a/frontend/src/static-data/static-data.ts b/frontend/src/static-data/static-data.ts
index 535dd3f6e..f37e8de44 100644
--- a/frontend/src/static-data/static-data.ts
+++ b/frontend/src/static-data/static-data.ts
@@ -1,7 +1,7 @@
import { INNSTILLINGER_BASE_PATH, KABAL_API_BASE_PATH } from '@app/redux-api/common';
import { loadStaticData } from '@app/static-data/loader';
-import { IUserData } from '@app/types/bruker';
-import { CountryCode, PostalCode } from '@app/types/common';
+import type { IUserData } from '@app/types/bruker';
+import type { CountryCode, PostalCode } from '@app/types/common';
export const user = loadStaticData(`${INNSTILLINGER_BASE_PATH}/me/brukerdata`, 'brukerdata');
export const countryCodes = loadStaticData(`${KABAL_API_BASE_PATH}/landinfo`, 'landliste');
diff --git a/frontend/src/styled-components/labels.ts b/frontend/src/styled-components/labels.ts
index 64d1ac690..8834eec5d 100644
--- a/frontend/src/styled-components/labels.ts
+++ b/frontend/src/styled-components/labels.ts
@@ -1,6 +1,6 @@
+import { SaksTypeEnum } from '@app/types/kodeverk';
import { Tag } from '@navikt/ds-react';
import { styled } from 'styled-components';
-import { SaksTypeEnum } from '@app/types/kodeverk';
export const TypeTag = styled(Tag)<{ $type: SaksTypeEnum }>`
background-color: ${({ $type: type }) =>
diff --git a/frontend/src/types/common-text-types.ts b/frontend/src/types/common-text-types.ts
index 6c3b6fabd..9390f7baf 100644
--- a/frontend/src/types/common-text-types.ts
+++ b/frontend/src/types/common-text-types.ts
@@ -1,4 +1,4 @@
-import { Language, UNTRANSLATED } from '@app/types/texts/language';
+import type { Language, UNTRANSLATED } from '@app/types/texts/language';
export enum TextChangeType {
RICH_TEXT_NB = 'RICH_TEXT_NB',
diff --git a/frontend/src/types/documents/common-params.ts b/frontend/src/types/documents/common-params.ts
index 076f65f66..049354aa2 100644
--- a/frontend/src/types/documents/common-params.ts
+++ b/frontend/src/types/documents/common-params.ts
@@ -1,4 +1,4 @@
-import { IOppgavebehandlingBaseParams } from '../oppgavebehandling/params';
+import type { IOppgavebehandlingBaseParams } from '../oppgavebehandling/params';
export interface IDocumentParams extends IOppgavebehandlingBaseParams {
dokumentId: string;
diff --git a/frontend/src/types/documents/documents.ts b/frontend/src/types/documents/documents.ts
index 07270e549..e826f64e2 100644
--- a/frontend/src/types/documents/documents.ts
+++ b/frontend/src/types/documents/documents.ts
@@ -1,10 +1,10 @@
-import { EditorValue } from '@app/plate/types';
-import { INavEmployee } from '@app/types/bruker';
-import { HandlingEnum, IAddress } from '@app/types/documents/recipients';
-import { IJournalfoertDokumentId, IPart } from '@app/types/oppgave-common';
-import { Language } from '@app/types/texts/language';
-import { TemplateIdEnum } from '../smart-editor/template-enums';
-import { DokumentInfo, Journalpost } from './../arkiverte-documents';
+import type { EditorValue } from '@app/plate/types';
+import type { INavEmployee } from '@app/types/bruker';
+import type { HandlingEnum, IAddress } from '@app/types/documents/recipients';
+import type { IJournalfoertDokumentId, IPart } from '@app/types/oppgave-common';
+import type { Language } from '@app/types/texts/language';
+import type { TemplateIdEnum } from '../smart-editor/template-enums';
+import type { DokumentInfo, Journalpost } from './../arkiverte-documents';
export type UUID = string;
diff --git a/frontend/src/types/documents/params.ts b/frontend/src/types/documents/params.ts
index 4f7af143f..7390d8940 100644
--- a/frontend/src/types/documents/params.ts
+++ b/frontend/src/types/documents/params.ts
@@ -1,9 +1,9 @@
-import { IArkivertDocument } from '@app/types/arkiverte-documents';
-import { HandlingEnum, IAddress } from '@app/types/documents/recipients';
-import { INavEmployee } from '../bruker';
-import { IOppgavebehandlingBaseParams } from '../oppgavebehandling/params';
-import { IDocumentParams } from './common-params';
-import { CreatorRole, DistribusjonsType, IMottaker, UUID } from './documents';
+import type { IArkivertDocument } from '@app/types/arkiverte-documents';
+import type { HandlingEnum, IAddress } from '@app/types/documents/recipients';
+import type { INavEmployee } from '../bruker';
+import type { IOppgavebehandlingBaseParams } from '../oppgavebehandling/params';
+import type { IDocumentParams } from './common-params';
+import type { CreatorRole, DistribusjonsType, IMottaker, UUID } from './documents';
export interface ISetParentParams extends IDocumentParams {
parentId: UUID | null;
diff --git a/frontend/src/types/documents/response.ts b/frontend/src/types/documents/response.ts
index 3d9e1a3a9..597a6f589 100644
--- a/frontend/src/types/documents/response.ts
+++ b/frontend/src/types/documents/response.ts
@@ -1,5 +1,5 @@
-import { IJournalfoertDokumentReference } from '@app/types/documents/documents';
-import { IJournalfoertDokumentId } from '@app/types/oppgave-common';
+import type { IJournalfoertDokumentReference } from '@app/types/documents/documents';
+import type { IJournalfoertDokumentId } from '@app/types/oppgave-common';
export interface ICreateVedleggFromJournalfoertDocumentResponse {
addedJournalfoerteDokumenter: IJournalfoertDokumentReference[];
@@ -19,7 +19,3 @@ export interface ISetParentResponse extends IModifiedDocumentResponse {
export interface IModifiedDocumentResponse {
modified: string;
}
-
-export interface IModifiedSmartDocumentResponse extends IModifiedDocumentResponse {
- version: number;
-}
diff --git a/frontend/src/types/documents/validation.ts b/frontend/src/types/documents/validation.ts
index 29426c7c0..6b0114a17 100644
--- a/frontend/src/types/documents/validation.ts
+++ b/frontend/src/types/documents/validation.ts
@@ -1,4 +1,4 @@
-import { Path } from 'slate';
+import type { Path } from 'slate';
export enum DocumentValidationErrorType {
EMPTY_PLACEHOLDER = 'EMPTY_PLACEHOLDER',
diff --git a/frontend/src/types/errors.ts b/frontend/src/types/errors.ts
index acb11171c..119af7b8f 100644
--- a/frontend/src/types/errors.ts
+++ b/frontend/src/types/errors.ts
@@ -1,4 +1,4 @@
-import { FetchBaseQueryError } from '@reduxjs/toolkit/query';
+import type { FetchBaseQueryError } from '@reduxjs/toolkit/query';
import { isGenericObject } from './types';
export interface ApiError {
diff --git a/frontend/src/types/kaka-kvalitetsvurdering/v1.ts b/frontend/src/types/kaka-kvalitetsvurdering/v1.ts
index 700f99e0c..ded778c40 100644
--- a/frontend/src/types/kaka-kvalitetsvurdering/v1.ts
+++ b/frontend/src/types/kaka-kvalitetsvurdering/v1.ts
@@ -1,5 +1,5 @@
-import { IKvalitetsvurderingBase } from './common';
-import { Radiovalg, RadiovalgExtended } from './radio';
+import type { IKvalitetsvurderingBase } from './common';
+import type { Radiovalg, RadiovalgExtended } from './radio';
interface Version {
version: 1;
diff --git a/frontend/src/types/kaka-kvalitetsvurdering/v2.ts b/frontend/src/types/kaka-kvalitetsvurdering/v2.ts
index 4b0f4df96..d719d3cb1 100644
--- a/frontend/src/types/kaka-kvalitetsvurdering/v2.ts
+++ b/frontend/src/types/kaka-kvalitetsvurdering/v2.ts
@@ -1,5 +1,5 @@
-import { IKvalitetsvurderingBase } from './common';
-import { Radiovalg, RadiovalgExtended } from './radio';
+import type { IKvalitetsvurderingBase } from './common';
+import type { Radiovalg, RadiovalgExtended } from './radio';
interface SakensDokumenter {
klageforberedelsenSakensDokumenter: boolean; // Sakens dokumenter.
diff --git a/frontend/src/types/maltekstseksjoner/params.ts b/frontend/src/types/maltekstseksjoner/params.ts
index 3e45b4acd..ad637697d 100644
--- a/frontend/src/types/maltekstseksjoner/params.ts
+++ b/frontend/src/types/maltekstseksjoner/params.ts
@@ -1,5 +1,5 @@
-import { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
-import { IGetMaltekstseksjonParams, ITextBaseMetadata } from '../common-text-types';
+import type { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
+import type { IGetMaltekstseksjonParams, ITextBaseMetadata } from '../common-text-types';
export type { IGetMaltekstseksjonParams } from '../common-text-types';
diff --git a/frontend/src/types/maltekstseksjoner/responses.ts b/frontend/src/types/maltekstseksjoner/responses.ts
index cc3890d63..288e7b519 100644
--- a/frontend/src/types/maltekstseksjoner/responses.ts
+++ b/frontend/src/types/maltekstseksjoner/responses.ts
@@ -1,4 +1,4 @@
-import { IPublishedRichText } from '../texts/responses';
+import type { IPublishedRichText } from '../texts/responses';
enum MaltekstseksjonChangeType {
MALTEKSTSEKSJON_TITLE = 'MALTEKSTSEKSJON_TITLE',
diff --git a/frontend/src/types/oppgave-common.ts b/frontend/src/types/oppgave-common.ts
index e5554ea0b..a8fd4c6cd 100644
--- a/frontend/src/types/oppgave-common.ts
+++ b/frontend/src/types/oppgave-common.ts
@@ -1,6 +1,6 @@
-import { IAddress } from '@app/types/documents/recipients';
-import { SexEnum } from '@app/types/kodeverk';
-import { INavEmployee } from './bruker';
+import type { IAddress } from '@app/types/documents/recipients';
+import type { SexEnum } from '@app/types/kodeverk';
+import type { INavEmployee } from './bruker';
export interface IJournalfoertDokumentId {
readonly journalpostId: string;
diff --git a/frontend/src/types/oppgavebehandling/oppgavebehandling.ts b/frontend/src/types/oppgavebehandling/oppgavebehandling.ts
index d8d6638da..fb25f4fc5 100644
--- a/frontend/src/types/oppgavebehandling/oppgavebehandling.ts
+++ b/frontend/src/types/oppgavebehandling/oppgavebehandling.ts
@@ -1,6 +1,6 @@
-import { INavEmployee } from '../bruker';
-import { SaksTypeEnum, UtfallEnum } from '../kodeverk';
-import { IMedunderskriverRol, IPart, ISakenGjelder, IVedlegg, IVenteperiode } from '../oppgave-common';
+import type { INavEmployee } from '../bruker';
+import type { SaksTypeEnum, UtfallEnum } from '../kodeverk';
+import type { IMedunderskriverRol, IPart, ISakenGjelder, IVedlegg, IVenteperiode } from '../oppgave-common';
type UUID = string;
diff --git a/frontend/src/types/oppgavebehandling/params.ts b/frontend/src/types/oppgavebehandling/params.ts
index 92c54660a..94b1cfd87 100644
--- a/frontend/src/types/oppgavebehandling/params.ts
+++ b/frontend/src/types/oppgavebehandling/params.ts
@@ -1,6 +1,6 @@
-import { INavEmployee } from '@app/types/bruker';
-import { SaksTypeEnum, UtfallEnum } from '../kodeverk';
-import { FlowState, IJournalfoertDokumentId, IPart } from '../oppgave-common';
+import type { INavEmployee } from '@app/types/bruker';
+import type { SaksTypeEnum, UtfallEnum } from '../kodeverk';
+import type { FlowState, IJournalfoertDokumentId, IPart } from '../oppgave-common';
export interface IOppgavebehandlingBaseParams {
oppgaveId: string;
diff --git a/frontend/src/types/oppgavebehandling/response.ts b/frontend/src/types/oppgavebehandling/response.ts
index a1f6f4128..19670fbd0 100644
--- a/frontend/src/types/oppgavebehandling/response.ts
+++ b/frontend/src/types/oppgavebehandling/response.ts
@@ -1,10 +1,10 @@
/* eslint-disable import/no-unused-modules */
-import { INavEmployee } from '@app/types/bruker';
-import { UtfallEnum } from '@app/types/kodeverk';
-import { FlowState, IMedunderskriverRol, ISakenGjelder } from '@app/types/oppgave-common';
-import { IFeilregistrering } from '@app/types/oppgavebehandling/oppgavebehandling';
-import { FradelReason } from '@app/types/oppgaver';
-import { BehandlingstidUnitType } from '@app/types/svarbrev';
+import type { INavEmployee } from '@app/types/bruker';
+import type { UtfallEnum } from '@app/types/kodeverk';
+import type { FlowState, IMedunderskriverRol, ISakenGjelder } from '@app/types/oppgave-common';
+import type { IFeilregistrering } from '@app/types/oppgavebehandling/oppgavebehandling';
+import type { FradelReason } from '@app/types/oppgaver';
+import type { BehandlingstidUnitType } from '@app/types/svarbrev';
export type ITilknyttDocumentResponse = IModifiedResponse;
diff --git a/frontend/src/types/oppgaver.ts b/frontend/src/types/oppgaver.ts
index 904749c77..1129e9be1 100644
--- a/frontend/src/types/oppgaver.ts
+++ b/frontend/src/types/oppgaver.ts
@@ -1,6 +1,6 @@
-import { INavEmployee } from '@app/types/bruker';
-import { SaksTypeEnum, UtfallEnum } from '@app/types/kodeverk';
-import { IHelper, IPartBase, IVenteperiode } from '@app/types/oppgave-common';
+import type { INavEmployee } from '@app/types/bruker';
+import type { SaksTypeEnum, UtfallEnum } from '@app/types/kodeverk';
+import type { IHelper, IPartBase, IVenteperiode } from '@app/types/oppgave-common';
/** DateTime */
type DateString = string;
diff --git a/frontend/src/types/smart-editor/comments.ts b/frontend/src/types/smart-editor/comments.ts
index 6418363f1..2c8abad84 100644
--- a/frontend/src/types/smart-editor/comments.ts
+++ b/frontend/src/types/smart-editor/comments.ts
@@ -1,4 +1,4 @@
-import { IDocumentParams } from '../documents/common-params';
+import type { IDocumentParams } from '../documents/common-params';
export interface IDeleteCommentOrReplyParams extends IDocumentParams {
commentId: string;
diff --git a/frontend/src/types/smart-editor/metadata.ts b/frontend/src/types/smart-editor/metadata.ts
index b1ceb1200..b8777a1e6 100644
--- a/frontend/src/types/smart-editor/metadata.ts
+++ b/frontend/src/types/smart-editor/metadata.ts
@@ -1,5 +1,5 @@
-import { DistribusjonsType } from '../documents/documents';
-import { TemplateIdEnum } from './template-enums';
+import type { DistribusjonsType } from '../documents/documents';
+import type { TemplateIdEnum } from './template-enums';
export interface INewSmartEditorMetadata {
templateId: TemplateIdEnum;
diff --git a/frontend/src/types/smart-editor/params.ts b/frontend/src/types/smart-editor/params.ts
index 4d53c88c6..6c7e47196 100644
--- a/frontend/src/types/smart-editor/params.ts
+++ b/frontend/src/types/smart-editor/params.ts
@@ -1,17 +1,15 @@
-import { TDescendant } from '@udecode/plate-common';
-import { EditorValue } from '@app/plate/types';
-import { Language } from '@app/types/texts/language';
-import { Role } from '../bruker';
-import { IDocumentParams } from '../documents/common-params';
-import { DistribusjonsType } from '../documents/documents';
-import { IOppgavebehandlingBaseParams } from '../oppgavebehandling/params';
-import { Immutable } from '../types';
-import { TemplateIdEnum } from './template-enums';
+import type { Language } from '@app/types/texts/language';
+import type { TDescendant } from '@udecode/plate-common';
+import type { Role } from '../bruker';
+import type { DistribusjonsType } from '../documents/documents';
+import type { IOppgavebehandlingBaseParams } from '../oppgavebehandling/params';
+import type { Immutable } from '../types';
+import type { TemplateIdEnum } from './template-enums';
interface IMutableCreateSmartDocumentParams extends IOppgavebehandlingBaseParams {
tittel: string;
richText: TDescendant[];
- templateId: TemplateIdEnum | null;
+ templateId: TemplateIdEnum;
dokumentTypeId: DistribusjonsType;
parentId: string | null;
creatorIdent: string;
@@ -20,8 +18,3 @@ interface IMutableCreateSmartDocumentParams extends IOppgavebehandlingBaseParams
}
export type ICreateSmartDocumentParams = Immutable;
-
-export interface IUpdateSmartDocumentParams extends IDocumentParams {
- content: EditorValue;
- version: number;
-}
diff --git a/frontend/src/types/smart-editor/smart-editor.ts b/frontend/src/types/smart-editor/smart-editor.ts
index 05a600d43..e831cf42b 100644
--- a/frontend/src/types/smart-editor/smart-editor.ts
+++ b/frontend/src/types/smart-editor/smart-editor.ts
@@ -1,6 +1,6 @@
-import { EditorValue } from '@app/plate/types';
-import { GenericObject, Immutable } from '../types';
-import { INewSmartEditorMetadata } from './metadata';
+import type { EditorValue } from '@app/plate/types';
+import type { GenericObject, Immutable } from '../types';
+import type { INewSmartEditorMetadata } from './metadata';
interface INewSmartEditor extends INewSmartEditorMetadata {
richText: EditorValue;
diff --git a/frontend/src/types/svarbrev.ts b/frontend/src/types/svarbrev.ts
index df13c4109..f1849f4a4 100644
--- a/frontend/src/types/svarbrev.ts
+++ b/frontend/src/types/svarbrev.ts
@@ -1,5 +1,5 @@
-import { INavEmployee } from '@app/types/bruker';
-import { SaksTypeEnum } from '@app/types/kodeverk';
+import type { INavEmployee } from '@app/types/bruker';
+import type { SaksTypeEnum } from '@app/types/kodeverk';
export enum BehandlingstidUnitType {
WEEKS = '1',
diff --git a/frontend/src/types/texts/common.ts b/frontend/src/types/texts/common.ts
index f3a2a2b96..13f250e3c 100644
--- a/frontend/src/types/texts/common.ts
+++ b/frontend/src/types/texts/common.ts
@@ -1,6 +1,6 @@
-import { EditorValue } from '@app/plate/types';
+import type { EditorValue } from '@app/plate/types';
import { Language, UNTRANSLATED } from '@app/types/texts/language';
-import {
+import type {
GOD_FORMULERING_TYPE,
ITextBaseMetadata,
PlainTextTypes,
diff --git a/frontend/src/types/texts/consumer.ts b/frontend/src/types/texts/consumer.ts
index ba0647532..b91797f75 100644
--- a/frontend/src/types/texts/consumer.ts
+++ b/frontend/src/types/texts/consumer.ts
@@ -1,7 +1,12 @@
-import { EditorValue } from '@app/plate/types';
-import { GOD_FORMULERING_TYPE, PlainTextTypes, REGELVERK_TYPE, RichTextTypes } from '@app/types/common-text-types';
-import { Language } from '@app/types/texts/language';
-import { INewPlainTextParams, INewRegelverkParams, INewRichTextParams } from './common';
+import type { EditorValue } from '@app/plate/types';
+import {
+ GOD_FORMULERING_TYPE,
+ type PlainTextTypes,
+ type REGELVERK_TYPE,
+ type RichTextTypes,
+} from '@app/types/common-text-types';
+import type { Language } from '@app/types/texts/language';
+import type { INewPlainTextParams, INewRegelverkParams, INewRichTextParams } from './common';
interface ConsumerMetadata {
id: string;
diff --git a/frontend/src/types/texts/params.ts b/frontend/src/types/texts/params.ts
index a7e4d7a02..0f154136e 100644
--- a/frontend/src/types/texts/params.ts
+++ b/frontend/src/types/texts/params.ts
@@ -1,7 +1,7 @@
-import { EditorValue } from '@app/plate/types';
-import { IGetTextsParams, RichTextTypes } from '../common-text-types';
-import { Language, UNTRANSLATED } from './language';
-import { IText } from './responses';
+import type { EditorValue } from '@app/plate/types';
+import type { IGetTextsParams, RichTextTypes } from '../common-text-types';
+import type { Language, UNTRANSLATED } from './language';
+import type { IText } from './responses';
export type { IGetTextsParams } from '../common-text-types';
export type { INewPlainTextParams, INewRichTextParams, INewTextParams } from './common';
diff --git a/frontend/src/types/texts/responses-maltekster.ts b/frontend/src/types/texts/responses-maltekster.ts
index 4c74a4856..fa76f3a4a 100644
--- a/frontend/src/types/texts/responses-maltekster.ts
+++ b/frontend/src/types/texts/responses-maltekster.ts
@@ -1,4 +1,4 @@
-import { IMaltekstseksjon } from '../maltekstseksjoner/responses';
+import type { IMaltekstseksjon } from '../maltekstseksjoner/responses';
export interface IDeleteDraftOrUnpublishTextResponse {
maltekstseksjonVersions: {
diff --git a/frontend/src/types/texts/responses.ts b/frontend/src/types/texts/responses.ts
index f7d38ae3d..62a396a66 100644
--- a/frontend/src/types/texts/responses.ts
+++ b/frontend/src/types/texts/responses.ts
@@ -1,5 +1,5 @@
-import { DraftTextReadOnlyMetadata, PublishedTextReadOnlyMetadata } from '../common-text-types';
-import { INewGodFormuleringParams, INewPlainTextParams, INewRegelverkParams, INewRichTextParams } from './common';
+import type { DraftTextReadOnlyMetadata, PublishedTextReadOnlyMetadata } from '../common-text-types';
+import type { INewGodFormuleringParams, INewPlainTextParams, INewRegelverkParams, INewRichTextParams } from './common';
export type IDraftRichText = INewRichTextParams & DraftTextReadOnlyMetadata;
type IDraftRegelverk = INewRegelverkParams & DraftTextReadOnlyMetadata;
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 1b5c99d64..b2a0d46eb 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -24,13 +24,11 @@
/* Paths */
"baseUrl": "./src",
"paths": {
- "@app/*": [
- "*"
- ]
+ "@app/*": ["*"]
},
"incremental": true,
- "tsBuildInfoFile": ".tsbuildinfo",
+ "tsBuildInfoFile": ".tsbuildinfo"
},
- "include": ["src"],
+ "include": ["src"]
}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index b574b3b58..320983a1d 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,6 +1,11 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
-import tsconfigPaths from 'vite-tsconfig-paths'
+import react from '@vitejs/plugin-react';
+import { defineConfig } from 'vite';
+import tsconfigPaths from 'vite-tsconfig-paths';
+
+const PROXY = {
+ target: 'https://kabal.intern.dev.nav.no',
+ changeOrigin: true,
+};
// https://vitejs.dev/config/
export default defineConfig({
@@ -11,13 +16,13 @@ export default defineConfig({
server: {
port: 8061,
proxy: {
- '/api': 'https://kabal.intern.dev.nav.no',
- '/arkivert-dokument': 'https://kabal.intern.dev.nav.no',
- '/kombinert-dokument': 'https://kabal.intern.dev.nav.no',
- '/nytt-dokument': 'https://kabal.intern.dev.nav.no',
- '/vedleggsoversikt': 'https://kabal.intern.dev.nav.no',
- '/version': 'https://kabal.intern.dev.nav.no',
+ '/api': PROXY,
+ '/collaboration': { ...PROXY, ws: true },
+ '/arkivert-dokument': PROXY,
+ '/kombinert-dokument': PROXY,
+ '/nytt-dokument': PROXY,
+ '/vedleggsoversikt': PROXY,
+ '/version': PROXY,
},
},
-})
-
+});
diff --git a/nais/nais.yaml b/nais/nais.yaml
index c90416284..591088b54 100644
--- a/nais/nais.yaml
+++ b/nais/nais.yaml
@@ -27,7 +27,7 @@ spec:
sidecar:
enabled: true
autoLogin: true
- autoLoginIgnorePaths:
+ autoLoginIgnorePaths:
- /assets/*
- /*.js.map
- /*.js
@@ -65,6 +65,8 @@ spec:
redis:
- instance: obo-cache
access: readwrite
+ - instance: hocuspocus
+ access: readwrite
liveness:
path: /isAlive
initialDelay: 3
diff --git a/server/.vscode/Kabal (backend for frontend).code-workspace b/server/.vscode/Kabal (backend for frontend).code-workspace
index e403da67e..0f43cdf45 100644
--- a/server/.vscode/Kabal (backend for frontend).code-workspace
+++ b/server/.vscode/Kabal (backend for frontend).code-workspace
@@ -5,4 +5,4 @@
"name": "Kabal (backend for frontend)"
}
]
-}
\ No newline at end of file
+}
diff --git a/server/.vscode/launch.json b/server/.vscode/launch.json
new file mode 100644
index 000000000..757b661a5
--- /dev/null
+++ b/server/.vscode/launch.json
@@ -0,0 +1,18 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Launch Program",
+ "skipFiles": [
+ "/**"
+ ],
+ "program": "${workspaceFolder}/dist/server.js",
+ "cwd": "${workspaceFolder}",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/server/.vscode/settings.json b/server/.vscode/settings.json
index 7462b8c5f..d1e74d186 100644
--- a/server/.vscode/settings.json
+++ b/server/.vscode/settings.json
@@ -1,17 +1,14 @@
{
- "eslint.validate": [
- "javascript",
- "typescript"
- ],
- "eslint.run": "onType",
- "eslint.codeActionsOnSave.mode": "all",
"editor.formatOnSave": true,
- "eslint.format.enable": true,
"editor.linkedEditing": true,
- "editor.defaultFormatter": "dbaeumer.vscode-eslint",
+ "editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": "explicit"
+ "quickfix.biome": "always",
+ "source.fixAll.biome": "always",
+ "source.organizeImports.biome": "always",
+ "source.addMissingImports.ts": "explicit"
},
+ "editor.acceptSuggestionOnCommitCharacter": false,
"typescript.validate.enable": true,
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.importModuleSpecifier": "non-relative",
@@ -25,15 +22,16 @@
"liveshare.allowGuestDebugControl": true,
"liveshare.allowGuestTaskControl": true,
"liveshare.notebooks.allowGuestExecuteCells": true,
- "tslint.enable": false,
- "prettier.requireConfig": true,
- "[jsonc]": {
- "editor.defaultFormatter": "vscode.json-language-features"
+ "[typescript]": {
+ "editor.defaultFormatter": "biomejs.biome"
+ },
+ "[javascript]": {
+ "editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
- "editor.defaultFormatter": "vscode.json-language-features"
+ "editor.defaultFormatter": "biomejs.biome"
},
- "[html]": {
- "editor.defaultFormatter": "vscode.html-language-features"
+ "[jsonc]": {
+ "editor.defaultFormatter": "biomejs.biome"
}
-}
\ No newline at end of file
+}
diff --git a/server/bun.lockb b/server/bun.lockb
index aca33b27a..a8d2ef076 100755
Binary files a/server/bun.lockb and b/server/bun.lockb differ
diff --git a/server/eslint.config.js b/server/eslint.config.js
deleted file mode 100644
index ea395a2c4..000000000
--- a/server/eslint.config.js
+++ /dev/null
@@ -1,214 +0,0 @@
-import globals from "globals";
-import pluginJs from "@eslint/js";
-import tseslint from "typescript-eslint";
-import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
-
-export default [{
- languageOptions: {
- globals: globals.node,
- parserOptions: {
- project: "./tsconfig.json"
- }
-
- }
-},
-pluginJs.configs.recommended,
-...tseslint.configs.recommended,
- eslintPluginPrettierRecommended,
-{
- "rules": {
- "@typescript-eslint/array-type": "error",
- "@typescript-eslint/consistent-type-definitions": [
- "error",
- "interface"
- ],
- "@typescript-eslint/consistent-type-imports": [
- "error",
- {
- "prefer": "no-type-imports"
- }
- ],
- "@typescript-eslint/explicit-module-boundary-types": "off",
- "@typescript-eslint/no-base-to-string": "error",
- "@typescript-eslint/no-empty-function": "off",
- "@typescript-eslint/no-shadow": [
- "error"
- ],
- "@typescript-eslint/no-unsafe-assignment": "error",
- "@typescript-eslint/no-unused-vars": [
- "error",
- {
- "args": "all",
- "argsIgnorePattern": "^_",
- "caughtErrors": "all",
- "caughtErrorsIgnorePattern": "^_",
- "destructuredArrayIgnorePattern": "^_",
- "ignoreRestSiblings": true
- }
- ],
- "@typescript-eslint/no-var-requires": 0,
- "@typescript-eslint/prefer-includes": "error",
- "@typescript-eslint/prefer-nullish-coalescing": "error",
- "@typescript-eslint/prefer-reduce-type-parameter": "error",
- "@typescript-eslint/restrict-template-expressions": [
- "error",
- {
- "allowNumber": true
- }
- ],
- "@typescript-eslint/strict-boolean-expressions": "error",
- "@typescript-eslint/switch-exhaustiveness-check": "error",
- "array-callback-return": "error",
- "arrow-body-style": [
- "error",
- "as-needed"
- ],
- "comma-dangle": [
- 0,
- "never"
- ],
- "complexity": [
- "error",
- {
- "max": 20
- }
- ],
- "curly": [
- "error",
- "all"
- ],
- "eol-last": [
- "error",
- "always"
- ],
- "eqeqeq": "error",
- "keyword-spacing": [
- "error",
- {
- "before": true
- }
- ],
- "linebreak-style": [
- "error",
- "unix"
- ],
- "max-depth": [
- "error",
- {
- "max": 4
- }
- ],
- "max-lines": [
- "error",
- {
- "max": 400,
- "skipBlankLines": false,
- "skipComments": true
- }
- ],
- "no-alert": "error",
- "no-console": [
- "error",
- {
- "allow": [
- "warn",
- "error",
- "info",
- "debug"
- ]
- }
- ],
- "no-debugger": "error",
- "no-duplicate-imports": "error",
- "no-else-return": "error",
- "no-eval": "error",
- "no-extend-native": "error",
- "no-implicit-coercion": "error",
- "no-lonely-if": "error",
- "no-nested-ternary": "error",
- "no-proto": "error",
- "no-restricted-globals": [
- "error",
- {
- "name": "event",
- "message": "Use local parameter instead."
- }
- ],
- "no-return-assign": [
- "error",
- "always"
- ],
- "no-shadow": "off",
- "no-unneeded-ternary": "error",
- "no-unused-vars": "off",
- "no-useless-rename": "error",
- "no-useless-return": "error",
- "no-var": "error",
- "object-shorthand": [
- "error",
- "always"
- ],
- "padded-blocks": [
- "error",
- "never"
- ],
- "padding-line-between-statements": [
- "error",
- {
- "blankLine": "always",
- "prev": "*",
- "next": [
- "block",
- "block-like",
- "return"
- ]
- },
- {
- "blankLine": "never",
- "prev": "*",
- "next": [
- "case",
- "default"
- ]
- }
- ],
- "prefer-const": "error",
- "prefer-destructuring": "error",
- "prefer-object-spread": "error",
- "prefer-rest-params": "error",
- "prefer-spread": "error",
- "prettier/prettier": [
- "error",
- {
- "semi": true,
- "singleQuote": true,
- "printWidth": 120,
- "tabWidth": 2,
- "endOfLine": "lf"
- }
- ],
- "radix": [
- "error",
- "always"
- ],
- "sort-imports": [
- "error",
- {
- "ignoreDeclarationSort": true
- }
- ],
- "space-before-blocks": [
- "error",
- {
- "functions": "always",
- "keywords": "always",
- "classes": "always"
- }
- ],
- "yoda": [
- "error",
- "never"
- ]
- }
-}
-];
\ No newline at end of file
diff --git a/server/package.json b/server/package.json
index 5b7fda148..ebc23361b 100644
--- a/server/package.json
+++ b/server/package.json
@@ -9,30 +9,31 @@
"type": "module",
"scripts": {
"start": "bun run build --watch & node --watch --trace-warnings dist/server.js",
- "build": "bun build ./src/server.ts --target node --format esm --sourcemap --outdir dist",
- "lint": "eslint ./src/**/*.ts --color --cache --cache-strategy content --cache-location .eslintcache",
+ "build": "bun build ./src/server.ts --target node --format esm --sourcemap=external --outdir dist",
+ "lint": "biome check",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@fastify/cors": "9.0.1",
"@fastify/http-proxy": "9.5.0",
- "@fastify/type-provider-typebox": "4.0.0",
- "@types/node": "20.14.12",
- "fastify": "4.28.1",
+ "@fastify/type-provider-typebox": "4.1.0",
+ "@hocuspocus/extension-redis": "2.13.5",
+ "@hocuspocus/server": "2.13.5",
+ "@slate-yjs/core": "1.0.2",
+ "@types/node": "22.5.4",
"fastify-metrics": "11.0.0",
- "jose": "5.6.3",
- "openid-client": "5.6.5",
+ "fastify": "4.28.1",
+ "jose": "5.8.0",
+ "openid-client": "5.7.0",
"prom-client": "15.1.3",
- "redis": "^4.6.15"
+ "redis": "4.7.0"
},
"devDependencies": {
- "@eslint/js": "9.7.0",
- "@types/bun": "1.1.6",
- "eslint": "8.57.0",
- "eslint-config-prettier": "9.1.0",
- "eslint-plugin-prettier": "5.2.1",
- "globals": "15.8.0",
- "typescript": "5.5.4",
- "typescript-eslint": "7.17.0"
+ "@biomejs/biome": "^1.8.3",
+ "@types/bun": "1.1.9",
+ "@types/uuid": "10.0.0",
+ "globals": "15.9.0",
+ "typescript": "5.6.2",
+ "typescript-eslint": "8.5.0"
}
}
diff --git a/server/src/auth/cache/memory-cache.ts b/server/src/auth/cache/memory-cache.ts
index a33520f63..ef09d11cd 100644
--- a/server/src/auth/cache/memory-cache.ts
+++ b/server/src/auth/cache/memory-cache.ts
@@ -1,5 +1,5 @@
import { memoryCacheGauge, memoryCacheSizeGauge } from '@app/auth/cache/cache-gauge';
-import { TokenMessage } from '@app/auth/cache/types';
+import type { TokenMessage } from '@app/auth/cache/types';
import { getLogger } from '@app/logger';
const log = getLogger('obo-memory-cache');
diff --git a/server/src/auth/cache/redis-cache.ts b/server/src/auth/cache/redis-cache.ts
index b70a9bca6..8d47539b6 100644
--- a/server/src/auth/cache/redis-cache.ts
+++ b/server/src/auth/cache/redis-cache.ts
@@ -1,7 +1,7 @@
-import { RedisClientType, createClient } from 'redis';
-import { getLogger } from '@app/logger';
import { memoryCacheGauge, redisCacheGauge, redisCacheSizeGauge } from '@app/auth/cache/cache-gauge';
-import { TokenMessage } from '@app/auth/cache/types';
+import type { TokenMessage } from '@app/auth/cache/types';
+import { getLogger } from '@app/logger';
+import { type RedisClientType, createClient } from 'redis';
const log = getLogger('obo-redis-cache');
diff --git a/server/src/auth/get-auth-client.ts b/server/src/auth/get-auth-client.ts
index 002d98c5b..06ef64cca 100644
--- a/server/src/auth/get-auth-client.ts
+++ b/server/src/auth/get-auth-client.ts
@@ -1,7 +1,7 @@
-import { BaseClient, Issuer } from 'openid-client';
import { AZURE_APP_CLIENT_ID, AZURE_APP_JWK, AZURE_APP_WELL_KNOWN_URL } from '@app/config/config';
-import { getLogger } from '@app/logger';
import { isLocal } from '@app/config/env';
+import { getLogger } from '@app/logger';
+import { type BaseClient, Issuer } from 'openid-client';
const log = getLogger('auth-client');
diff --git a/server/src/auth/on-behalf-of.ts b/server/src/auth/on-behalf-of.ts
index 046e497c4..7f2da5253 100644
--- a/server/src/auth/on-behalf-of.ts
+++ b/server/src/auth/on-behalf-of.ts
@@ -1,8 +1,8 @@
-import { Client, GrantBody } from 'openid-client';
+import { createHash } from 'node:crypto';
+import { oboCache } from '@app/auth/cache/cache';
import { AZURE_APP_CLIENT_ID, NAIS_CLUSTER_NAME } from '@app/config/config';
import { getLogger } from '@app/logger';
-import { oboCache } from '@app/auth/cache/cache';
-import { createHash } from 'node:crypto';
+import type { Client, GrantBody } from 'openid-client';
const log = getLogger('obo-token');
diff --git a/server/src/config/config.ts b/server/src/config/config.ts
index f2696e613..ac6c76ade 100644
--- a/server/src/config/config.ts
+++ b/server/src/config/config.ts
@@ -1,7 +1,7 @@
import path from 'path';
-import { JWK } from 'jose';
-import { requiredEnvJson, requiredEnvString } from '@app/config/env-var';
import { isLocal } from '@app/config/env';
+import { requiredEnvJson, requiredEnvString } from '@app/config/env-var';
+import type { JWK } from 'jose';
export const API_CLIENT_IDS = [
'kabal-api',
diff --git a/server/src/config/cors.ts b/server/src/config/cors.ts
index 849f840da..677bd1bb8 100644
--- a/server/src/config/cors.ts
+++ b/server/src/config/cors.ts
@@ -1,5 +1,5 @@
-import { FastifyCorsOptions } from '@fastify/cors';
import { URL } from '@app/config/env';
+import type { FastifyCorsOptions } from '@fastify/cors';
export const corsOptions: FastifyCorsOptions = {
credentials: true,
diff --git a/server/src/config/env-var.ts b/server/src/config/env-var.ts
index 31253bb49..c65c95137 100644
--- a/server/src/config/env-var.ts
+++ b/server/src/config/env-var.ts
@@ -46,7 +46,7 @@ export const requiredEnvJson = (name: string, defaultValue?: T): T => {
export const requiredEnvNumber = (name: string, defaultValue?: number): number => {
const envString = optionalEnvString(name);
- const parsed = typeof envString === 'undefined' ? NaN : Number.parseInt(envString, 10);
+ const parsed = typeof envString === 'undefined' ? Number.NaN : Number.parseInt(envString, 10);
if (Number.isInteger(parsed)) {
return parsed;
diff --git a/server/src/functions/guards.ts b/server/src/functions/guards.ts
new file mode 100644
index 000000000..10b67188a
--- /dev/null
+++ b/server/src/functions/guards.ts
@@ -0,0 +1 @@
+export const isNotNull = (value: T | null): value is T => value !== null;
diff --git a/server/src/helpers/get-header-query.ts b/server/src/helpers/get-header-query.ts
index 3e4e0f8a5..213e61a7d 100644
--- a/server/src/helpers/get-header-query.ts
+++ b/server/src/helpers/get-header-query.ts
@@ -1,4 +1,4 @@
-import { FastifyRequest } from 'fastify';
+import type { FastifyRequest } from 'fastify';
export const TAB_ID_QUERY = 'tabId';
export const CLIENT_VERSION_QUERY = 'version';
diff --git a/server/src/helpers/mime-type.ts b/server/src/helpers/mime-type.ts
index 8c47b4835..b306e1806 100644
--- a/server/src/helpers/mime-type.ts
+++ b/server/src/helpers/mime-type.ts
@@ -20,6 +20,8 @@ export const getMimeType = (filePath: string): string | undefined => {
return 'image/svg+xml';
case 'ico':
return 'image/x-icon';
+ case 'map':
+ return 'application/json';
default:
return undefined;
}
diff --git a/server/src/helpers/prepare-request-headers.ts b/server/src/helpers/prepare-request-headers.ts
index ade324a86..b5bc49476 100644
--- a/server/src/helpers/prepare-request-headers.ts
+++ b/server/src/helpers/prepare-request-headers.ts
@@ -8,7 +8,7 @@ import {
TAB_ID_HEADER,
} from '@app/headers';
import { getLogger } from '@app/logger';
-import { FastifyRequest, RawServerBase, RequestGenericInterface } from 'fastify';
+import type { FastifyRequest, RawServerBase, RequestGenericInterface } from 'fastify';
const log = getLogger('prepare-proxy-request-headers');
@@ -43,7 +43,7 @@ export const getProxyRequestHeaders = (
headers[AUTHORIZATION_HEADER] = `Bearer ${oboAccessToken}`;
}
- log.info({
+ log.debug({
msg: 'Prepared proxy request headers',
tab_id,
trace_id,
diff --git a/server/src/init.ts b/server/src/init.ts
index a251c4b65..614733726 100644
--- a/server/src/init.ts
+++ b/server/src/init.ts
@@ -1,9 +1,9 @@
+import { getAzureADClient } from '@app/auth/get-auth-client';
import { isDeployed } from '@app/config/env';
+import { formatDuration, getDuration } from '@app/helpers/duration';
import { getLogger } from '@app/logger';
-import { EmojiIcons, sendToSlack } from '@app/slack';
-import { getAzureADClient } from '@app/auth/get-auth-client';
import { resetClientsAndUniqueUsersMetrics } from '@app/plugins/version/unique-users-gauge';
-import { formatDuration, getDuration } from '@app/helpers/duration';
+import { EmojiIcons, sendToSlack } from '@app/slack';
const log = getLogger('init');
diff --git a/server/src/logger.ts b/server/src/logger.ts
index 47c742a90..e99a8835e 100644
--- a/server/src/logger.ts
+++ b/server/src/logger.ts
@@ -22,7 +22,7 @@ export interface AnyObject {
[key: string]: SerializableValue;
}
-type LogArgs =
+export type LogArgs =
| {
msg?: string;
trace_id?: string;
@@ -60,7 +60,7 @@ interface Log extends AnyObject {
stacktrace?: string;
}
-type Level = 'debug' | 'info' | 'warn' | 'error';
+export type Level = 'debug' | 'info' | 'warn' | 'error';
export const getLogger = (module: string): Logger => {
const cachedLogger = LOGGERS.get(module);
diff --git a/server/src/plugins/access-token.ts b/server/src/plugins/access-token.ts
index 1a26e6996..65cb8ed22 100644
--- a/server/src/plugins/access-token.ts
+++ b/server/src/plugins/access-token.ts
@@ -1,5 +1,5 @@
import { AUTHORIZATION_HEADER } from '@app/headers';
-import { FastifyRequest } from 'fastify';
+import type { FastifyRequest } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
declare module 'fastify' {
diff --git a/server/src/plugins/api-proxy.ts b/server/src/plugins/api-proxy.ts
index 6bf8dfaed..a31d5ef46 100644
--- a/server/src/plugins/api-proxy.ts
+++ b/server/src/plugins/api-proxy.ts
@@ -1,11 +1,11 @@
-import proxy from '@fastify/http-proxy';
import { DEV_URL, isDeployed } from '@app/config/env';
+import { getDuration } from '@app/helpers/duration';
import { getProxyRequestHeaders } from '@app/helpers/prepare-request-headers';
import { getLogger } from '@app/logger';
-import { getDuration } from '@app/helpers/duration';
-import fastifyPlugin from 'fastify-plugin';
-import { SERVER_TIMING_HEADER, SERVER_TIMING_PLUGIN_ID } from '@app/plugins/server-timing';
import { OBO_ACCESS_TOKEN_PLUGIN_ID } from '@app/plugins/obo-token';
+import { SERVER_TIMING_HEADER, SERVER_TIMING_PLUGIN_ID } from '@app/plugins/server-timing';
+import proxy from '@fastify/http-proxy';
+import fastifyPlugin from 'fastify-plugin';
const log = getLogger('api-proxy');
@@ -37,7 +37,7 @@ export const apiProxyPlugin = fastifyPlugin(
const { method, url, trace_id, span_id, tab_id, client_version, proxyStartTime } = req;
const responseTime = getDuration(proxyStartTime);
- log.info({
+ log.debug({
msg: `Proxy response (${appName}) ${reply.statusCode} ${method} ${url} ${responseTime}ms`,
trace_id,
span_id,
@@ -65,7 +65,7 @@ export const apiProxyPlugin = fastifyPlugin(
websocket: true,
proxyPayloads: true,
preHandler: async (req, reply) => {
- log.info({
+ log.debug({
msg: `Proxy request (${appName}) ${req.method} ${req.url}`,
tab_id: req.tab_id,
trace_id: req.trace_id,
diff --git a/server/src/plugins/client-version.ts b/server/src/plugins/client-version.ts
index c633aebac..5ff4cc799 100644
--- a/server/src/plugins/client-version.ts
+++ b/server/src/plugins/client-version.ts
@@ -1,6 +1,6 @@
import { CLIENT_VERSION_HEADER } from '@app/headers';
import { CLIENT_VERSION_QUERY, getHeaderOrQueryValue } from '@app/helpers/get-header-query';
-import { FastifyRequest } from 'fastify';
+import type { FastifyRequest } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
declare module 'fastify' {
diff --git a/server/src/plugins/crdt/api/get-document.ts b/server/src/plugins/crdt/api/get-document.ts
new file mode 100644
index 000000000..354cf9fd6
--- /dev/null
+++ b/server/src/plugins/crdt/api/get-document.ts
@@ -0,0 +1,92 @@
+import { getLogger } from '@app/logger';
+import { getHeaders } from '@app/plugins/crdt/api/headers';
+import { KABAL_API_URL } from '@app/plugins/crdt/api/url';
+import { isObject } from '@app/plugins/crdt/functions';
+import { slateNodesToInsertDelta } from '@slate-yjs/core';
+import type { FastifyRequest } from 'fastify';
+import type { Node } from 'slate';
+import { Doc, XmlText, encodeStateAsUpdateV2 } from 'yjs';
+
+const log = getLogger('collaboration');
+
+export const getDocument = async (
+ req: FastifyRequest,
+ behandlingId: string,
+ dokumentId: string,
+): Promise => {
+ const res = await fetch(`${KABAL_API_URL}/behandlinger/${behandlingId}/dokumenter/${dokumentId}`, {
+ method: 'GET',
+ headers: getHeaders(req),
+ });
+
+ if (!res.ok) {
+ const msg = `Failed to fetch document. API responded with status code ${res.status}.`;
+ log.error({
+ msg,
+ trace_id: req.trace_id,
+ span_id: req.span_id,
+ tab_id: req.tab_id,
+ client_version: req.client_version,
+ data: { behandlingId, dokumentId, statusCode: res.status },
+ });
+ throw new Error(msg);
+ }
+
+ const json = await res.json();
+
+ if (!isDocumentResponse(json)) {
+ const msg = 'Invalid document response';
+ log.error({
+ msg,
+ trace_id: req.trace_id,
+ span_id: req.span_id,
+ tab_id: req.tab_id,
+ client_version: req.client_version,
+ data: { response: JSON.stringify(json) },
+ });
+
+ throw new Error(msg);
+ }
+
+ const { content, data } = json;
+
+ // If the document has no binary data, create and save it.
+ if (data === null || data.length === 0) {
+ const document = new Doc();
+ const sharedRoot = document.get('content', XmlText);
+ const insertDelta = slateNodesToInsertDelta(content);
+ sharedRoot.applyDelta(insertDelta);
+ const state = encodeStateAsUpdateV2(document);
+ const base64data = Buffer.from(state).toString('base64');
+
+ // Save the binary data to the database.
+ await fetch(`${KABAL_API_URL}/behandlinger/${behandlingId}/smartdokumenter/${dokumentId}`, {
+ method: 'PATCH',
+ headers: { ...getHeaders(req), 'Content-Type': 'application/json' },
+ body: JSON.stringify({ content, data: base64data }),
+ });
+
+ // Return the document and binary data.
+ return { content, data: base64data };
+ }
+
+ return { content, data };
+};
+
+interface DocumentResponse {
+ content: Node[];
+ data: string;
+}
+
+interface ApiDocumentResponse {
+ content: Node[];
+ data: string | null;
+}
+
+export const isDocumentResponse = (data: unknown): data is ApiDocumentResponse =>
+ isObject(data) &&
+ 'isSmartDokument' in data &&
+ data.isSmartDokument === true &&
+ 'content' in data &&
+ Array.isArray(data.content) &&
+ 'data' in data;
diff --git a/server/src/plugins/crdt/api/headers.ts b/server/src/plugins/crdt/api/headers.ts
new file mode 100644
index 000000000..a329fc6c5
--- /dev/null
+++ b/server/src/plugins/crdt/api/headers.ts
@@ -0,0 +1,57 @@
+import { PROXY_VERSION } from '@app/config/config';
+import { isDeployed } from '@app/config/env';
+import { AZURE_AD_TOKEN_HEADER, CLIENT_VERSION_HEADER, PROXY_VERSION_HEADER, TAB_ID_HEADER } from '@app/headers';
+import { generateTraceparent } from '@app/helpers/traceparent';
+import type { FastifyRequest } from 'fastify';
+
+type GetHeadersFn = (req: FastifyRequest) => Record;
+
+const getDeployedHeaders: GetHeadersFn = (req) => {
+ const { accessToken, client_version, tab_id } = req;
+
+ return {
+ Authorization: `Bearer ${req.getOboAccessToken('kabal-api')}`,
+ Accept: 'application/json',
+ traceparent: req.traceparent,
+ [AZURE_AD_TOKEN_HEADER]: accessToken,
+ [PROXY_VERSION_HEADER]: PROXY_VERSION,
+ [CLIENT_VERSION_HEADER]: client_version,
+ [TAB_ID_HEADER]: tab_id,
+ };
+};
+
+const IGNORED_HEADERS = [
+ 'host',
+ 'origin',
+ 'connection',
+ 'upgrade',
+ 'sec-websocket-version',
+ 'sec-websocket-extensions',
+ 'sec-websocket-version',
+ 'sec-websocket-key',
+ 'content-length',
+];
+
+const getLocalHeaders: GetHeadersFn = (req) => {
+ const headers: Record = {
+ host: 'kabal.intern.dev.nav.no',
+ origin: 'https://kabal.intern.dev.nav.no',
+ };
+
+ for (const [key, value] of Object.entries(req.headers)) {
+ if (value !== undefined && !IGNORED_HEADERS.includes(key)) {
+ headers[key] = value;
+ }
+ }
+
+ if ('traceparent' in req.headers) {
+ return headers;
+ }
+
+ return {
+ ...headers,
+ traceparent: generateTraceparent(),
+ };
+};
+
+export const getHeaders: GetHeadersFn = isDeployed ? getDeployedHeaders : getLocalHeaders;
diff --git a/server/src/plugins/crdt/api/set-document.ts b/server/src/plugins/crdt/api/set-document.ts
new file mode 100644
index 000000000..36923592c
--- /dev/null
+++ b/server/src/plugins/crdt/api/set-document.ts
@@ -0,0 +1,45 @@
+import { getLogger } from '@app/logger';
+import { getHeaders } from '@app/plugins/crdt/api/headers';
+import { KABAL_API_URL } from '@app/plugins/crdt/api/url';
+import type { Document } from '@hocuspocus/server';
+import { yTextToSlateElement } from '@slate-yjs/core';
+import type { FastifyRequest } from 'fastify';
+import { XmlText, encodeStateAsUpdateV2 } from 'yjs';
+
+const log = getLogger('collaboration');
+
+export const setDocument = async (
+ req: FastifyRequest,
+ document: Document,
+ behandlingId: string,
+ dokumentId: string,
+) => {
+ const update = Buffer.from(encodeStateAsUpdateV2(document));
+ const data = update.toString('base64url');
+
+ const sharedRoot = document.get('content', XmlText);
+ const nodes = yTextToSlateElement(sharedRoot);
+
+ const res = await fetch(`${KABAL_API_URL}/behandlinger/${behandlingId}/smartdokumenter/${dokumentId}`, {
+ method: 'PATCH',
+ headers: { ...getHeaders(req), 'Content-Type': 'application/json' },
+ body: JSON.stringify({ content: nodes.children, data }),
+ });
+
+ if (!res.ok) {
+ const msg = `Failed to save document. API responded with status code ${res.status}.`;
+ const text = await res.text();
+ log.error({
+ msg,
+ trace_id: req.trace_id,
+ span_id: req.span_id,
+ tab_id: req.tab_id,
+ client_version: req.client_version,
+ data: { behandlingId, dokumentId, statusCode: res.status, response: text },
+ });
+
+ throw new Error(`${msg} - ${text}`);
+ }
+
+ return res;
+};
diff --git a/server/src/plugins/crdt/api/url.ts b/server/src/plugins/crdt/api/url.ts
new file mode 100644
index 000000000..afabac97b
--- /dev/null
+++ b/server/src/plugins/crdt/api/url.ts
@@ -0,0 +1,3 @@
+import { isDeployed } from '@app/config/env';
+
+export const KABAL_API_URL = isDeployed ? 'http://kabal-api' : 'https://kabal.intern.dev.nav.no/api/kabal-api';
diff --git a/server/src/plugins/crdt/collaboration-server.ts b/server/src/plugins/crdt/collaboration-server.ts
new file mode 100644
index 000000000..a7b93fd68
--- /dev/null
+++ b/server/src/plugins/crdt/collaboration-server.ts
@@ -0,0 +1,84 @@
+import { isDeployed } from '@app/config/env';
+import { isNotNull } from '@app/functions/guards';
+import { type Level, getLogger } from '@app/logger';
+import { getDocument } from '@app/plugins/crdt/api/get-document';
+import { setDocument } from '@app/plugins/crdt/api/set-document';
+import { type ConnectionContext, isConnectionContext } from '@app/plugins/crdt/context';
+import { getRedisExtension } from '@app/plugins/crdt/redis';
+import { Server } from '@hocuspocus/server';
+import { applyUpdateV2 } from 'yjs';
+
+const log = getLogger('collaboration');
+
+const logContext = (msg: string, context: ConnectionContext, level: Level = 'info') => {
+ log[level]({
+ msg,
+ trace_id: context.req.trace_id,
+ span_id: context.req.span_id,
+ tab_id: context.req.tab_id,
+ client_version: context.req.client_version,
+ data: { behandlingId: context.behandlingId, dokumentId: context.dokumentId },
+ });
+};
+
+export const collaborationServer = Server.configure({
+ onConnect: async ({ context }) => {
+ if (isConnectionContext(context)) {
+ // context.req.navIdent is not defined when server is run without Wonderwall (ie. locally).
+ logContext(`Collaboration connection established for ${context.req.navIdent}!`, context);
+ } else {
+ log.error({ msg: 'Tried to establish collaboration connection without context' });
+ throw new Error('Invalid context');
+ }
+ },
+
+ onDisconnect: async ({ context }) => {
+ if (isConnectionContext(context)) {
+ // req.navIdent is not defined locally.
+ logContext(`Collaboration connection closed for ${context.req.navIdent}!`, context);
+ } else {
+ log.error({ msg: 'Tried to close collaboration connection without context' });
+ throw new Error('Invalid context');
+ }
+ },
+
+ onLoadDocument: async ({ context, document }) => {
+ if (!isConnectionContext(context)) {
+ log.error({ msg: 'Tried to load document without context' });
+ throw new Error('Invalid context');
+ }
+
+ const { behandlingId, dokumentId, req } = context;
+
+ if (!document.isEmpty('content')) {
+ logContext('Document already loaded', context);
+
+ return document;
+ }
+
+ const res = await getDocument(req, behandlingId, dokumentId);
+
+ logContext('Loaded state/update', context);
+
+ const update = new Uint8Array(Buffer.from(res.data, 'base64url'));
+
+ applyUpdateV2(document, update);
+
+ logContext('Loaded state/update applied', context);
+ },
+
+ onStoreDocument: async ({ context, document }) => {
+ if (!isConnectionContext(context)) {
+ log.error({ msg: 'Tried to store document without context' });
+ throw new Error('Invalid context');
+ }
+
+ const { behandlingId, dokumentId, req } = context;
+
+ await setDocument(req, document, behandlingId, dokumentId);
+
+ logContext('Saved document to database', context);
+ },
+
+ extensions: isDeployed ? [getRedisExtension()].filter(isNotNull) : [],
+});
diff --git a/server/src/plugins/crdt/context.ts b/server/src/plugins/crdt/context.ts
new file mode 100644
index 000000000..2de660ba4
--- /dev/null
+++ b/server/src/plugins/crdt/context.ts
@@ -0,0 +1,11 @@
+import { isObject } from '@app/plugins/crdt/functions';
+import type { FastifyRequest } from 'fastify';
+
+export interface ConnectionContext {
+ readonly behandlingId: string;
+ readonly dokumentId: string;
+ readonly req: FastifyRequest;
+}
+
+export const isConnectionContext = (data: unknown): data is ConnectionContext =>
+ isObject(data) && 'behandlingId' in data && 'dokumentId' in data && 'req' in data;
diff --git a/server/src/plugins/crdt/crdt.ts b/server/src/plugins/crdt/crdt.ts
new file mode 100644
index 000000000..e3c909af5
--- /dev/null
+++ b/server/src/plugins/crdt/crdt.ts
@@ -0,0 +1,193 @@
+import type { IncomingMessage } from 'node:http';
+import { Socket } from 'node:net';
+import type { Duplex } from 'node:stream';
+import { isDeployed } from '@app/config/env';
+import { type AnyObject, type Level, type LogArgs, getLogger } from '@app/logger';
+import { ACCESS_TOKEN_PLUGIN_ID } from '@app/plugins/access-token';
+import { getHeaders } from '@app/plugins/crdt/api/headers';
+import { KABAL_API_URL } from '@app/plugins/crdt/api/url';
+import { collaborationServer } from '@app/plugins/crdt/collaboration-server';
+import type { ConnectionContext } from '@app/plugins/crdt/context';
+import { isObject } from '@app/plugins/crdt/functions';
+import { OBO_ACCESS_TOKEN_PLUGIN_ID } from '@app/plugins/obo-token';
+import { TAB_ID_PLUGIN_ID } from '@app/plugins/tab-id';
+import { TRACEPARENT_PLUGIN_ID } from '@app/plugins/traceparent/traceparent';
+import { Type, type TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
+import { slateNodesToInsertDelta } from '@slate-yjs/core';
+import fastifyPlugin from 'fastify-plugin';
+import type { FastifyRequest } from 'fastify/types/request';
+import WebSocket from 'ws';
+import * as Y from 'yjs';
+
+const UPGRADE_MAP: Map = new Map();
+const UPGRADE_TIMEOUT = 1_000;
+
+export const CRDT_PLUGIN_ID = 'crdt';
+
+const log = getLogger(CRDT_PLUGIN_ID);
+
+const logReq = (msg: string, req: FastifyRequest, data: AnyObject, level: Level = 'info', error?: unknown) => {
+ const { trace_id, span_id, tab_id, client_version } = req;
+ const body: LogArgs = { msg, trace_id, span_id, tab_id, client_version, data };
+
+ if (error !== undefined) {
+ body.error = error;
+ }
+
+ log[level](body);
+};
+
+export const crdtPlugin = fastifyPlugin(
+ (app, _, pluginDone) => {
+ app.withTypeProvider().post(
+ '/collaboration/behandlinger/:behandlingId/dokumenter',
+ {
+ schema: {
+ tags: ['collaboration'],
+ params: Type.Object({ behandlingId: Type.String() }),
+ body: {
+ content: Type.Array(Type.Object({})),
+ templateId: Type.String(),
+ tittel: Type.String(),
+ dokumentTypeId: Type.String(),
+ parentId: Type.String(),
+ language: Type.String(),
+ },
+ produces: ['application/json'],
+ },
+ },
+ async (req, reply) => {
+ const { behandlingId } = req.params;
+ logReq('Creating new collaboration document', req, { behandlingId });
+
+ if (isDeployed) {
+ await req.ensureOboAccessToken('kabal-api');
+ }
+
+ const { body } = req;
+
+ if (!isObject(body) || !('content' in body) || !Array.isArray(body.content)) {
+ logReq('Invalid request body', req, { behandlingId }, 'error');
+
+ return reply.status(400).send();
+ }
+
+ try {
+ const { content } = body;
+ const document = new Y.Doc();
+ const sharedRoot = document.get('content', Y.XmlText);
+ const insertDelta = slateNodesToInsertDelta(content);
+ sharedRoot.applyDelta(insertDelta);
+ const state = Y.encodeStateAsUpdateV2(document);
+ const data = Buffer.from(state).toString('base64');
+
+ logReq('Saving new document to database', req, { behandlingId, data });
+
+ const res = await fetch(`${KABAL_API_URL}/behandlinger/${behandlingId}/smartdokumenter`, {
+ method: 'POST',
+ headers: { 'content-type': 'application/json', ...getHeaders(req) },
+ body: JSON.stringify({ ...body, data }),
+ });
+
+ if (!res.ok) {
+ const msg = `Failed to save document. API responded with status code ${res.status}`;
+ logReq(msg, req, { behandlingId, statusCode: res.status }, 'error');
+ } else {
+ logReq('Saved new document to database', req, { behandlingId });
+ }
+
+ return reply.send(res);
+ } catch (error) {
+ logReq('Failed to save document', req, { behandlingId }, 'error', error);
+
+ return reply.status(500).send();
+ }
+ },
+ );
+
+ const wss = new WebSocket.Server({ noServer: true });
+
+ app.server.on('upgrade', (rawRequest, socket, head) => {
+ UPGRADE_MAP.set(rawRequest, { socket, head });
+
+ // Make sure the upgrade request is deleted, even if the handler does not.
+ setTimeout(() => {
+ if (UPGRADE_MAP.has(rawRequest)) {
+ log.warn({ msg: 'Upgrade request timed out' });
+ UPGRADE_MAP.delete(rawRequest);
+ }
+ }, UPGRADE_TIMEOUT);
+ });
+
+ app.withTypeProvider().get(
+ '/collaboration/behandlinger/:behandlingId/dokumenter/:dokumentId',
+ {
+ schema: {
+ tags: ['collaboration'],
+ params: Type.Object({ behandlingId: Type.String(), dokumentId: Type.String() }),
+ },
+ },
+ async (req, reply) => {
+ const upgradeData = UPGRADE_MAP.get(req.raw);
+ UPGRADE_MAP.delete(req.raw);
+
+ if (upgradeData === undefined) {
+ return reply.code(400).send('No upgrade data found');
+ }
+
+ const { behandlingId, dokumentId } = req.params;
+ logReq('Websocket connection init', req, { behandlingId, dokumentId });
+
+ if (isDeployed) {
+ const oboAccessToken = await req.ensureOboAccessToken('kabal-api');
+
+ if (oboAccessToken === undefined) {
+ const msg = 'Tried to authenticate collaboration connection without OBO access token';
+ logReq(msg, req, { behandlingId, dokumentId }, 'warn');
+
+ return reply.code(401).send('Unauthorized');
+ }
+ }
+
+ const { socket, head } = upgradeData;
+
+ try {
+ const webSocket = await new Promise((resolve, reject) => {
+ wss.handleUpgrade(req.raw, socket, head, (ws) => {
+ wss.emit('connection', socket, req.raw);
+
+ socket.on('error', (error) => {
+ app.log.error(error);
+ reject(error);
+ });
+
+ resolve(ws);
+ });
+ });
+
+ reply.raw.assignSocket(new Socket(socket));
+
+ reply.hijack();
+
+ logReq('Handing over connection to HocusPocus', req, { behandlingId, dokumentId });
+
+ collaborationServer.handleConnection(webSocket, req.raw, {
+ behandlingId,
+ dokumentId,
+ req,
+ } satisfies ConnectionContext);
+ } catch (e) {
+ reply.code(500).send(e instanceof Error ? e.message : 'Internal Server Error');
+ console.error(e);
+ }
+ },
+ );
+
+ pluginDone();
+ },
+ {
+ fastify: '4',
+ name: CRDT_PLUGIN_ID,
+ dependencies: [ACCESS_TOKEN_PLUGIN_ID, OBO_ACCESS_TOKEN_PLUGIN_ID, TRACEPARENT_PLUGIN_ID, TAB_ID_PLUGIN_ID],
+ },
+);
diff --git a/server/src/plugins/crdt/functions.ts b/server/src/plugins/crdt/functions.ts
new file mode 100644
index 000000000..35af4d454
--- /dev/null
+++ b/server/src/plugins/crdt/functions.ts
@@ -0,0 +1 @@
+export const isObject = (data: unknown): data is object => typeof data === 'object' && data !== null;
diff --git a/server/src/plugins/crdt/redis-extension/redis-extension.ts b/server/src/plugins/crdt/redis-extension/redis-extension.ts
new file mode 100644
index 000000000..646390383
--- /dev/null
+++ b/server/src/plugins/crdt/redis-extension/redis-extension.ts
@@ -0,0 +1,260 @@
+import { randomUUID } from 'node:crypto';
+import { getLogger } from '@app/logger';
+import type { RedisOptions } from '@app/plugins/crdt/redis-extension/types';
+import {
+ Debugger,
+ type Document,
+ type Extension,
+ type Hocuspocus,
+ IncomingMessage,
+ MessageReceiver,
+ OutgoingMessage,
+ type afterLoadDocumentPayload,
+ type afterStoreDocumentPayload,
+ type beforeBroadcastStatelessPayload,
+ type onAwarenessUpdatePayload,
+ type onChangePayload,
+ type onConfigurePayload,
+ type onDisconnectPayload,
+} from '@hocuspocus/server';
+import { type RedisClientType, createClient } from 'redis';
+
+const log = getLogger('redis-extension');
+
+export class RedisExtension implements Extension {
+ readonly #prefix = 'hocuspocus';
+ readonly #identifier = `host-${randomUUID()}`;
+ readonly #disconnectDelay = 1_000;
+ readonly #redisConnectionRetryDelay = 100;
+ readonly #redisTransactionOrigin = '__hocuspocus__redis__origin__';
+ readonly #pub: RedisClientType;
+ readonly #sub: RedisClientType;
+ readonly #messagePrefix: Buffer;
+ instance?: Hocuspocus;
+
+ #isReady = false;
+
+ public constructor(options: RedisOptions) {
+ log.debug({ msg: 'Creating RedisExtension' });
+
+ this.#pub = createClient(options);
+ this.#sub = this.#pub.duplicate();
+
+ this.#pub.on('error', (error) => log.error({ msg: 'Redis publish client error', error }));
+ this.#sub.on('error', (error) => log.error({ msg: 'Redis subscribe client error', error }));
+
+ this.#init();
+
+ const identifierBuffer = Buffer.from(this.#identifier, 'utf-8');
+ this.#messagePrefix = Buffer.concat([Buffer.from([identifierBuffer.length]), identifierBuffer]);
+ }
+
+ async #init() {
+ await Promise.all([this.#sub.connect(), this.#pub.connect()]);
+
+ this.#isReady = true;
+ }
+
+ async onConfigure({ instance }: onConfigurePayload) {
+ log.debug({ msg: 'Configuring RedisExtension' });
+ this.instance = instance;
+ }
+
+ #getKey = (documentName: string) => `${this.#prefix}:${documentName}`;
+
+ #encodeMessage = (message: Uint8Array) => Buffer.concat([this.#messagePrefix, Buffer.from(message)]);
+
+ #decodeMessage(buffer: Buffer): [string, Buffer] {
+ const [identifierLength] = buffer;
+
+ if (identifierLength === undefined) {
+ throw new Error('Invalid message received');
+ }
+
+ const messageStart = identifierLength + 1;
+ const identifier = buffer.toString('utf-8', 1, messageStart);
+
+ return [identifier, buffer.subarray(messageStart)];
+ }
+
+ public async afterLoadDocument(params: afterLoadDocumentPayload): Promise {
+ const { documentName, document } = params;
+
+ log.debug({ msg: `Subscribing to document: ${documentName}` });
+
+ if (!this.#isReady) {
+ log.warn({ msg: 'Redis is not ready', data: { document: documentName, method: 'afterLoadDocument' } });
+
+ await delay(this.#redisConnectionRetryDelay);
+
+ return this.afterLoadDocument(params);
+ }
+
+ // On document creation the node will connect to pub and sub channels
+ // for the document.
+ await this.#sub.subscribe(this.#getKey(documentName), async (msg) => this.#handleIncomingMessage(msg), true);
+
+ await Promise.all([
+ this.#publishFirstSyncStep(documentName, document),
+ this.#requestAwarenessFromOtherInstances(documentName),
+ ]);
+ }
+
+ async #handleIncomingMessage(data: Buffer) {
+ const [identifier, messageBuffer] = this.#decodeMessage(data);
+
+ if (identifier === this.#identifier) {
+ return;
+ }
+
+ const message = new IncomingMessage(messageBuffer);
+ const documentName = message.readVarString();
+ message.writeVarString(documentName);
+
+ if (this.instance === undefined) {
+ log.warn({
+ msg: 'HocusPocus instance is undefined',
+ data: { document: documentName, method: 'handleIncomingMessage' },
+ });
+
+ return;
+ }
+
+ const document = this.instance.documents.get(documentName);
+
+ if (document === undefined) {
+ // Received message for unknown document. Should not happen.
+ log.error({ msg: `Received message for unknown document ${documentName}` });
+
+ return;
+ }
+
+ new MessageReceiver(message, new Debugger(), this.#redisTransactionOrigin).apply(document, undefined, (reply) =>
+ this.#pub.publish(this.#getKey(document.name), this.#encodeMessage(reply)),
+ );
+ }
+
+ async #publishFirstSyncStep(documentName: string, document: Document): Promise {
+ if (!this.#isReady) {
+ log.warn({ msg: 'Redis is not ready', data: { document: documentName, method: 'publishFirstSyncStep' } });
+
+ await delay(this.#redisConnectionRetryDelay);
+
+ return this.#publishFirstSyncStep(documentName, document);
+ }
+
+ const syncMessage = new OutgoingMessage(documentName).createSyncMessage().writeFirstSyncStepFor(document);
+
+ return this.#pub.publish(this.#getKey(documentName), this.#encodeMessage(syncMessage.toUint8Array()));
+ }
+
+ async #requestAwarenessFromOtherInstances(documentName: string): Promise {
+ if (!this.#isReady) {
+ log.warn({
+ msg: 'Redis is not ready',
+ data: { document: documentName, method: 'requestAwarenessFromOtherInstances' },
+ });
+
+ await delay(this.#redisConnectionRetryDelay);
+
+ return this.#requestAwarenessFromOtherInstances(documentName);
+ }
+
+ const awarenessMessage = new OutgoingMessage(documentName).writeQueryAwareness();
+
+ return this.#pub.publish(this.#getKey(documentName), this.#encodeMessage(awarenessMessage.toUint8Array()));
+ }
+
+ async afterStoreDocument({ socketId }: afterStoreDocumentPayload) {
+ log.debug({ msg: `afterStoreDocument - socket: ${socketId}` });
+
+ // If the change was initiated by a directConnection, we need to delay this hook to make sure sync can finish first.
+ // For provider connections, this usually happens in the onDisconnect hook.
+ if (socketId === 'server' && this.#disconnectDelay > 0) {
+ await delay(this.#disconnectDelay);
+ }
+ }
+
+ async onAwarenessUpdate(params: onAwarenessUpdatePayload): Promise {
+ const { documentName, awareness, added, updated, removed } = params;
+ log.debug({ msg: `onAwarenessUpdate - document: ${documentName}` });
+
+ if (!this.#isReady) {
+ log.warn({ msg: 'Redis is not ready', data: { document: documentName, method: 'onAwarenessUpdate' } });
+
+ await delay(this.#redisConnectionRetryDelay);
+
+ return this.onAwarenessUpdate(params);
+ }
+
+ const changedClients = added.concat(updated, removed);
+ const message = new OutgoingMessage(documentName).createAwarenessUpdateMessage(awareness, changedClients);
+
+ return this.#pub.publish(this.#getKey(documentName), this.#encodeMessage(message.toUint8Array()));
+ }
+
+ public async onChange(data: onChangePayload): Promise {
+ log.debug({ msg: `onChange - document: ${data.documentName}` });
+
+ if (data.transactionOrigin === this.#redisTransactionOrigin) {
+ return;
+ }
+
+ this.#publishFirstSyncStep(data.documentName, data.document);
+ }
+
+ public async onDisconnect({ documentName }: onDisconnectPayload): Promise {
+ log.debug({ msg: `onDisconnect - document: ${documentName}` });
+
+ // Delay the disconnect procedure to allow last minute syncs to happen.
+ setTimeout(() => this.#disconnect(documentName), this.#disconnectDelay);
+ }
+
+ async #disconnect(documentName: string): Promise {
+ if (this.instance === undefined) {
+ log.warn({ msg: 'HocusPocus instance is undefined', data: { document: documentName, method: 'disconnect' } });
+
+ // Time to end the subscription on the document channel.
+ return await this.#sub.unsubscribe(this.#getKey(documentName));
+ }
+
+ const document = this.instance.documents.get(documentName);
+
+ // Do nothing, when other users are still connected to the document.
+ if (document === undefined || document.getConnectionsCount() > 0) {
+ return;
+ }
+
+ // Time to end the subscription on the document channel.
+ await this.#sub.unsubscribe(this.#getKey(documentName));
+
+ this.instance.unloadDocument(document);
+ }
+
+ public async beforeBroadcastStateless(data: beforeBroadcastStatelessPayload): Promise {
+ const { documentName } = data;
+
+ log.debug({ msg: `beforeBroadcastStateless - document: ${documentName}` });
+
+ if (!this.#isReady) {
+ log.warn({ msg: 'Redis is not ready', data: { document: documentName, method: 'beforeBroadcastStateless' } });
+
+ await delay(this.#redisConnectionRetryDelay);
+
+ return this.beforeBroadcastStateless(data);
+ }
+
+ const message = new OutgoingMessage(documentName).writeBroadcastStateless(data.payload);
+
+ this.#pub.publish(this.#getKey(documentName), this.#encodeMessage(message.toUint8Array()));
+ }
+
+ public async onDestroy() {
+ log.debug({ msg: 'Destroying RedisExtension' });
+
+ this.#pub.disconnect();
+ this.#sub.disconnect();
+ }
+}
+
+const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
diff --git a/server/src/plugins/crdt/redis-extension/types.ts b/server/src/plugins/crdt/redis-extension/types.ts
new file mode 100644
index 000000000..677bbc7f0
--- /dev/null
+++ b/server/src/plugins/crdt/redis-extension/types.ts
@@ -0,0 +1,17 @@
+export interface RedisOptions {
+ /**
+ * The URL of the Redis server.
+ * @example 'redis://localhost:6379'
+ */
+ readonly url: string;
+ /**
+ * The username for the Redis server.
+ * @example 'username'
+ */
+ readonly username?: string;
+ /**
+ * The password for the Redis server.
+ * @example 'password'
+ */
+ readonly password?: string;
+}
diff --git a/server/src/plugins/crdt/redis.ts b/server/src/plugins/crdt/redis.ts
new file mode 100644
index 000000000..e61b9113a
--- /dev/null
+++ b/server/src/plugins/crdt/redis.ts
@@ -0,0 +1,27 @@
+import { optionalEnvString } from '@app/config/env-var';
+import { getLogger } from '@app/logger';
+import { RedisExtension } from '@app/plugins/crdt/redis-extension/redis-extension';
+
+const log = getLogger('collaboration');
+
+const REDIS_URI = optionalEnvString('REDIS_URI_HOCUSPOCUS');
+const REDIS_USERNAME = optionalEnvString('REDIS_USERNAME_HOCUSPOCUS');
+const REDIS_PASSWORD = optionalEnvString('REDIS_PASSWORD_HOCUSPOCUS');
+
+export const getRedisExtension = () => {
+ const hasRedis = REDIS_URI !== undefined && REDIS_USERNAME !== undefined && REDIS_PASSWORD !== undefined;
+
+ if (!hasRedis) {
+ log.info({ msg: 'No collaboration Redis connection configured' });
+
+ return null;
+ }
+
+ log.info({ msg: 'Collaboration Redis connection configured' });
+
+ return new RedisExtension({
+ url: REDIS_URI,
+ username: REDIS_USERNAME,
+ password: REDIS_PASSWORD,
+ });
+};
diff --git a/server/src/plugins/document.ts b/server/src/plugins/document.ts
index dfd4d9439..7ddea7ecb 100644
--- a/server/src/plugins/document.ts
+++ b/server/src/plugins/document.ts
@@ -1,14 +1,14 @@
import fs from 'node:fs';
import path from 'node:path';
-import { getLogger } from '@app/logger';
-import { getProxyRequestHeaders } from '@app/helpers/prepare-request-headers';
-import { getDuration } from '@app/helpers/duration';
import { isDeployed } from '@app/config/env';
-import { FastifyReply, FastifyRequest } from 'fastify';
-import fastifyPlugin from 'fastify-plugin';
-import { Static, Type, TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
-import { SERVER_TIMING_HEADER, SERVER_TIMING_PLUGIN_ID } from '@app/plugins/server-timing';
+import { getDuration } from '@app/helpers/duration';
+import { getProxyRequestHeaders } from '@app/helpers/prepare-request-headers';
+import { getLogger } from '@app/logger';
import { OBO_ACCESS_TOKEN_PLUGIN_ID } from '@app/plugins/obo-token';
+import { SERVER_TIMING_HEADER, SERVER_TIMING_PLUGIN_ID } from '@app/plugins/server-timing';
+import { type Static, Type, type TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
+import type { FastifyReply, FastifyRequest } from 'fastify';
+import fastifyPlugin from 'fastify-plugin';
interface IBaseMetadata {
title: string;
diff --git a/server/src/plugins/http-logger.ts b/server/src/plugins/http-logger.ts
index 2972d4dde..ccc5adc1f 100644
--- a/server/src/plugins/http-logger.ts
+++ b/server/src/plugins/http-logger.ts
@@ -1,5 +1,5 @@
import { getDuration } from '@app/helpers/duration';
-import { AnyObject, getLogger } from '@app/logger';
+import { type AnyObject, getLogger } from '@app/logger';
import { PROXY_VERSION_PLUGIN_ID } from '@app/plugins/proxy-version';
import { SERVE_ASSETS_PLUGIN_ID } from '@app/plugins/serve-assets';
import { SERVE_INDEX_PLUGIN_ID } from '@app/plugins/serve-index';
diff --git a/server/src/plugins/obo-token.ts b/server/src/plugins/obo-token.ts
index c2ba2642f..d36bfe9d4 100644
--- a/server/src/plugins/obo-token.ts
+++ b/server/src/plugins/obo-token.ts
@@ -1,23 +1,23 @@
-import { FastifyReply, FastifyRequest } from 'fastify';
+import { oboRequestDuration } from '@app/auth/cache/cache-gauge';
import { getAzureADClient } from '@app/auth/get-auth-client';
-import { getDuration } from '@app/helpers/duration';
-import { getLogger } from '@app/logger';
import { getOnBehalfOfAccessToken } from '@app/auth/on-behalf-of';
-import fastifyPlugin from 'fastify-plugin';
import { isDeployed } from '@app/config/env';
-import { oboRequestDuration } from '@app/auth/cache/cache-gauge';
+import { getDuration } from '@app/helpers/duration';
+import { getLogger } from '@app/logger';
import { ACCESS_TOKEN_PLUGIN_ID } from '@app/plugins/access-token';
-import { SERVER_TIMING_PLUGIN_ID } from '@app/plugins/server-timing';
import { NAV_IDENT_PLUGIN_ID } from '@app/plugins/nav-ident';
+import { SERVER_TIMING_PLUGIN_ID } from '@app/plugins/server-timing';
+import type { FastifyReply, FastifyRequest } from 'fastify';
+import fastifyPlugin from 'fastify-plugin';
const log = getLogger('obo-token-plugin');
-const oboAccessTokenMapKey = Symbol('oboAccessTokenMap');
+const OBO_ACCESS_TOKEN_MAP_KEY = Symbol('oboAccessTokenMapKey');
declare module 'fastify' {
interface FastifyRequest {
- [oboAccessTokenMapKey]: Map;
- ensureOboAccessToken(appName: string, reply: FastifyReply): Promise;
+ [OBO_ACCESS_TOKEN_MAP_KEY]: Map;
+ ensureOboAccessToken(appName: string, reply?: FastifyReply): Promise;
getOboAccessToken(appName: string): string | undefined;
}
}
@@ -28,19 +28,19 @@ export const OBO_ACCESS_TOKEN_PLUGIN_ID = 'obo-access-token';
export const oboAccessTokenPlugin = fastifyPlugin(
(app, _, pluginDone) => {
- app.decorateRequest(oboAccessTokenMapKey, null);
+ app.decorateRequest('_oboAccessTokenMapKey', null);
app.addHook('onRequest', async (req): Promise => {
- req[oboAccessTokenMapKey] = new Map();
+ req[OBO_ACCESS_TOKEN_MAP_KEY] = new Map();
});
if (isDeployed) {
- app.decorateRequest('ensureOboAccessToken', async function (appName: string, reply: FastifyReply) {
- const oboAccessToken = await getOboToken(appName, this, reply);
+ app.decorateRequest('ensureOboAccessToken', async function (appName: string, reply?: FastifyReply) {
+ const oboAccessToken = this[OBO_ACCESS_TOKEN_MAP_KEY].get(appName) ?? (await getOboToken(appName, this, reply));
if (oboAccessToken !== undefined) {
log.debug({
- msg: `Adding OBO token for "${appName}". Had ${this[oboAccessTokenMapKey].size} before.`,
+ msg: `Adding OBO token for "${appName}". Had ${this[OBO_ACCESS_TOKEN_MAP_KEY].size} before.`,
trace_id: this.trace_id,
span_id: this.span_id,
tab_id: this.tab_id,
@@ -48,7 +48,7 @@ export const oboAccessTokenPlugin = fastifyPlugin(
data: { route: this.url },
});
- this[oboAccessTokenMapKey].set(appName, oboAccessToken);
+ this[OBO_ACCESS_TOKEN_MAP_KEY].set(appName, oboAccessToken);
}
return oboAccessToken;
@@ -59,7 +59,7 @@ export const oboAccessTokenPlugin = fastifyPlugin(
app.decorateRequest('getOboAccessToken', function (appName: string) {
log.debug({
- msg: `Getting OBO token for "${appName}". Has ${this[oboAccessTokenMapKey].size} tokens.`,
+ msg: `Getting OBO token for "${appName}". Has ${this[OBO_ACCESS_TOKEN_MAP_KEY].size} tokens.`,
trace_id: this.trace_id,
span_id: this.span_id,
tab_id: this.tab_id,
@@ -67,7 +67,7 @@ export const oboAccessTokenPlugin = fastifyPlugin(
data: { route: this.url },
});
- return this[oboAccessTokenMapKey].get(appName);
+ return this[OBO_ACCESS_TOKEN_MAP_KEY].get(appName);
});
pluginDone();
@@ -79,7 +79,7 @@ export const oboAccessTokenPlugin = fastifyPlugin(
},
);
-type GetOboToken = (appName: string, req: FastifyRequest, reply: FastifyReply) => Promise;
+type GetOboToken = (appName: string, req: FastifyRequest, reply?: FastifyReply) => Promise;
const getOboToken: GetOboToken = async (appName, req, reply) => {
const { trace_id, span_id, accessToken } = req;
@@ -91,14 +91,14 @@ const getOboToken: GetOboToken = async (appName, req, reply) => {
try {
const azureClientStart = performance.now();
const authClient = await getAzureADClient();
- reply.addServerTiming('azure_client_middleware', getDuration(azureClientStart), 'Azure Client Middleware');
+ reply?.addServerTiming('azure_client_middleware', getDuration(azureClientStart), 'Azure Client Middleware');
const oboStart = performance.now();
const oboAccessToken = await getOnBehalfOfAccessToken(authClient, accessToken, appName, trace_id, span_id);
const duration = getDuration(oboStart);
oboRequestDuration.observe(duration);
- reply.addServerTiming('obo_token_middleware', duration, 'OBO Token Middleware');
+ reply?.addServerTiming('obo_token_middleware', duration, 'OBO Token Middleware');
return oboAccessToken;
} catch (error) {
diff --git a/server/src/plugins/serve-assets.ts b/server/src/plugins/serve-assets.ts
index 821869b03..1f8e0c982 100644
--- a/server/src/plugins/serve-assets.ts
+++ b/server/src/plugins/serve-assets.ts
@@ -1,6 +1,6 @@
import { existsSync, readFileSync, readdirSync } from 'node:fs';
-import { getLogger } from '@app/logger';
import { getMimeType } from '@app/helpers/mime-type';
+import { getLogger } from '@app/logger';
import fastifyPlugin from 'fastify-plugin';
const log = getLogger('serve-assets');
diff --git a/server/src/plugins/serve-index.ts b/server/src/plugins/serve-index.ts
index fec9376e5..1de33d970 100644
--- a/server/src/plugins/serve-index.ts
+++ b/server/src/plugins/serve-index.ts
@@ -1,9 +1,9 @@
-import { getLogger } from '@app/logger';
import { existsSync, readFileSync } from 'node:fs';
import { PROXY_VERSION, frontendDistDirectoryPath } from '@app/config/config';
import { ENVIRONMENT } from '@app/config/env';
+import { getLogger } from '@app/logger';
+import type { RouteHandler } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
-import { RouteHandler } from 'fastify';
const log = getLogger('serve-index-file');
diff --git a/server/src/plugins/tab-id.ts b/server/src/plugins/tab-id.ts
index 22ed4634b..5b9328989 100644
--- a/server/src/plugins/tab-id.ts
+++ b/server/src/plugins/tab-id.ts
@@ -1,6 +1,6 @@
import { TAB_ID_HEADER } from '@app/headers';
import { TAB_ID_QUERY, getHeaderOrQueryValue } from '@app/helpers/get-header-query';
-import { FastifyRequest } from 'fastify';
+import type { FastifyRequest } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
declare module 'fastify' {
diff --git a/server/src/plugins/traceparent/traceparent.ts b/server/src/plugins/traceparent/traceparent.ts
index 22bcc75bc..fc37494bf 100644
--- a/server/src/plugins/traceparent/traceparent.ts
+++ b/server/src/plugins/traceparent/traceparent.ts
@@ -4,7 +4,7 @@ import {
generateTraceparent,
getTraceIdAndSpanIdFromTraceparent,
} from '@app/helpers/traceparent';
-import { FastifyRequest } from 'fastify';
+import type { FastifyRequest } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
declare module 'fastify' {
diff --git a/server/src/plugins/version/session-histogram.ts b/server/src/plugins/version/session-histogram.ts
index a0346f3a0..05c7f6dbb 100644
--- a/server/src/plugins/version/session-histogram.ts
+++ b/server/src/plugins/version/session-histogram.ts
@@ -1,5 +1,5 @@
-import { Histogram } from 'prom-client';
import { proxyRegister } from '@app/prometheus/types';
+import { Histogram } from 'prom-client';
export const histogram = new Histogram({
name: 'session_duration_seconds',
diff --git a/server/src/plugins/version/unique-users-gauge.ts b/server/src/plugins/version/unique-users-gauge.ts
index 2b397f020..71a8c7ea8 100644
--- a/server/src/plugins/version/unique-users-gauge.ts
+++ b/server/src/plugins/version/unique-users-gauge.ts
@@ -1,8 +1,8 @@
-import { Gauge, LabelValues } from 'prom-client';
import { PROXY_VERSION, START_TIME } from '@app/config/config';
import { getLogger } from '@app/logger';
import { proxyRegister } from '@app/prometheus/types';
-import { FastifyRequest } from 'fastify';
+import type { FastifyRequest } from 'fastify';
+import { Gauge, type LabelValues } from 'prom-client';
const log = getLogger('active-clients');
diff --git a/server/src/plugins/version/update-request.ts b/server/src/plugins/version/update-request.ts
index 47e5d0531..2d0bdd40a 100644
--- a/server/src/plugins/version/update-request.ts
+++ b/server/src/plugins/version/update-request.ts
@@ -1,14 +1,14 @@
import { PROXY_VERSION } from '@app/config/config';
import { getLogger } from '@app/logger';
-import { FastifyRequest } from 'fastify';
+import type { FastifyRequest } from 'fastify';
const log = getLogger('update-request');
/** Threshold for when client is required to update.
* @format `YYYY-mm-ddTHH:MM:ss`
*/
-const UPDATE_REQUIRED_THRESHOLD: `${string}-${string}-${string}T${string}:${string}:${string}` = '2024-06-10T13:37:00';
-const UPDATE_OPTIONAL_THRESHOLD: `${string}-${string}-${string}T${string}:${string}:${string}` = '2024-09-05T11:15:00';
+const UPDATE_REQUIRED_THRESHOLD: `${string}-${string}-${string}T${string}:${string}:${string}` = '2024-09-10T14:15:00';
+const UPDATE_OPTIONAL_THRESHOLD: `${string}-${string}-${string}T${string}:${string}:${string}` = '2024-09-10T14:15:00';
if (UPDATE_REQUIRED_THRESHOLD > PROXY_VERSION) {
log.error({
diff --git a/server/src/process-errors.ts b/server/src/process-errors.ts
index 7294b6481..2f9337ece 100644
--- a/server/src/process-errors.ts
+++ b/server/src/process-errors.ts
@@ -1,7 +1,7 @@
-import { resetClientsAndUniqueUsersMetrics } from '@app/plugins/version/unique-users-gauge';
+import { isDeployed } from '@app/config/env';
import { getLogger } from '@app/logger';
+import { resetClientsAndUniqueUsersMetrics } from '@app/plugins/version/unique-users-gauge';
import { EmojiIcons, sendToSlack } from '@app/slack';
-import { isDeployed } from '@app/config/env';
const log = getLogger('process-errors');
diff --git a/server/src/prometheus/types.ts b/server/src/prometheus/types.ts
index 8925802f7..a9be55cad 100644
--- a/server/src/prometheus/types.ts
+++ b/server/src/prometheus/types.ts
@@ -1,6 +1,6 @@
-import { register } from 'prom-client';
import { PROXY_VERSION } from '@app/config/config';
import { NAIS_NAMESPACE, POD_NAME } from '@app/config/env';
+import { register } from 'prom-client';
register.setDefaultLabels({
app_version: PROXY_VERSION.substring(0, 7),
diff --git a/server/src/server.ts b/server/src/server.ts
index 57d3cf51d..33f61fa6e 100644
--- a/server/src/server.ts
+++ b/server/src/server.ts
@@ -1,14 +1,20 @@
import { API_CLIENT_IDS } from '@app/config/config';
+import { corsOptions } from '@app/config/cors';
import { isDeployed } from '@app/config/env';
import { serverConfig } from '@app/config/server-config';
+import { querystringParser } from '@app/helpers/query-parser';
import { init } from '@app/init';
import { getLogger } from '@app/logger';
-import { corsOptions } from '@app/config/cors';
import { accessTokenPlugin } from '@app/plugins/access-token';
import { apiProxyPlugin } from '@app/plugins/api-proxy';
import { clientVersionPlugin } from '@app/plugins/client-version';
+import { crdtPlugin } from '@app/plugins/crdt/crdt';
import { documentPlugin } from '@app/plugins/document';
+import { healthPlugin } from '@app/plugins/health';
+import { httpLoggerPlugin } from '@app/plugins/http-logger';
+import { navIdentPlugin } from '@app/plugins/nav-ident';
import { oboAccessTokenPlugin } from '@app/plugins/obo-token';
+import { proxyVersionPlugin } from '@app/plugins/proxy-version';
import { serveAssetsPlugin } from '@app/plugins/serve-assets';
import { serveIndexPlugin } from '@app/plugins/serve-index';
import { serverTimingPlugin } from '@app/plugins/server-timing';
@@ -20,11 +26,6 @@ import { EmojiIcons, sendToSlack } from '@app/slack';
import cors from '@fastify/cors';
import { fastify } from 'fastify';
import metricsPlugin from 'fastify-metrics';
-import { querystringParser } from '@app/helpers/query-parser';
-import { httpLoggerPlugin } from '@app/plugins/http-logger';
-import { proxyVersionPlugin } from '@app/plugins/proxy-version';
-import { healthPlugin } from '@app/plugins/health';
-import { navIdentPlugin } from '@app/plugins/nav-ident';
processErrors();
@@ -61,6 +62,7 @@ fastify({ trustProxy: true, querystringParser, bodyLimit })
.register(serveAssetsPlugin)
.register(serveIndexPlugin)
.register(httpLoggerPlugin)
+ .register(crdtPlugin)
// Start server.
.listen({ host: '0.0.0.0', port: serverConfig.port });
diff --git a/server/src/slack.ts b/server/src/slack.ts
index 97e655624..f00579ee3 100644
--- a/server/src/slack.ts
+++ b/server/src/slack.ts
@@ -1,5 +1,5 @@
-import { optionalEnvString, requiredEnvString } from '@app/config/env-var';
import { ENVIRONMENT, isDeployed, isLocal } from '@app/config/env';
+import { optionalEnvString, requiredEnvString } from '@app/config/env-var';
import { getLogger } from '@app/logger';
const log = getLogger('slack');
diff --git a/server/tsconfig.json b/server/tsconfig.json
index a83c05a16..7eec5b24d 100644
--- a/server/tsconfig.json
+++ b/server/tsconfig.json
@@ -1,9 +1,7 @@
{
"compilerOptions": {
// enable latest features
- "lib": [
- "ESNext"
- ],
+ "lib": ["ESNext"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
@@ -30,17 +28,11 @@
"outDir": "./dist",
"baseUrl": "./src",
"paths": {
- "@app/*": [
- "*"
- ]
+ "@app/*": ["*"]
},
"incremental": true,
- "tsBuildInfoFile": ".tsbuildinfo",
+ "tsBuildInfoFile": ".tsbuildinfo"
},
- "include": [
- "src/**/*"
- ],
- "exclude": [
- "./dist/**"
- ]
-}
\ No newline at end of file
+ "include": ["src/**/*"],
+ "exclude": ["./dist/**"]
+}