@innspel/react-native 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { MaskRect, InnspelClient, OpenFeedbackOptions, ParticipationView, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
1
+ import { MaskRect, InnspelClient, OpenFeedbackOptions, ParticipationView, ProgramTask, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
2
2
  export { AttachmentInput, AttachmentOutcome, CaptureInput, ContextInput, InnspelClient, InnspelOptions, InnspelStorage, InvitationView, MyPrograms, MySubmission, OpenFeedbackOptions, ParticipationView, ProgramTask, ProgramView, SubmissionStatusView } from '@innspel/core';
3
3
  import React, { RefObject, ComponentRef } from 'react';
4
4
  import { View, StyleProp, ViewStyle } from 'react-native';
@@ -376,18 +376,23 @@ declare function InnspelPrivate({ children, style }: {
376
376
  * dyplenken og gir det hit. Uten det viser banneret fortsatt invitasjonen — men
377
377
  * «Bli med» er inaktiv, med lenken forklart, i stedet for å feile ved trykk.
378
378
  *
379
- * Oppgavene, «Hvordan gikk det?» og oppfølgingene er M3.2.
379
+ * Deltakelsen (3c) viser oppgavene med «Gjort» → «Hvordan gikk det?» (tre lukkede
380
+ * valg og en setning, aldri en skala) og oppfølgingskortene (3d, DD-34), der arten
381
+ * avgjør knappene. «Ikke» tilbyr å melde feilen — til verten via `onMeldFeil`, fordi
382
+ * feilsporet og navigasjonen er vertens.
380
383
  *
381
384
  * @example
382
385
  * ```tsx
383
386
  * <InnspelProgram invitationToken={tokenFraDyplenke} />
384
387
  * ```
385
388
  */
386
- declare function InnspelProgram({ invitationToken, onEndring, }: {
389
+ declare function InnspelProgram({ invitationToken, onEndring, onMeldFeil, }: {
387
390
  /** Engangs-tokenet fra leietakerens egen kanal, typisk fanget fra en dyplenke. */
388
391
  invitationToken?: string;
389
392
  /** Kalles når testeren blir med eller går ut — for vertens egen navigasjon. */
390
393
  onEndring?: (deltakelse: ParticipationView | null) => void;
394
+ /** Kalles når testeren vil melde at en oppgave ikke gikk — verten åpner feilsporet med oppgaven som kontekst. Utelatt = tilbudet vises ikke. */
395
+ onMeldFeil?: (task: ProgramTask) => void;
391
396
  }): React.JSX.Element | null;
392
397
 
393
398
  /**
@@ -582,6 +587,30 @@ declare const tekst: {
582
587
  readonly gaaUtTekst: (navn: string) => string;
583
588
  readonly bliVaerende: "Bli værende";
584
589
  readonly gaaUtBekreft: "Gå ut";
590
+ readonly oppgaverTittel: "Oppgavene";
591
+ readonly oppgaveIkkeBegynt: "Ikke begynt";
592
+ readonly oppgaveGjort: "Gjort";
593
+ readonly oppgaveGjortLabel: (tittel: string) => string;
594
+ readonly oppgaveGjortMed: (utfall: string) => string;
595
+ readonly hvordanGikkDet: "Hvordan gikk det?";
596
+ readonly utfallFungerte: "Fungerte";
597
+ readonly utfallDelvis: "Delvis";
598
+ readonly utfallIkke: "Ikke";
599
+ readonly setningLabel: "Vil du si én setning om det? (valgfritt)";
600
+ readonly setningPlaceholder: "Kopien kom, men rullesjefen manglet på torsdag.";
601
+ readonly lagre: "Lagre";
602
+ readonly lagreHint: "Ferdig — ingen flere spørsmål nå.";
603
+ /** Etter «Ikke»: feilsporet er vertens — plattformen navigerer aldri selv. */
604
+ readonly meldFeil: "Meld det som feil";
605
+ readonly meldFeilHint: "Gikk det ikke? Et innspill hjelper oss å finne ut hvorfor.";
606
+ readonly svarFeil: (arsak: string) => string;
607
+ readonly oppfolgingRegion: "Oppfølging fra betaprogrammet";
608
+ readonly oppfolgingKontakt: (nr: number, maks: number) => string;
609
+ readonly oppfolgingPaaminnelseHint: (nr: number, maks: number) => string;
610
+ readonly oppfolgingUtgangHint: (nr: number, maks: number) => string;
611
+ readonly senere: "Senere";
612
+ readonly svarNaa: "Svar nå";
613
+ readonly aapneOppgavene: "Åpne oppgavene";
585
614
  readonly tilbake: "Tilbake";
586
615
  readonly lukk: "Lukk";
587
616
  readonly venterPaaNett: "Venter på nett — sendes automatisk";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MaskRect, InnspelClient, OpenFeedbackOptions, ParticipationView, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
1
+ import { MaskRect, InnspelClient, OpenFeedbackOptions, ParticipationView, ProgramTask, InnspelStorage, SubmissionStatus, SubmissionType } from '@innspel/core';
2
2
  export { AttachmentInput, AttachmentOutcome, CaptureInput, ContextInput, InnspelClient, InnspelOptions, InnspelStorage, InvitationView, MyPrograms, MySubmission, OpenFeedbackOptions, ParticipationView, ProgramTask, ProgramView, SubmissionStatusView } from '@innspel/core';
3
3
  import React, { RefObject, ComponentRef } from 'react';
4
4
  import { View, StyleProp, ViewStyle } from 'react-native';
@@ -376,18 +376,23 @@ declare function InnspelPrivate({ children, style }: {
376
376
  * dyplenken og gir det hit. Uten det viser banneret fortsatt invitasjonen — men
377
377
  * «Bli med» er inaktiv, med lenken forklart, i stedet for å feile ved trykk.
378
378
  *
379
- * Oppgavene, «Hvordan gikk det?» og oppfølgingene er M3.2.
379
+ * Deltakelsen (3c) viser oppgavene med «Gjort» → «Hvordan gikk det?» (tre lukkede
380
+ * valg og en setning, aldri en skala) og oppfølgingskortene (3d, DD-34), der arten
381
+ * avgjør knappene. «Ikke» tilbyr å melde feilen — til verten via `onMeldFeil`, fordi
382
+ * feilsporet og navigasjonen er vertens.
380
383
  *
381
384
  * @example
382
385
  * ```tsx
383
386
  * <InnspelProgram invitationToken={tokenFraDyplenke} />
384
387
  * ```
385
388
  */
386
- declare function InnspelProgram({ invitationToken, onEndring, }: {
389
+ declare function InnspelProgram({ invitationToken, onEndring, onMeldFeil, }: {
387
390
  /** Engangs-tokenet fra leietakerens egen kanal, typisk fanget fra en dyplenke. */
388
391
  invitationToken?: string;
389
392
  /** Kalles når testeren blir med eller går ut — for vertens egen navigasjon. */
390
393
  onEndring?: (deltakelse: ParticipationView | null) => void;
394
+ /** Kalles når testeren vil melde at en oppgave ikke gikk — verten åpner feilsporet med oppgaven som kontekst. Utelatt = tilbudet vises ikke. */
395
+ onMeldFeil?: (task: ProgramTask) => void;
391
396
  }): React.JSX.Element | null;
392
397
 
393
398
  /**
@@ -582,6 +587,30 @@ declare const tekst: {
582
587
  readonly gaaUtTekst: (navn: string) => string;
583
588
  readonly bliVaerende: "Bli værende";
584
589
  readonly gaaUtBekreft: "Gå ut";
590
+ readonly oppgaverTittel: "Oppgavene";
591
+ readonly oppgaveIkkeBegynt: "Ikke begynt";
592
+ readonly oppgaveGjort: "Gjort";
593
+ readonly oppgaveGjortLabel: (tittel: string) => string;
594
+ readonly oppgaveGjortMed: (utfall: string) => string;
595
+ readonly hvordanGikkDet: "Hvordan gikk det?";
596
+ readonly utfallFungerte: "Fungerte";
597
+ readonly utfallDelvis: "Delvis";
598
+ readonly utfallIkke: "Ikke";
599
+ readonly setningLabel: "Vil du si én setning om det? (valgfritt)";
600
+ readonly setningPlaceholder: "Kopien kom, men rullesjefen manglet på torsdag.";
601
+ readonly lagre: "Lagre";
602
+ readonly lagreHint: "Ferdig — ingen flere spørsmål nå.";
603
+ /** Etter «Ikke»: feilsporet er vertens — plattformen navigerer aldri selv. */
604
+ readonly meldFeil: "Meld det som feil";
605
+ readonly meldFeilHint: "Gikk det ikke? Et innspill hjelper oss å finne ut hvorfor.";
606
+ readonly svarFeil: (arsak: string) => string;
607
+ readonly oppfolgingRegion: "Oppfølging fra betaprogrammet";
608
+ readonly oppfolgingKontakt: (nr: number, maks: number) => string;
609
+ readonly oppfolgingPaaminnelseHint: (nr: number, maks: number) => string;
610
+ readonly oppfolgingUtgangHint: (nr: number, maks: number) => string;
611
+ readonly senere: "Senere";
612
+ readonly svarNaa: "Svar nå";
613
+ readonly aapneOppgavene: "Åpne oppgavene";
585
614
  readonly tilbake: "Tilbake";
586
615
  readonly lukk: "Lukk";
587
616
  readonly venterPaaNett: "Venter på nett — sendes automatisk";
package/dist/index.js CHANGED
@@ -580,6 +580,32 @@ var tekst = {
580
580
  gaaUtTekst: (navn) => `Du tas ut av testgruppen med en gang. \xAB${navn}\xBB sl\xE5s av for deg, og vi tar ikke kontakt igjen. Det du allerede har svart, beholdes.`,
581
581
  bliVaerende: "Bli v\xE6rende",
582
582
  gaaUtBekreft: "G\xE5 ut",
583
+ // 3c (M3.2) — oppgavene og «Hvordan gikk det?»: tre lukkede valg, aldri en skala
584
+ oppgaverTittel: "Oppgavene",
585
+ oppgaveIkkeBegynt: "Ikke begynt",
586
+ oppgaveGjort: "Gjort",
587
+ oppgaveGjortLabel: (tittel) => `Gjort: ${tittel}`,
588
+ oppgaveGjortMed: (utfall) => `Gjort \xB7 ${utfall}`,
589
+ hvordanGikkDet: "Hvordan gikk det?",
590
+ utfallFungerte: "Fungerte",
591
+ utfallDelvis: "Delvis",
592
+ utfallIkke: "Ikke",
593
+ setningLabel: "Vil du si \xE9n setning om det? (valgfritt)",
594
+ setningPlaceholder: "Kopien kom, men rullesjefen manglet p\xE5 torsdag.",
595
+ lagre: "Lagre",
596
+ lagreHint: "Ferdig \u2014 ingen flere sp\xF8rsm\xE5l n\xE5.",
597
+ /** Etter «Ikke»: feilsporet er vertens — plattformen navigerer aldri selv. */
598
+ meldFeil: "Meld det som feil",
599
+ meldFeilHint: "Gikk det ikke? Et innspill hjelper oss \xE5 finne ut hvorfor.",
600
+ svarFeil: (arsak) => `Kunne ikke lagre svaret: ${arsak}`,
601
+ // 3d (M3.2) — oppfølgingskortene (DD-34): arten avgjør knappene
602
+ oppfolgingRegion: "Oppf\xF8lging fra betaprogrammet",
603
+ oppfolgingKontakt: (nr, maks) => `Kontakt ${nr} av ${maks} \xB7 tar under ett minutt`,
604
+ oppfolgingPaaminnelseHint: (nr, maks) => `Kontakt ${nr} av ${maks} \xB7 planlagt p\xE5minnelse`,
605
+ oppfolgingUtgangHint: (nr, maks) => `Kontakt ${nr} av ${maks} \xB7 den siste vi sender`,
606
+ senere: "Senere",
607
+ svarNaa: "Svar n\xE5",
608
+ aapneOppgavene: "\xC5pne oppgavene",
583
609
  // Felles
584
610
  tilbake: "Tilbake",
585
611
  lukk: "Lukk",
@@ -1791,9 +1817,11 @@ function InnspelPrivate({ children, style }) {
1791
1817
  }
1792
1818
 
1793
1819
  // src/program/InnspelProgram.tsx
1794
- import { InnspelApiError as InnspelApiError2 } from "@innspel/core";
1795
- import { useCallback as useCallback4, useEffect as useEffect3, useState as useState5 } from "react";
1796
- import { Modal as Modal3, Pressable as Pressable7, Text as Text10, View as View12 } from "react-native";
1820
+ import {
1821
+ InnspelApiError as InnspelApiError2
1822
+ } from "@innspel/core";
1823
+ import { useCallback as useCallback4, useEffect as useEffect3, useState as useState6 } from "react";
1824
+ import { Modal as Modal4, Pressable as Pressable8, Text as Text13, View as View15 } from "react-native";
1797
1825
 
1798
1826
  // src/program/Aksept.tsx
1799
1827
  import "react";
@@ -1949,21 +1977,263 @@ function Aksept({
1949
1977
  ) }) });
1950
1978
  }
1951
1979
 
1980
+ // src/program/HvordanGikkDet.tsx
1981
+ import { useState as useState5 } from "react";
1982
+ import { Modal as Modal3, Pressable as Pressable7, Text as Text10, TextInput as TextInput2, View as View12 } from "react-native";
1983
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1984
+ var UTFALL = [
1985
+ { verdi: "fungerte", ikon: "\u2713", etikett: tekst.utfallFungerte },
1986
+ { verdi: "delvis", ikon: "\u25D0", etikett: tekst.utfallDelvis },
1987
+ { verdi: "ikke", ikon: "\u2715", etikett: tekst.utfallIkke }
1988
+ ];
1989
+ var SETNING_MAKS = 140;
1990
+ function utfallEtikett(verdi) {
1991
+ return UTFALL.find((u) => u.verdi === verdi)?.etikett ?? verdi;
1992
+ }
1993
+ function HvordanGikkDet({
1994
+ under,
1995
+ sender,
1996
+ feil,
1997
+ onLagre,
1998
+ onLukk
1999
+ }) {
2000
+ const tema = useTema();
2001
+ const [valgt, setValgt] = useState5();
2002
+ const [setning, setSetning] = useState5("");
2003
+ return /* @__PURE__ */ jsx12(Modal3, { visible: true, transparent: true, animationType: "fade", onRequestClose: onLukk, accessibilityViewIsModal: true, children: /* @__PURE__ */ jsx12(View12, { style: { flex: 1, backgroundColor: tema.color.scrim, justifyContent: "flex-end", padding: tema.space.md }, children: /* @__PURE__ */ jsxs11(
2004
+ View12,
2005
+ {
2006
+ testID: "hvordan-gikk-det",
2007
+ accessibilityViewIsModal: true,
2008
+ style: {
2009
+ backgroundColor: tema.color.surface,
2010
+ borderRadius: tema.radius.lg,
2011
+ borderWidth: tema.hairline,
2012
+ borderColor: tema.color.borderStrong,
2013
+ padding: tema.space.md,
2014
+ gap: tema.space.md
2015
+ },
2016
+ children: [
2017
+ /* @__PURE__ */ jsx12(Btn, { variant: "ghost", testID: "hvordan-tilbake", ikon: "\u2190", label: tekst.tilbake, onPress: onLukk }),
2018
+ /* @__PURE__ */ jsx12(Hint, { children: under }),
2019
+ /* @__PURE__ */ jsx12(H, { children: tekst.hvordanGikkDet }),
2020
+ /* @__PURE__ */ jsx12(View12, { testID: "utfall-gruppe", accessibilityRole: "radiogroup", accessibilityLabel: tekst.hvordanGikkDet, style: { flexDirection: "row", gap: tema.space.sm }, children: UTFALL.map((u) => {
2021
+ const aktiv = valgt === u.verdi;
2022
+ return /* @__PURE__ */ jsxs11(
2023
+ Pressable7,
2024
+ {
2025
+ testID: `utfall-${u.verdi}`,
2026
+ accessibilityRole: "radio",
2027
+ accessibilityLabel: u.etikett,
2028
+ accessibilityState: { checked: aktiv },
2029
+ onPress: () => setValgt(u.verdi),
2030
+ style: {
2031
+ flex: 1,
2032
+ minHeight: tema.touch,
2033
+ alignItems: "center",
2034
+ justifyContent: "center",
2035
+ gap: tema.space.xs,
2036
+ padding: tema.space.sm,
2037
+ borderRadius: tema.radius.md,
2038
+ borderWidth: tema.hairline,
2039
+ borderColor: aktiv ? tema.color.brand : tema.color.borderStrong,
2040
+ backgroundColor: aktiv ? tema.color.brandBg : tema.color.surfaceRaised
2041
+ },
2042
+ children: [
2043
+ /* @__PURE__ */ jsx12(Text10, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.h3, color: tema.color.textPrimary }, children: u.ikon }),
2044
+ /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textPrimary, fontSize: tema.font.small, fontWeight: aktiv ? tema.weight.semibold : tema.weight.regular }, children: u.etikett })
2045
+ ]
2046
+ },
2047
+ u.verdi
2048
+ );
2049
+ }) }),
2050
+ /* @__PURE__ */ jsxs11(View12, { style: { gap: tema.space.xs }, children: [
2051
+ /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textSecondary, fontSize: tema.font.small }, children: tekst.setningLabel }),
2052
+ /* @__PURE__ */ jsx12(
2053
+ TextInput2,
2054
+ {
2055
+ testID: "hvordan-setning",
2056
+ accessibilityLabel: tekst.setningLabel,
2057
+ value: setning,
2058
+ onChangeText: setSetning,
2059
+ maxLength: SETNING_MAKS,
2060
+ multiline: true,
2061
+ numberOfLines: 3,
2062
+ placeholder: tekst.setningPlaceholder,
2063
+ placeholderTextColor: tema.color.textMuted,
2064
+ style: {
2065
+ backgroundColor: tema.color.surfaceRaised,
2066
+ borderWidth: tema.hairline,
2067
+ borderColor: tema.color.borderStrong,
2068
+ borderRadius: tema.radius.md,
2069
+ padding: tema.space.md,
2070
+ color: tema.color.textPrimary,
2071
+ fontSize: tema.font.body,
2072
+ lineHeight: tema.lineHeight.body,
2073
+ minHeight: tema.touch * 2,
2074
+ textAlignVertical: "top"
2075
+ }
2076
+ }
2077
+ )
2078
+ ] }),
2079
+ feil ? /* @__PURE__ */ jsx12(Banner, { live: true, ikon: "\u26A0", tone: "danger", children: tekst.svarFeil(feil) }) : null,
2080
+ /* @__PURE__ */ jsx12(
2081
+ Btn,
2082
+ {
2083
+ testID: "hvordan-lagre",
2084
+ label: tekst.lagre,
2085
+ hint: tekst.lagreHint,
2086
+ disabled: valgt === void 0 || sender,
2087
+ onPress: () => {
2088
+ if (valgt === void 0) return;
2089
+ const trimmet = setning.trim();
2090
+ onLagre(valgt, trimmet.length > 0 ? trimmet : void 0);
2091
+ }
2092
+ }
2093
+ )
2094
+ ]
2095
+ }
2096
+ ) }) });
2097
+ }
2098
+
2099
+ // src/program/Oppfolging.tsx
2100
+ import "react";
2101
+ import { Text as Text11, View as View13 } from "react-native";
2102
+ import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
2103
+ function tarSvar(kind) {
2104
+ return kind === "hvordan_gikk_det" || kind === "sjekkpunkt";
2105
+ }
2106
+ function Oppfolging({
2107
+ oppfolging,
2108
+ kontaktNr,
2109
+ maksKontakter,
2110
+ onSenere,
2111
+ onSvar,
2112
+ onAapneOppgavene,
2113
+ onGaaUt
2114
+ }) {
2115
+ const tema = useTema();
2116
+ const art = tarSvar(oppfolging.kind) ? "spoersmaal" : oppfolging.kind === "tilbud_om_utgang" ? "utgang" : "paaminnelse";
2117
+ const ikon = art === "spoersmaal" ? "\u{1F9EA}" : art === "utgang" ? "\u{1F6AA}" : "\u{1F4C5}";
2118
+ const hint = art === "spoersmaal" ? tekst.oppfolgingKontakt(kontaktNr, maksKontakter) : art === "utgang" ? tekst.oppfolgingUtgangHint(kontaktNr, maksKontakter) : tekst.oppfolgingPaaminnelseHint(kontaktNr, maksKontakter);
2119
+ return /* @__PURE__ */ jsxs12(
2120
+ View13,
2121
+ {
2122
+ testID: `oppfolging-${oppfolging.id}`,
2123
+ accessibilityLabel: tekst.oppfolgingRegion,
2124
+ style: {
2125
+ backgroundColor: tema.color.brandBg,
2126
+ borderWidth: tema.hairline,
2127
+ borderColor: tema.color.brand,
2128
+ borderRadius: tema.radius.lg,
2129
+ padding: tema.space.md,
2130
+ gap: tema.space.sm
2131
+ },
2132
+ children: [
2133
+ /* @__PURE__ */ jsxs12(View13, { style: { flexDirection: "row", gap: tema.space.sm, alignItems: "flex-start" }, children: [
2134
+ /* @__PURE__ */ jsx13(Text11, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.h3 }, children: ikon }),
2135
+ /* @__PURE__ */ jsxs12(View13, { style: { flex: 1, gap: tema.space.xs }, children: [
2136
+ /* @__PURE__ */ jsx13(Text11, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: oppfolging.question }),
2137
+ /* @__PURE__ */ jsx13(Hint, { children: hint })
2138
+ ] })
2139
+ ] }),
2140
+ /* @__PURE__ */ jsx13(View13, { style: { flexDirection: "row", gap: tema.space.sm }, children: art === "utgang" ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
2141
+ /* @__PURE__ */ jsx13(Btn, { grow: true, variant: "secondary", testID: `bli-vaerende-${oppfolging.id}`, label: tekst.bliVaerende, onPress: onSenere }),
2142
+ /* @__PURE__ */ jsx13(Btn, { grow: true, variant: "danger", testID: `gaa-ut-${oppfolging.id}`, ikon: "\u{1F6AA}", label: tekst.gaaUtBekreft, onPress: onGaaUt })
2143
+ ] }) : /* @__PURE__ */ jsxs12(Fragment2, { children: [
2144
+ /* @__PURE__ */ jsx13(Btn, { grow: true, variant: "secondary", testID: `senere-${oppfolging.id}`, label: tekst.senere, onPress: onSenere }),
2145
+ art === "spoersmaal" ? /* @__PURE__ */ jsx13(Btn, { grow: true, testID: `svar-naa-${oppfolging.id}`, label: tekst.svarNaa, onPress: onSvar }) : /* @__PURE__ */ jsx13(Btn, { grow: true, testID: `aapne-oppgavene-${oppfolging.id}`, label: tekst.aapneOppgavene, onPress: onAapneOppgavene })
2146
+ ] }) })
2147
+ ]
2148
+ }
2149
+ );
2150
+ }
2151
+
2152
+ // src/program/Oppgaver.tsx
2153
+ import "react";
2154
+ import { Text as Text12, View as View14 } from "react-native";
2155
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
2156
+ function Oppgaver({
2157
+ tasks,
2158
+ completions,
2159
+ onGjort
2160
+ }) {
2161
+ const tema = useTema();
2162
+ const ferdig = new Map(completions.map((c) => [c.taskId, c]));
2163
+ return /* @__PURE__ */ jsx14(View14, { testID: "oppgaver", accessibilityLabel: tekst.oppgaverTittel, style: { gap: tema.space.sm }, children: [...tasks].sort((a, b) => a.order - b.order).map((o) => {
2164
+ const c = ferdig.get(o.id);
2165
+ return /* @__PURE__ */ jsxs13(
2166
+ View14,
2167
+ {
2168
+ testID: `oppgave-${o.id}`,
2169
+ style: {
2170
+ flexDirection: "row",
2171
+ alignItems: "center",
2172
+ gap: tema.space.sm,
2173
+ paddingVertical: tema.space.xs,
2174
+ borderBottomWidth: tema.hairline,
2175
+ borderBottomColor: tema.color.border
2176
+ },
2177
+ children: [
2178
+ /* @__PURE__ */ jsxs13(View14, { style: { flex: 1, gap: tema.space.xs }, children: [
2179
+ /* @__PURE__ */ jsx14(Text12, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: o.title }),
2180
+ /* @__PURE__ */ jsxs13(Hint, { children: [
2181
+ c ? tekst.oppgaveGjortMed(utfallEtikett(c.outcome)) : `${tekst.oppgaveIkkeBegynt} \xB7 ${tekst.akseptOppgaveTid(o.estimatedMinutes)}`,
2182
+ c?.text ? ` \xB7 \xAB${c.text}\xBB` : ""
2183
+ ] })
2184
+ ] }),
2185
+ c ? /* @__PURE__ */ jsx14(Text12, { accessibilityLabel: tekst.oppgaveGjortMed(utfallEtikett(c.outcome)), style: { color: tema.color.success, fontSize: tema.font.h3 }, children: "\u2713" }) : /* @__PURE__ */ jsx14(Btn, { variant: "secondary", testID: `gjort-${o.id}`, ikon: "\u2713", label: tekst.oppgaveGjort, accessibilityLabel: tekst.oppgaveGjortLabel(o.title), onPress: () => onGjort(o) })
2186
+ ]
2187
+ },
2188
+ o.id
2189
+ );
2190
+ }) });
2191
+ }
2192
+
1952
2193
  // src/program/InnspelProgram.tsx
1953
- import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2194
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1954
2195
  function InnspelProgram({
1955
2196
  invitationToken,
1956
- onEndring
2197
+ onEndring,
2198
+ onMeldFeil
1957
2199
  }) {
1958
2200
  const { client } = useInnspel();
1959
2201
  const tema = useTema();
1960
- const [mine, setMine] = useState5();
1961
- const [deltakelse, setDeltakelse] = useState5();
1962
- const [viserAksept, setViserAksept] = useState5(false);
1963
- const [viserGaaUt, setViserGaaUt] = useState5(false);
1964
- const [avvist, setAvvist] = useState5(false);
1965
- const [sender, setSender] = useState5(false);
1966
- const [feil, setFeil] = useState5();
2202
+ const [mine, setMine] = useState6();
2203
+ const [deltakelse, setDeltakelse] = useState6();
2204
+ const [viserAksept, setViserAksept] = useState6(false);
2205
+ const [viserGaaUt, setViserGaaUt] = useState6(false);
2206
+ const [avvist, setAvvist] = useState6(false);
2207
+ const [sender, setSender] = useState6(false);
2208
+ const [feil, setFeil] = useState6();
2209
+ const [dialog, setDialog] = useState6();
2210
+ const [svarFeil, setSvarFeil] = useState6();
2211
+ const [lagtBort, setLagtBort] = useState6(/* @__PURE__ */ new Set());
2212
+ const [meldFeilFor, setMeldFeilFor] = useState6();
2213
+ const leggBort = useCallback4((id) => setLagtBort((s) => /* @__PURE__ */ new Set([...s, id])), []);
2214
+ const lagreSvar = useCallback4(
2215
+ async (outcome, text) => {
2216
+ if (!dialog || !deltakelse) return;
2217
+ setSender(true);
2218
+ setSvarFeil(void 0);
2219
+ try {
2220
+ if (dialog.type === "oppgave") {
2221
+ const ny = await client.programs.completeTask(dialog.task.id, { outcome, ...text ? { text } : {} });
2222
+ setDeltakelse({ ...deltakelse, completions: [...deltakelse.completions, ny] });
2223
+ if (outcome === "ikke" && onMeldFeil) setMeldFeilFor(dialog.task);
2224
+ } else {
2225
+ await client.programs.respond(dialog.oppfolging.id, { outcome, ...text ? { text } : {} });
2226
+ setMine((m) => m ? { ...m, pendingFollowUps: m.pendingFollowUps.filter((f) => f.id !== dialog.oppfolging.id) } : m);
2227
+ }
2228
+ setDialog(void 0);
2229
+ } catch (e) {
2230
+ setSvarFeil(e instanceof InnspelApiError2 ? e.message : "tjeneren svarte ikke.");
2231
+ } finally {
2232
+ setSender(false);
2233
+ }
2234
+ },
2235
+ [client, deltakelse, dialog, onMeldFeil]
2236
+ );
1967
2237
  useEffect3(() => {
1968
2238
  if (client.isAnonymous) return;
1969
2239
  let levende = true;
@@ -2009,14 +2279,69 @@ function InnspelProgram({
2009
2279
  const program = deltakelse?.program ?? invitasjon?.program;
2010
2280
  if (!program) return null;
2011
2281
  if (deltakelse) {
2012
- return /* @__PURE__ */ jsxs11(View12, { testID: "program-deltakelse", accessibilityLabel: tekst.deltakelseRegion, style: { gap: tema.space.sm }, children: [
2013
- /* @__PURE__ */ jsxs11(Card, { children: [
2014
- /* @__PURE__ */ jsx12(H, { sub: tekst.deltakelsePeriode(periode(program.opensAt, program.closesAt), deltakelse.contactsSent, program.maxContacts), children: program.name }),
2015
- /* @__PURE__ */ jsx12(Hint, { children: tekst.deltakelseOppgaverKommer }),
2016
- /* @__PURE__ */ jsx12(Btn, { variant: "ghost", testID: "gaa-ut", ikon: "\u{1F6AA}", label: tekst.gaaUt, onPress: () => setViserGaaUt(true) })
2282
+ const kort = (mine?.pendingFollowUps ?? []).filter((f) => f.programId === program.id && !lagtBort.has(f.id)).sort((a, b) => a.emittedAt.localeCompare(b.emittedAt));
2283
+ const forsteKontaktNr = Math.max(1, deltakelse.contactsSent - kort.length + 1);
2284
+ return /* @__PURE__ */ jsxs14(View15, { testID: "program-deltakelse", accessibilityLabel: tekst.deltakelseRegion, style: { gap: tema.space.sm }, children: [
2285
+ kort.map((f, i) => /* @__PURE__ */ jsx15(
2286
+ Oppfolging,
2287
+ {
2288
+ oppfolging: f,
2289
+ kontaktNr: forsteKontaktNr + i,
2290
+ maksKontakter: program.maxContacts,
2291
+ onSenere: () => leggBort(f.id),
2292
+ onSvar: () => {
2293
+ setSvarFeil(void 0);
2294
+ setDialog({ type: "oppfolging", oppfolging: f });
2295
+ },
2296
+ onAapneOppgavene: () => leggBort(f.id),
2297
+ onGaaUt: () => setViserGaaUt(true)
2298
+ },
2299
+ f.id
2300
+ )),
2301
+ /* @__PURE__ */ jsxs14(Card, { children: [
2302
+ /* @__PURE__ */ jsx15(H, { sub: tekst.deltakelsePeriode(periode(program.opensAt, program.closesAt), deltakelse.contactsSent, program.maxContacts), children: program.name }),
2303
+ /* @__PURE__ */ jsx15(
2304
+ Oppgaver,
2305
+ {
2306
+ tasks: program.tasks,
2307
+ completions: deltakelse.completions,
2308
+ onGjort: (task) => {
2309
+ setSvarFeil(void 0);
2310
+ setDialog({ type: "oppgave", task });
2311
+ }
2312
+ }
2313
+ ),
2314
+ meldFeilFor && onMeldFeil ? /* @__PURE__ */ jsxs14(View15, { style: { gap: tema.space.xs }, children: [
2315
+ /* @__PURE__ */ jsx15(Hint, { children: tekst.meldFeilHint }),
2316
+ /* @__PURE__ */ jsx15(
2317
+ Btn,
2318
+ {
2319
+ variant: "secondary",
2320
+ testID: "meld-feil",
2321
+ ikon: "\u{1F41E}",
2322
+ label: tekst.meldFeil,
2323
+ onPress: () => {
2324
+ const task = meldFeilFor;
2325
+ setMeldFeilFor(void 0);
2326
+ onMeldFeil(task);
2327
+ }
2328
+ }
2329
+ )
2330
+ ] }) : null,
2331
+ /* @__PURE__ */ jsx15(Btn, { variant: "ghost", testID: "gaa-ut", ikon: "\u{1F6AA}", label: tekst.gaaUt, onPress: () => setViserGaaUt(true) })
2017
2332
  ] }),
2018
- viserGaaUt ? /* @__PURE__ */ jsx12(Modal3, { visible: true, transparent: true, animationType: "fade", onRequestClose: () => setViserGaaUt(false), accessibilityViewIsModal: true, children: /* @__PURE__ */ jsx12(View12, { style: { flex: 1, backgroundColor: tema.color.scrim, justifyContent: "flex-end", padding: tema.space.md }, children: /* @__PURE__ */ jsxs11(
2019
- View12,
2333
+ dialog ? /* @__PURE__ */ jsx15(
2334
+ HvordanGikkDet,
2335
+ {
2336
+ under: dialog.type === "oppgave" ? dialog.task.title : dialog.oppfolging.question,
2337
+ sender,
2338
+ ...svarFeil ? { feil: svarFeil } : {},
2339
+ onLagre: (outcome, text) => void lagreSvar(outcome, text),
2340
+ onLukk: () => setDialog(void 0)
2341
+ }
2342
+ ) : null,
2343
+ viserGaaUt ? /* @__PURE__ */ jsx15(Modal4, { visible: true, transparent: true, animationType: "fade", onRequestClose: () => setViserGaaUt(false), accessibilityViewIsModal: true, children: /* @__PURE__ */ jsx15(View15, { style: { flex: 1, backgroundColor: tema.color.scrim, justifyContent: "flex-end", padding: tema.space.md }, children: /* @__PURE__ */ jsxs14(
2344
+ View15,
2020
2345
  {
2021
2346
  testID: "gaa-ut-dialog",
2022
2347
  accessibilityViewIsModal: true,
@@ -2029,11 +2354,11 @@ function InnspelProgram({
2029
2354
  gap: tema.space.md
2030
2355
  },
2031
2356
  children: [
2032
- /* @__PURE__ */ jsx12(H, { children: tekst.gaaUtTittel }),
2033
- /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: tekst.gaaUtTekst(program.name) }),
2034
- /* @__PURE__ */ jsxs11(View12, { style: { flexDirection: "row", gap: tema.space.sm }, children: [
2035
- /* @__PURE__ */ jsx12(Btn, { grow: true, variant: "secondary", testID: "bli-vaerende", label: tekst.bliVaerende, onPress: () => setViserGaaUt(false) }),
2036
- /* @__PURE__ */ jsx12(Btn, { grow: true, variant: "danger", testID: "bekreft-gaa-ut", ikon: "\u{1F6AA}", label: tekst.gaaUtBekreft, disabled: sender, onPress: () => void gaaUt() })
2357
+ /* @__PURE__ */ jsx15(H, { children: tekst.gaaUtTittel }),
2358
+ /* @__PURE__ */ jsx15(Text13, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: tekst.gaaUtTekst(program.name) }),
2359
+ /* @__PURE__ */ jsxs14(View15, { style: { flexDirection: "row", gap: tema.space.sm }, children: [
2360
+ /* @__PURE__ */ jsx15(Btn, { grow: true, variant: "secondary", testID: "bli-vaerende", label: tekst.bliVaerende, onPress: () => setViserGaaUt(false) }),
2361
+ /* @__PURE__ */ jsx15(Btn, { grow: true, variant: "danger", testID: "bekreft-gaa-ut", ikon: "\u{1F6AA}", label: tekst.gaaUtBekreft, disabled: sender, onPress: () => void gaaUt() })
2037
2362
  ] })
2038
2363
  ]
2039
2364
  }
@@ -2041,9 +2366,9 @@ function InnspelProgram({
2041
2366
  ] });
2042
2367
  }
2043
2368
  if (!invitasjon || avvist) return null;
2044
- return /* @__PURE__ */ jsxs11(Fragment2, { children: [
2045
- /* @__PURE__ */ jsxs11(
2046
- View12,
2369
+ return /* @__PURE__ */ jsxs14(Fragment3, { children: [
2370
+ /* @__PURE__ */ jsxs14(
2371
+ View15,
2047
2372
  {
2048
2373
  testID: "program-banner",
2049
2374
  accessibilityLabel: tekst.invitasjonRegion,
@@ -2056,30 +2381,30 @@ function InnspelProgram({
2056
2381
  gap: tema.space.sm
2057
2382
  },
2058
2383
  children: [
2059
- /* @__PURE__ */ jsxs11(View12, { style: { flexDirection: "row", gap: tema.space.sm, alignItems: "flex-start" }, children: [
2060
- /* @__PURE__ */ jsx12(Text10, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.h3 }, children: "\u{1F9EA}" }),
2061
- /* @__PURE__ */ jsxs11(View12, { style: { flex: 1, gap: tema.space.xs }, children: [
2062
- /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: tekst.invitasjonHva(program.name) }),
2063
- /* @__PURE__ */ jsx12(Hint, { children: tekst.invitasjonKontakter(program.maxContacts) })
2384
+ /* @__PURE__ */ jsxs14(View15, { style: { flexDirection: "row", gap: tema.space.sm, alignItems: "flex-start" }, children: [
2385
+ /* @__PURE__ */ jsx15(Text13, { accessibilityElementsHidden: true, importantForAccessibility: "no", style: { fontSize: tema.font.h3 }, children: "\u{1F9EA}" }),
2386
+ /* @__PURE__ */ jsxs14(View15, { style: { flex: 1, gap: tema.space.xs }, children: [
2387
+ /* @__PURE__ */ jsx15(Text13, { style: { color: tema.color.textPrimary, fontSize: tema.font.body, lineHeight: tema.lineHeight.body }, children: tekst.invitasjonHva(program.name) }),
2388
+ /* @__PURE__ */ jsx15(Hint, { children: tekst.invitasjonKontakter(program.maxContacts) })
2064
2389
  ] }),
2065
- /* @__PURE__ */ jsx12(
2066
- Pressable7,
2390
+ /* @__PURE__ */ jsx15(
2391
+ Pressable8,
2067
2392
  {
2068
2393
  testID: "avvis-invitasjon",
2069
2394
  accessibilityRole: "button",
2070
2395
  accessibilityLabel: tekst.avvisInvitasjon,
2071
2396
  onPress: () => setAvvist(true),
2072
2397
  style: { minWidth: tema.touch, minHeight: tema.touch, alignItems: "center", justifyContent: "center" },
2073
- children: /* @__PURE__ */ jsx12(Text10, { style: { color: tema.color.textSecondary, fontSize: tema.font.body }, children: "\u2715" })
2398
+ children: /* @__PURE__ */ jsx15(Text13, { style: { color: tema.color.textSecondary, fontSize: tema.font.body }, children: "\u2715" })
2074
2399
  }
2075
2400
  )
2076
2401
  ] }),
2077
- feil && !viserAksept ? /* @__PURE__ */ jsx12(Banner, { live: true, ikon: "\u26A0", tone: "danger", children: tekst.akseptFeil(feil) }) : null,
2078
- /* @__PURE__ */ jsx12(Btn, { variant: "secondary", testID: "les-mer", label: tekst.lesMer, onPress: () => setViserAksept(true) })
2402
+ feil && !viserAksept ? /* @__PURE__ */ jsx15(Banner, { live: true, ikon: "\u26A0", tone: "danger", children: tekst.akseptFeil(feil) }) : null,
2403
+ /* @__PURE__ */ jsx15(Btn, { variant: "secondary", testID: "les-mer", label: tekst.lesMer, onPress: () => setViserAksept(true) })
2079
2404
  ]
2080
2405
  }
2081
2406
  ),
2082
- viserAksept ? /* @__PURE__ */ jsx12(
2407
+ viserAksept ? /* @__PURE__ */ jsx15(
2083
2408
  Aksept,
2084
2409
  {
2085
2410
  program,