Skip to content

Commit 6652d9e

Browse files
eriksson-danielcskrov
authored andcommitted
Add timeline history for maltekstseksjoner
1 parent 3fb7855 commit 6652d9e

File tree

23 files changed

+615
-53
lines changed

23 files changed

+615
-53
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export const CURRENT_YEAR_IN_CENTURY = Number.parseInt(new Date().getFullYear().toString().slice(2), 10);
22
export const ISO_FORMAT = 'yyyy-MM-dd';
33
export const PRETTY_FORMAT = 'dd.MM.yyyy';
4+
export const PRETTY_DATETIME_FORMAT = 'dd.MM.yyyy HH:mm:ss';
45
export const FORMAT = 'yyyy-MM-dd';
56
export const ISO_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";

frontend/src/components/maltekstseksjoner/maltekstseksjon/draft/draft.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { EditableTitle } from '@app/components/editable-title/editable-title';
55
import { EditorName } from '@app/components/editor-name/editor-name';
66
import { Filters } from '@app/components/maltekstseksjoner/filters';
77
import { MaltekstseksjonTexts } from '@app/components/maltekstseksjoner/maltekstseksjon/texts';
8+
import { MaltekstHistoryModal } from '@app/components/maltekstseksjoner/maltekstseksjon/timeline/timeline';
89
import {
910
TagContainer,
1011
TemplateSectionTagList,
@@ -19,18 +20,24 @@ import {
1920
useUpdateYtelseHjemmelIdListMutation,
2021
} from '@app/redux-api/maltekstseksjoner/mutations';
2122
import { IGetMaltekstseksjonParams } from '@app/types/maltekstseksjoner/params';
22-
import { IDraftMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
23+
import { IDraftMaltekstseksjon, IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
2324
import { Container, DateTimeContainer, Header, MetadataContainer } from '../common';
2425
import { Actions } from './actions';
2526
import { Sidebar } from './sidebar';
2627

2728
interface MaltekstProps {
2829
maltekstseksjon: IDraftMaltekstseksjon;
30+
nextMaltekstseksjon?: IMaltekstseksjon;
2931
query: IGetMaltekstseksjonParams;
3032
onDraftDeleted: () => void;
3133
}
3234

33-
export const DraftMaltekstSection = ({ maltekstseksjon, query, onDraftDeleted }: MaltekstProps) => {
35+
export const DraftMaltekstSection = ({
36+
maltekstseksjon,
37+
nextMaltekstseksjon,
38+
query,
39+
onDraftDeleted,
40+
}: MaltekstProps) => {
3441
const { id, title } = maltekstseksjon;
3542
const [updateTitle, { isLoading: isUpdatingTitle }] = useUpdateMaltekstTitleMutation({
3643
fixedCacheKey: `${maltekstseksjon.id}-title`,
@@ -65,6 +72,10 @@ export const DraftMaltekstSection = ({ maltekstseksjon, query, onDraftDeleted }:
6572
</DateTimeContainer>
6673
<span>av {lastEditor === undefined ? 'Ukjent' : <EditorName editorId={lastEditor.navIdent} />}</span>
6774
<TextHistory {...maltekstseksjon} isUpdating={isUpdating} />
75+
<MaltekstHistoryModal
76+
maltekstseksjon={maltekstseksjon}
77+
nextMaltekstseksjonDate={nextMaltekstseksjon?.publishedDateTime}
78+
/>
6879
</MetadataContainer>
6980
<Filters maltekst={maltekstseksjon} query={query} />
7081
<TagContainer>
@@ -77,7 +88,7 @@ export const DraftMaltekstSection = ({ maltekstseksjon, query, onDraftDeleted }:
7788

7889
<Sidebar maltekstseksjon={maltekstseksjon} query={query} />
7990

80-
<MaltekstseksjonTexts maltekstseksjon={maltekstseksjon} query={query} />
91+
<MaltekstseksjonTexts maltekstseksjon={maltekstseksjon} nextMaltekstseksjon={nextMaltekstseksjon} query={query} />
8192
</Container>
8293
);
8394
};

frontend/src/components/maltekstseksjoner/maltekstseksjon/list-item.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { useGetTextVersionsQuery } from '@app/redux-api/texts/queries';
1010
import { IGetMaltekstseksjonParams, RichTextTypes } from '@app/types/common-text-types';
1111
import { isApiError } from '@app/types/errors';
1212
import { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
13+
import { IText } from '@app/types/texts/responses';
1314
import { DragAndDropContext } from '../drag-and-drop/drag-context';
1415
import { TextLink } from '../text-link';
1516

@@ -43,7 +44,10 @@ export const LoadTextListItem = ({ textId, maltekstseksjon, query }: LoadTextLis
4344
[setDraggedTextId, textId],
4445
);
4546

46-
const text = !isLoading && versions !== undefined ? versions[0] : undefined;
47+
const text =
48+
!isLoading && versions !== undefined
49+
? getFirstText(versions, maltekstseksjon.publishedDateTime !== null)
50+
: undefined;
4751

4852
const isReady = text !== undefined;
4953

@@ -145,3 +149,11 @@ const HelpTextContainer = styled.div`
145149
max-width: 300px;
146150
white-space: normal;
147151
`;
152+
153+
const getFirstText = (versions: IText[], isPublished: boolean) => {
154+
if (isPublished) {
155+
return versions.at(0);
156+
}
157+
158+
return versions.find((v) => v.published);
159+
};

frontend/src/components/maltekstseksjoner/maltekstseksjon/maltekstseksjon-published.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { DateTime } from '@app/components/datetime/datetime';
77
import { getTitle } from '@app/components/editable-title/editable-title';
88
import { EditorName } from '@app/components/editor-name/editor-name';
99
import { MaltekstseksjonTexts } from '@app/components/maltekstseksjoner/maltekstseksjon/texts';
10+
import { MaltekstHistoryModal } from '@app/components/maltekstseksjoner/maltekstseksjon/timeline/timeline';
1011
import {
1112
TagContainer,
1213
TemplateSectionTagList,
@@ -16,7 +17,7 @@ import {
1617
import { TextHistory } from '@app/components/text-history/text-history';
1718
import { useCreateDraftFromVersionMutation } from '@app/redux-api/maltekstseksjoner/mutations';
1819
import { IGetMaltekstseksjonParams } from '@app/types/maltekstseksjoner/params';
19-
import { IPublishedMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
20+
import { IMaltekstseksjon, IPublishedMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
2021
import { TextListItem } from '../styled-components';
2122
import {
2223
ActionsContainer,
@@ -31,11 +32,17 @@ import { LoadTextListItem } from './list-item';
3132

3233
interface MaltekstProps {
3334
maltekstseksjon: IPublishedMaltekstseksjon;
35+
nextMaltekstseksjon?: IMaltekstseksjon;
3436
query: IGetMaltekstseksjonParams;
3537
onDraftCreated: (versionId: string) => void;
3638
}
3739

38-
export const PublishedMaltekstSection = ({ maltekstseksjon, query, onDraftCreated }: MaltekstProps) => {
40+
export const PublishedMaltekstSection = ({
41+
maltekstseksjon,
42+
nextMaltekstseksjon,
43+
query,
44+
onDraftCreated,
45+
}: MaltekstProps) => {
3946
const { textId: activeTextId } = useParams<{ textId: string }>();
4047
const { id, title, textIdList, publishedDateTime, versionId, publishedBy } = maltekstseksjon;
4148
const [createDraft, { isLoading }] = useCreateDraftFromVersionMutation();
@@ -60,6 +67,10 @@ export const PublishedMaltekstSection = ({ maltekstseksjon, query, onDraftCreate
6067
av {publishedBy === null ? 'Ukjent' : <EditorName editorId={publishedBy} />}
6168
</LabelValue>
6269
<TextHistory {...maltekstseksjon} isUpdating={false} />
70+
<MaltekstHistoryModal
71+
maltekstseksjon={maltekstseksjon}
72+
nextMaltekstseksjonDate={nextMaltekstseksjon?.publishedDateTime}
73+
/>
6374
</Row>
6475
<TagContainer>
6576
<TemplateSectionTagList templateSectionIdList={maltekstseksjon.templateSectionIdList} />
@@ -83,7 +94,7 @@ export const PublishedMaltekstSection = ({ maltekstseksjon, query, onDraftCreate
8394
</List>
8495
</SidebarContainer>
8596

86-
<MaltekstseksjonTexts maltekstseksjon={maltekstseksjon} query={query} />
97+
<MaltekstseksjonTexts maltekstseksjon={maltekstseksjon} nextMaltekstseksjon={nextMaltekstseksjon} query={query} />
8798
</Container>
8899
);
89100
};

frontend/src/components/maltekstseksjoner/maltekstseksjon/maltekstseksjon-versions.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import { DraftMaltekstSection } from './draft/draft';
1414
import { PublishedMaltekstSection } from './maltekstseksjon-published';
1515

1616
interface Props {
17-
id: string;
17+
maltekstseksjon: IMaltekstseksjon;
1818
query: IGetMaltekstseksjonParams;
1919
}
2020

21-
export const MaltekstseksjonVersions = ({ id, query }: Props) => {
22-
const { data: versions, isLoading } = useGetMaltekstseksjonVersionsQuery(id);
21+
export const MaltekstseksjonVersions = ({ maltekstseksjon, query }: Props) => {
22+
const { data: versions, isLoading } = useGetMaltekstseksjonVersionsQuery(maltekstseksjon.id);
2323

2424
if (isLoading || versions === undefined) {
2525
return null;
@@ -81,14 +81,24 @@ const Loaded = ({ versions, first, query }: LoadedProps) => {
8181
selectedTabId={maltekstseksjonVersionId}
8282
setSelectedTabId={setSelectedTabId}
8383
versions={versions}
84-
createDraftPanel={(version) => (
84+
createDraftPanel={(version, nextVersion) => (
8585
<PanelContent>
86-
<DraftMaltekstSection maltekstseksjon={version} query={query} onDraftDeleted={onDraftDeleted} />
86+
<DraftMaltekstSection
87+
maltekstseksjon={version}
88+
nextMaltekstseksjon={nextVersion}
89+
query={query}
90+
onDraftDeleted={onDraftDeleted}
91+
/>
8792
</PanelContent>
8893
)}
89-
createPublishedPanel={(version) => (
94+
createPublishedPanel={(version, nextVersion) => (
9095
<PanelContent>
91-
<PublishedMaltekstSection maltekstseksjon={version} query={query} onDraftCreated={onDraftCreated} />
96+
<PublishedMaltekstSection
97+
maltekstseksjon={version}
98+
nextMaltekstseksjon={nextVersion}
99+
query={query}
100+
onDraftCreated={onDraftCreated}
101+
/>
92102
</PanelContent>
93103
)}
94104
/>

frontend/src/components/maltekstseksjoner/maltekstseksjon/maltekstseksjon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const Maltekstseksjon = ({ maltekstseksjon, query }: Props) => (
1616
<UnpublishMaltekstseksjonButton id={maltekstseksjon.id} title={maltekstseksjon.title} query={query} />
1717
</MaltekstseksjonHeader>
1818

19-
<MaltekstseksjonVersions id={maltekstseksjon.id} query={query} />
19+
<MaltekstseksjonVersions maltekstseksjon={maltekstseksjon} query={query} />
2020
</MaltekstseksjonContainer>
2121
);
2222

frontend/src/components/maltekstseksjoner/maltekstseksjon/preview.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,57 @@ import { Alert, Loader } from '@navikt/ds-react';
22
import React, { useEffect, useState } from 'react';
33
import { styled } from 'styled-components';
44
import { RedaktoerRichText } from '@app/components/redaktoer-rich-text/redaktoer-rich-text';
5+
import { isNotUndefined } from '@app/functions/is-not-type-guards';
56
import { isRichText } from '@app/functions/is-rich-plain-text';
67
import { useRedaktoerLanguage } from '@app/hooks/use-redaktoer-language';
78
import { SPELL_CHECK_LANGUAGES } from '@app/hooks/use-smart-editor-language';
89
import { EditorValue } from '@app/plate/types';
910
import { useUpdateTextIdListMutation } from '@app/redux-api/maltekstseksjoner/mutations';
10-
import { useLazyGetTextByIdQuery } from '@app/redux-api/texts/queries';
11+
import { useLazyGetTextVersionsQuery } from '@app/redux-api/texts/queries';
1112
import { isApiError } from '@app/types/errors';
1213
import { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
1314
import { IRichText } from '@app/types/texts/responses';
1415

1516
interface Props {
1617
maltekstseksjon: IMaltekstseksjon;
18+
nextMaltekstseksjon?: IMaltekstseksjon;
1719
}
1820

19-
export const MaltekstseksjonPreview = ({ maltekstseksjon }: Props) => {
21+
export const MaltekstseksjonPreview = ({ maltekstseksjon, nextMaltekstseksjon }: Props) => {
2022
const [, { isLoading: isUpdating }] = useUpdateTextIdListMutation({ fixedCacheKey: maltekstseksjon.id });
2123
const [texts, setTexts] = useState<IRichText[]>([]);
2224
const [error, setError] = useState<string | null>(null);
23-
const [getText] = useLazyGetTextByIdQuery();
25+
const [getTextVersions] = useLazyGetTextVersionsQuery();
2426
const { textIdList } = maltekstseksjon;
2527
const lang = useRedaktoerLanguage();
2628
const editorId = `${textIdList.join(':')}-${lang}-preview`;
2729

2830
useEffect(() => {
2931
setError(null);
3032

31-
const promises = textIdList.map((textId) => getText(textId, true).unwrap());
33+
const promises = textIdList.map((textId) => getTextVersions(textId, true).unwrap());
3234

3335
Promise.all(promises)
36+
.then((textVersionsList) =>
37+
textVersionsList
38+
.map((textVersions) =>
39+
textVersions.find((v) => {
40+
if (maltekstseksjon.publishedDateTime === null || nextMaltekstseksjon === undefined) {
41+
return true;
42+
}
43+
44+
if (nextMaltekstseksjon.publishedDateTime === null) {
45+
return v.publishedDateTime !== null;
46+
}
47+
48+
return v.publishedDateTime !== null && v.publishedDateTime < nextMaltekstseksjon.publishedDateTime;
49+
}),
50+
)
51+
.filter(isNotUndefined),
52+
)
3453
.then((textList) => setTexts(textList.filter(isRichText)))
3554
.catch((e) => setError('data' in e && isApiError(e.data) ? e.data.detail : e.message));
36-
}, [getText, textIdList]);
55+
}, [getTextVersions, maltekstseksjon.published, maltekstseksjon.publishedDateTime, nextMaltekstseksjon, textIdList]);
3756

3857
if (error !== null) {
3958
return (

frontend/src/components/maltekstseksjoner/maltekstseksjon/texts.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { IMaltekstseksjon } from '@app/types/maltekstseksjoner/responses';
99

1010
interface Props {
1111
maltekstseksjon: IMaltekstseksjon;
12+
nextMaltekstseksjon?: IMaltekstseksjon;
1213
query: IGetMaltekstseksjonParams;
1314
}
1415

@@ -17,7 +18,7 @@ enum TabsEnum {
1718
EDIT = 'EDIT',
1819
}
1920

20-
export const MaltekstseksjonTexts = ({ maltekstseksjon, query }: Props) => {
21+
export const MaltekstseksjonTexts = ({ maltekstseksjon, nextMaltekstseksjon, query }: Props) => {
2122
const [activeTab, setActiveTab] = useState<TabsEnum>(TabsEnum.PREVIEW);
2223
const textCount = maltekstseksjon.textIdList.length;
2324
const [previousTextCount, setPreviousTextCount] = useState(textCount);
@@ -41,10 +42,10 @@ export const MaltekstseksjonTexts = ({ maltekstseksjon, query }: Props) => {
4142
<Tabs.Tab value={TabsEnum.PREVIEW} label="Forhåndsvisning" icon={<MagnifyingGlassIcon aria-hidden />} />
4243
</Tabs.List>
4344
<StyledTabPanel value={TabsEnum.PREVIEW}>
44-
<MaltekstseksjonPreview maltekstseksjon={maltekstseksjon} />
45+
<MaltekstseksjonPreview maltekstseksjon={maltekstseksjon} nextMaltekstseksjon={nextMaltekstseksjon} />
4546
</StyledTabPanel>
4647
<StyledTabPanel value={TabsEnum.EDIT}>
47-
<TextList maltekstseksjon={maltekstseksjon} query={query} />
48+
<TextList maltekstseksjon={maltekstseksjon} nextMaltekstseksjon={nextMaltekstseksjon} query={query} />
4849
</StyledTabPanel>
4950
</StyledTabs>
5051
);
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { format } from 'date-fns';
2+
import React from 'react';
3+
import { styled } from 'styled-components';
4+
import { ISO_DATETIME_FORMAT } from '@app/components/date-picker/constants';
5+
import { Time } from '@app/components/time/time';
6+
7+
interface Props {
8+
from: Date;
9+
to: Date;
10+
value: Date;
11+
onChange: (value: Date) => void;
12+
}
13+
14+
export const PinTime = ({ from, to, value, onChange }: Props) => {
15+
const minTime = from.getTime();
16+
const maxTime = to.getTime();
17+
const length = maxTime - minTime;
18+
const min = 0;
19+
const max = length;
20+
const step = length / 1_000;
21+
22+
return (
23+
<Container>
24+
<Time date={to} />
25+
<input
26+
type="datetime-local"
27+
value={format(value, ISO_DATETIME_FORMAT)}
28+
onChange={(e) => onChange(new Date(Number.parseInt(e.target.value, 10)))}
29+
/>
30+
<RangeInput
31+
type="range"
32+
min={min}
33+
max={max}
34+
step={step}
35+
onChange={(e) => onChange(new Date(maxTime - Number.parseInt(e.target.value, 10)))}
36+
value={maxTime - value.getTime()}
37+
/>
38+
<Time date={from} />
39+
</Container>
40+
);
41+
};
42+
43+
const Container = styled.div`
44+
display: flex;
45+
flex-direction: row;
46+
gap: 8px;
47+
`;
48+
49+
const RangeInput = styled.input`
50+
flex-grow: 1;
51+
`;

0 commit comments

Comments
 (0)