Skip to content

Commit 66b0b9b

Browse files
mahvalcskrov
authored andcommitted
Show autosave status to user
1 parent 9738e38 commit 66b0b9b

File tree

5 files changed

+149
-20
lines changed

5 files changed

+149
-20
lines changed

src/assets/images/icons/Success.tsx

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
3+
function Success() {
4+
return (
5+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="none" viewBox="0 0 18 18">
6+
<path stroke="#78706A" d="M9 1C4.589 1 1 4.59 1 9s3.589 8 8 8c4.41 0 8-3.59 8-8s-3.59-8-8-8z"></path>
7+
<path
8+
fill="#78706A"
9+
d="M7.427 10.628l4.306-3.893a.665.665 0 01.918.03.615.615 0 01-.031.889l-4.766 4.309a.662.662 0 01-.902-.016l-1.588-1.539a.615.615 0 010-.889.664.664 0 01.918 0l1.145 1.109z"
10+
></path>
11+
</svg>
12+
);
13+
}
14+
15+
export default Success;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React, { useState } from 'react';
2+
import { Element } from 'nav-frontend-typografi';
3+
import styled from 'styled-components/macro';
4+
import { Ellipsis } from '../../../styled-components/ellipsis';
5+
import Success from '../../../assets/images/icons/Success';
6+
import Popover, { PopoverOrientering } from 'nav-frontend-popover';
7+
8+
export enum AutosaveStatus {
9+
NONE,
10+
SAVING,
11+
SAVED,
12+
FAILED
13+
}
14+
15+
interface Props {
16+
autosaveStatus: AutosaveStatus;
17+
}
18+
19+
const AutosaveContainer = styled.div`
20+
margin-top: 16px;
21+
color: #78706a;
22+
text-align: right;
23+
display: flex;
24+
justify-content: flex-end;
25+
div {
26+
cursor: pointer;
27+
> svg {
28+
margin-right: 5px;
29+
}
30+
display: flex;
31+
flex-flow: row wrap;
32+
align-items: center;
33+
}
34+
`;
35+
36+
const AutosaveProgressIndicator = (props: Props) => {
37+
const [anker, setAnker] = useState<(EventTarget & HTMLDivElement) | undefined>(undefined);
38+
39+
const togglePopover = (ankerEl: EventTarget & HTMLDivElement): void => setAnker(anker ? undefined : ankerEl);
40+
41+
return (
42+
<>
43+
<Popover
44+
ankerEl={anker}
45+
onRequestClose={() => setAnker(undefined)}
46+
orientering={PopoverOrientering.OverHoyre}
47+
>
48+
<p style={{ padding: '1rem' }}>Vi lagrer endringene dine automatisk.</p>
49+
</Popover>
50+
51+
<AutosaveContainer>
52+
<div onClick={e => togglePopover(e.currentTarget)}>{getContent(props.autosaveStatus)}</div>
53+
</AutosaveContainer>
54+
</>
55+
);
56+
};
57+
58+
const getContent = (status: AutosaveStatus) => {
59+
if (status === AutosaveStatus.SAVING) {
60+
return (
61+
<Element>
62+
<Ellipsis>Lagrer</Ellipsis>
63+
</Element>
64+
);
65+
}
66+
if (status === AutosaveStatus.SAVED) {
67+
return (
68+
<>
69+
<Success />
70+
<Element>Lagret</Element>
71+
</>
72+
);
73+
}
74+
if (status === AutosaveStatus.FAILED) {
75+
return <Element>Klarte ikke lagre</Element>;
76+
}
77+
return null;
78+
};
79+
80+
export default AutosaveProgressIndicator;

src/routes/klageskjema/begrunnelse/begrunnelse.tsx

+26-20
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import NavFrontendSpinner from 'nav-frontend-spinner';
1010
import {
1111
CenteredContainer,
1212
FlexCenteredContainer,
13-
InlineMargin48Container,
1413
Margin40Container,
1514
Margin48TopContainer,
1615
MarginContainer,
@@ -29,6 +28,7 @@ import { ApiError, NotLoggedInError } from '../../../api/errors';
2928
import klageStore from '../../../klage/klage-store';
3029
import { login } from '../../../auth/login';
3130
import { LoginButton } from '../../../styled-components/login-button';
31+
import AutosaveProgressIndicator, { AutosaveStatus } from './autosave-progress';
3232

3333
interface UploadError {
3434
timestamp: ISODateTime;
@@ -55,6 +55,7 @@ const Begrunnelse = ({ klage }: Props) => {
5555
const [attachmentsLoading, setAttachmentsLoading] = useState<boolean>(false);
5656
const [attachmentError, setAttachmentError] = useState<string | null>(null);
5757
const [submitted, setSubmitted] = useState<boolean>(false);
58+
const [autosaveStatus, setAutosaveStatus] = useState<AutosaveStatus>(AutosaveStatus.NONE);
5859

5960
useEffect(() => {
6061
if (klage.status !== KlageStatus.DRAFT) {
@@ -64,31 +65,34 @@ const Begrunnelse = ({ klage }: Props) => {
6465

6566
useEffect(() => window.scrollTo(0, 0), []);
6667

67-
const performKlageUpdate = useCallback(() => {
68+
const performKlageUpdate = useCallback(async () => {
6869
const klageUpdate = createKlageUpdate(klage, fritekst, vedtakType, vedtakDate);
69-
return updateKlage(klageUpdate)
70-
.then(() => {
71-
setKlage({
72-
...klage,
73-
...klageUpdate,
74-
vedlegg: attachments
75-
});
76-
klageStore.clear();
77-
return true;
78-
})
79-
.catch((error: Error) => {
80-
klageStore.store(fritekst, vedtakType, vedtakDate);
81-
setError(error);
82-
return false;
70+
try {
71+
await updateKlage(klageUpdate);
72+
setKlage({
73+
...klage,
74+
...klageUpdate,
75+
vedlegg: attachments
8376
});
77+
klageStore.clear();
78+
setAutosaveStatus(AutosaveStatus.SAVED);
79+
return true;
80+
} catch (error) {
81+
setAutosaveStatus(AutosaveStatus.FAILED);
82+
klageStore.store(fritekst, vedtakType, vedtakDate);
83+
setError(error);
84+
return false;
85+
}
8486
}, [fritekst, vedtakDate, vedtakType, attachments, klage, setKlage]);
8587

8688
useEffect(() => {
8789
if (klage.vedtakType === vedtakType && klage.vedtakDate === vedtakDate && klage.fritekst === fritekst) {
90+
setAutosaveStatus(AutosaveStatus.SAVED);
8891
return;
8992
}
90-
const timeout = setTimeout(performKlageUpdate, 1000); // 1s - timeout til å kjøre funksjon om timeouten ikke blir nullstillt
91-
return () => clearTimeout(timeout); // Nullstill og ikke kjør funksjon
93+
setAutosaveStatus(AutosaveStatus.SAVING);
94+
const timeout = setTimeout(performKlageUpdate, 1000);
95+
return () => clearTimeout(timeout); // Clear existing timer every time it runs.
9296
}, [fritekst, vedtakDate, vedtakType, klage, performKlageUpdate]);
9397

9498
const fileInput = useRef<HTMLInputElement>(null);
@@ -223,7 +227,7 @@ const Begrunnelse = ({ klage }: Props) => {
223227
/>
224228
</MarginContainer>
225229
)}
226-
<InlineMargin48Container>
230+
<InlineMargin48TopContainer>
227231
<Undertittel>Begrunn klagen din</Undertittel>
228232
<Textarea
229233
name="begrunnelse"
@@ -237,7 +241,9 @@ const Begrunnelse = ({ klage }: Props) => {
237241
}}
238242
feil={submitted && !validBegrunnelse() && 'Du må skrive en begrunnelse før du går videre.'}
239243
/>
240-
</InlineMargin48Container>
244+
</InlineMargin48TopContainer>
245+
246+
<AutosaveProgressIndicator autosaveStatus={autosaveStatus} />
241247

242248
<MarginContainer>
243249
<Undertittel>Vedlegg ({attachments.length})</Undertittel>

src/styled-components/common.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ export const InlineMargin48Container = styled(Margin48Container)`
7777
position: initial;
7878
`;
7979

80+
export const InlineMargin48TopContainer = styled(Margin48TopContainer)`
81+
display: inline-block;
82+
position: initial;
83+
`;
84+
8085
export const PaddingContainer = styled.div`
8186
padding-top: 32px;
8287
padding-bottom: 32px;

src/styled-components/ellipsis.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import styled from 'styled-components/macro';
2+
3+
export const Ellipsis = styled.span`
4+
&::before {
5+
display: inline-block;
6+
animation: ellipsis 1.25s infinite;
7+
content: '.';
8+
width: 1em;
9+
text-align: right;
10+
margin-right: 5px;
11+
}
12+
@keyframes ellipsis {
13+
0% {
14+
content: '.';
15+
}
16+
33% {
17+
content: '..';
18+
}
19+
66% {
20+
content: '...';
21+
}
22+
}
23+
`;

0 commit comments

Comments
 (0)