Før kalenderarbeid

This commit is contained in:
ErolHaagenrud 2025-12-11 13:37:05 +01:00
parent c23b4ba3d3
commit 0c33b4fc0f
3 changed files with 528 additions and 133 deletions

View file

@ -49,6 +49,8 @@ import androidx.fragment.app.Fragment;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import org.json.JSONArray;
import org.json.JSONException;
@ -71,7 +73,6 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
@ -105,7 +106,7 @@ public class FormsFragment extends Fragment {
private TextView txtStatus;
private TextView lblHistory;
private ProgressBar loadingSpinner;
private ImageView btnToggleHistory; // NY: Knapp for å vise/skjule historikk
private ImageView btnToggleHistory;
// --- HOVEDSKJEMA STATE ---
private Map<String, View> fieldWrappers = new HashMap<>();
@ -183,12 +184,10 @@ public class FormsFragment extends Fragment {
lblHistory = view.findViewById(R.id.lbl_history);
loadingSpinner = view.findViewById(R.id.loading_spinner);
// --- NY KODE START: Vis/Skjul knapp ---
btnToggleHistory = view.findViewById(R.id.btn_toggle_history);
if (btnToggleHistory != null) {
btnToggleHistory.setOnClickListener(v -> toggleHistoryVisibility());
}
// --- NY KODE SLUTT ---
// --- FIKS FOR NULLPOINTER EXCEPTION LAYOUTTRANSITION ---
if (view instanceof ViewGroup) {
@ -217,28 +216,24 @@ public class FormsFragment extends Fragment {
// --- UI LOGIKK FOR DELT SKJERM ---
// Kalles når brukeren interagerer med et felt i skjemaet
private void expandFormModule() {
if (historyWrapper != null && historyWrapper.getVisibility() == View.VISIBLE) {
historyWrapper.setVisibility(View.GONE);
// Oppdater ikonet til å peke NED (for å vise at man kan hente historikk ned igjen)
if (btnToggleHistory != null) {
btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
}
}
}
// NY METODE: Håndterer klikk pil-knappen
private void toggleHistoryVisibility() {
if (historyWrapper == null || btnToggleHistory == null) return;
if (historyWrapper.getVisibility() == View.VISIBLE) {
// Skjul historikk ( til fullskjerm)
// Skjul historikk
historyWrapper.setVisibility(View.GONE);
btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
} else {
// Vis historikk ( til splitscreen)
// Vis historikk
historyWrapper.setVisibility(View.VISIBLE);
btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
}
@ -246,20 +241,17 @@ public class FormsFragment extends Fragment {
private void attachInteractionListener(View view) {
if (view == null) return;
// Touch listener fanger opp klikk før tastaturet kommer opp
view.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
expandFormModule();
}
return false; // Return false to allow normal processing
return false;
});
// Focus listener for edittexts etc
view.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
expandFormModule();
}
});
// Click listener for buttons/checkboxes
if (view.isClickable()) {
view.setOnClickListener(v -> expandFormModule());
}
@ -308,7 +300,7 @@ public class FormsFragment extends Fragment {
nestedEntries.clear();
updateStatus("");
// Reset visibility of history on new load (Splitscreen start)
// Reset visibility of history on new load
if (historyWrapper != null) {
historyWrapper.setVisibility(View.VISIBLE);
if (btnToggleHistory != null) btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
@ -444,7 +436,8 @@ public class FormsFragment extends Fragment {
childFormId = Integer.parseInt(field.gpnfForm);
} catch (NumberFormatException e) { e.printStackTrace(); }
}
openChildFormDialog(childFormId);
// NYTT: Sender med felt-ID fra hovedskjemaet (f.eks "25")
openChildFormDialog(childFormId, field.id);
});
container.addView(btnAdd);
@ -462,7 +455,8 @@ public class FormsFragment extends Fragment {
container.addView(totalAmountView);
}
private void openChildFormDialog(int childFormId) {
// NY SIGNATUR: Tar imot parentFieldId
private void openChildFormDialog(int childFormId, String parentFieldId) {
if (getActivity() == null) return;
ProgressBar pBar = new ProgressBar(getContext());
AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
@ -475,7 +469,7 @@ public class FormsFragment extends Fragment {
public void onResponse(retrofit2.Call<GravityForm> call, retrofit2.Response<GravityForm> response) {
loadingDialog.dismiss();
if (response.isSuccessful() && response.body() != null) {
showChildFormDialog(response.body());
showChildFormDialog(response.body(), parentFieldId);
} else {
Toast.makeText(getContext(), "Kunne ikke hente underskjema", Toast.LENGTH_SHORT).show();
}
@ -489,7 +483,8 @@ public class FormsFragment extends Fragment {
});
}
private void showChildFormDialog(GravityForm childForm) {
// NY SIGNATUR: Tar imot parentFieldId
private void showChildFormDialog(GravityForm childForm, String parentFieldId) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
childInputViews.clear();
childRequiredFieldsMap.clear();
@ -534,11 +529,12 @@ public class FormsFragment extends Fragment {
dialog.show();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
submitChildForm(childForm.id, dialog);
submitChildForm(childForm.id, dialog, parentFieldId);
});
}
private void submitChildForm(int childFormId, AlertDialog dialog) {
// NY SIGNATUR: Tar imot parentFieldId og sender den som meta
private void submitChildForm(int childFormId, AlertDialog dialog, String parentFieldId) {
JSONObject inputValues = new JSONObject();
for (Map.Entry<String, View> entry : childInputViews.entrySet()) {
String val = getInputValueGeneric(entry.getValue());
@ -562,6 +558,15 @@ public class FormsFragment extends Fragment {
textParts.put(key, RequestBody.create(MultipartBody.FORM, val));
}
}
// --- HER ER FIKSEN FOR BUGGEN ---
// Vi legger ved en ekstra parameter som forteller Gravity Forms at dette
// er en nested entry som hører til et bestemt felt (f.eks "25").
if (parentFieldId != null) {
textParts.put("gpnf_entry_nested_form_field", RequestBody.create(MultipartBody.FORM, parentFieldId));
}
// ---------------------------------
for (Map.Entry<String, Uri> fileEntry : childFileUploads.entrySet()) {
String fieldId = fileEntry.getKey();
Uri uri = fileEntry.getValue();
@ -1196,11 +1201,11 @@ public class FormsFragment extends Fragment {
String url = BASE_URL_GF + "/forms/" + formId + "/submissions";
Request request = new Request.Builder().url(url).post(body).header("Cookie", cookie).build();
client.newCall(request).enqueue(new okhttp3.Callback() {
public void onFailure(Call call, IOException e) {
public void onFailure(okhttp3.Call call, IOException e) {
Log.e(TAG, "JSON submit failed", e);
updateStatus("Feil: " + e.getMessage());
}
public void onResponse(Call call, Response response) {
public void onResponse(okhttp3.Call call, Response response) {
Log.d(TAG, "JSON response code: " + response.code());
if (response.isSuccessful()) {
if (getActivity() != null) getActivity().runOnUiThread(() -> {
@ -1313,12 +1318,12 @@ public class FormsFragment extends Fragment {
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) {
Log.e(TAG, "Kunne ikke hente historikk", e);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
public void onResponse(@NonNull okhttp3.Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
String jsonStr = response.body().string();
try {
@ -1382,52 +1387,205 @@ public class FormsFragment extends Fragment {
}
}
// NY METODE: Vis detaljer i dialog
// NY METODE: Vis detaljer i dialog med delingsknapp
private void showEntryDetails(JSONObject entry) {
StringBuilder details = new StringBuilder();
try {
// Loop gjennom alle felter i entry og match med skjema-definisjon
// Merk: Entry keys er felt-IDer (f.eks "1", "3.2")
// HVIS dette er Form 16 (Refusjon), vi først hente vedleggene (som ligger i Form 18)
if (formId == ID_REFUSJON_UTLEGG) {
Log.d(TAG, "Form 16 detected. Checking for child entries...");
String nestedIds = entry.optString("25"); // Felt 25 i Form 16 inneholder ID-ene til vedleggene
if (!nestedIds.isEmpty()) {
Log.d(TAG, "Nested IDs found: " + nestedIds);
List<String> ids = new ArrayList<>();
// ROBUST PARSING AV ID-LISTE
if (nestedIds.startsWith("[") && nestedIds.endsWith("]")) {
// Dette er en JSON-array streng (f.eks [101, 102])
try {
JSONArray jsonArray = new JSONArray(nestedIds);
for(int i=0; i<jsonArray.length(); i++) {
ids.add(jsonArray.getString(i));
}
} catch(JSONException e) {
Log.e(TAG, "Failed to parse nested IDs as JSON array", e);
}
} else {
// Dette er en komma-separert streng (f.eks "101, 102")
for (String id : nestedIds.split(",")) {
ids.add(id.trim());
}
}
if (!ids.isEmpty()) {
// Vis en "Laster..." dialog mens vi henter data
AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
.setMessage("Henter vedleggsdetaljer...")
.setCancelable(false)
.show();
// Start rekursiv henting
StringBuilder accumulatedHtml = new StringBuilder();
StringBuilder accumulatedText = new StringBuilder();
// Legg først til basis-info fra hovedskjemaet
appendBasicInfo(entry, accumulatedHtml, accumulatedText);
fetchChildEntriesRecursive(ids, 0, accumulatedHtml, accumulatedText, loadingDialog);
return; // Stopp her, dialogen vises når data er hentet
}
} else {
Log.d(TAG, "No nested IDs found in field 25.");
}
}
// --- STANDARD VISNING (For alle andre skjemaer) ---
StringBuilder htmlBuilder = new StringBuilder();
StringBuilder textBuilder = new StringBuilder();
appendBasicInfo(entry, htmlBuilder, textBuilder);
showFinalDialog(htmlBuilder, textBuilder);
}
// Hjelpemetode for å legge til info fra hovedskjemaet
private void appendBasicInfo(JSONObject entry, StringBuilder html, StringBuilder text) {
try {
String date = entry.optString("date_created");
html.append("<b>Innsendt:</b> ").append(date).append("<br><br>");
text.append("Innsendt: ").append(date).append("\n\n");
// Dato
details.append("<b>Innsendt:</b> ").append(entry.optString("date_created")).append("<br><br>");
// Iterer gjennom feltene i skjema-definisjonen for å riktig rekkefølge
if (currentForm != null && currentForm.fields != null) {
for (GravityField field : currentForm.fields) {
if ("section".equals(field.type) || "html".equals(field.type) || "captcha".equals(field.type)) continue;
// Hopp over felt 25 (Vedleggs-IDer) i Form 16, siden vi viser det bedre senere
if (formId == ID_REFUSJON_UTLEGG && "25".equals(field.id)) continue;
String value = "";
if (field.inputs != null && !field.inputs.isEmpty()) {
// Composite fields (Address, Name)
for (GravityField input : field.inputs) {
String subVal = entry.optString(input.id);
if (!subVal.isEmpty()) {
value += " " + subVal;
}
if (!subVal.isEmpty()) value += " " + subVal;
}
} else {
// Standard field
value = entry.optString(String.valueOf(field.id));
}
if (!value.trim().isEmpty()) {
// For filopplastinger er verdien ofte en URL.
if ("fileupload".equals(field.type)) {
value = "(Vedlegg)";
// Håndter filopplastinger (single/multiple)
// Her antar vi enkel URL, men for sikkerhets skyld bruker vi extractUrl
String cleanUrl = extractUrl(value);
if (cleanUrl.startsWith("http")) {
html.append("<b>").append(field.label).append(":</b><br>")
.append("<a href=\"").append(cleanUrl).append("\">Åpne fil</a><br><br>");
text.append(field.label).append(":\n").append(cleanUrl).append("\n\n");
} else {
html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
text.append(field.label).append(":\n").append(value).append("\n\n");
}
} else {
html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
text.append(field.label).append(":\n").append(value).append("\n\n");
}
details.append("<b>").append(field.label).append(":</b><br>")
.append(value).append("<br><br>");
}
}
}
} catch (Exception e) {}
}
} catch (Exception e) {
details.append("Kunne ikke vise detaljer.");
// Rekursiv metode for å hente barn-oppføringer (Vedlegg)
private void fetchChildEntriesRecursive(List<String> ids, int index, StringBuilder html, StringBuilder text, AlertDialog loader) {
if (index >= ids.size()) {
// Alle ferdige! Vis dialogen.
loader.dismiss();
showFinalDialog(html, text);
return;
}
String entryId = ids.get(index);
Log.d(TAG, "Fetching child entry ID: " + entryId);
RetrofitClient.getApiService().getSingleEntry(entryId).enqueue(new retrofit2.Callback<JsonElement>() {
@Override
public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
if (response.isSuccessful() && response.body() != null) {
try {
JsonObject json = response.body().getAsJsonObject();
// I Form 18: Felt 1 = Fil, Felt 3 = Beskrivelse, Felt 4 = Beløp
// Parse Description and Price
String desc = json.has("3") ? json.get("3").getAsString() : "Uten beskrivelse";
String price = json.has("4") ? json.get("4").getAsString() : "";
html.append("<b>Vedlegg ").append(index + 1).append(":</b><br>");
text.append("Vedlegg ").append(index + 1).append(":\n");
html.append(desc).append(" (").append(price).append(")<br>");
text.append(desc).append(" (").append(price).append(")\n");
// Parse File Field (ID 1) - Can be multiple files!
if (json.has("1")) {
JsonElement fileEl = json.get("1");
if (fileEl.isJsonArray()) {
// It is a real JSON array
JsonArray arr = fileEl.getAsJsonArray();
for (int i = 0; i < arr.size(); i++) {
String url = arr.get(i).getAsString().replace("\\/", "/");
html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
text.append(url).append("\n");
}
} else {
// It is a string. Check if it's a JSON string array like "[\"http...\"]"
String rawString = fileEl.getAsString();
if (rawString.startsWith("[") && rawString.endsWith("]")) {
try {
JSONArray arr = new JSONArray(rawString);
for (int i = 0; i < arr.length(); i++) {
String url = arr.getString(i).replace("\\/", "/");
html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
text.append(url).append("\n");
}
} catch (JSONException ex) {
// Fallback: simple cleanup
String clean = extractUrl(rawString);
html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
text.append(clean).append("\n");
}
} else {
// Just a plain URL string
String clean = extractUrl(rawString);
if(clean.startsWith("http")) {
html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
text.append(clean).append("\n");
}
}
}
}
html.append("<br>");
text.append("\n");
} catch (Exception e) {
Log.e(TAG, "Error parsing child entry", e);
}
} else {
Log.e(TAG, "Failed to fetch child entry: " + response.code());
}
// til neste (uansett om denne feilet eller ei)
fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
}
@Override
public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) {
Log.e(TAG, "Network error fetching child entry", t);
// Hopp over ved feil
fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
}
});
}
private void showFinalDialog(StringBuilder htmlBuilder, StringBuilder textBuilder) {
ScrollView scroll = new ScrollView(getContext());
TextView text = new TextView(getContext());
text.setText(Html.fromHtml(details.toString(), Html.FROM_HTML_MODE_COMPACT));
text.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
text.setText(Html.fromHtml(htmlBuilder.toString(), Html.FROM_HTML_MODE_COMPACT));
text.setPadding(40, 40, 40, 40);
scroll.addView(text);
@ -1435,9 +1593,31 @@ public class FormsFragment extends Fragment {
.setTitle("Detaljer")
.setView(scroll)
.setPositiveButton("Lukk", null)
.setNeutralButton("Del", (d, w) -> shareEntryDetails(textBuilder.toString()))
.create()
.show();
}
// Hjelpemetode for å rydde opp i URLer fra JSON (f.eks ["http://..."] -> http://...)
private String extractUrl(String rawValue) {
if (rawValue == null) return "";
String clean = rawValue.replace("[", "")
.replace("]", "")
.replace("\"", "")
.replace("\\/", "/");
return clean.trim();
}
private void shareEntryDetails(String text) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
sendIntent.setType("text/plain");
Intent shareIntent = Intent.createChooser(sendIntent, "Del innsending via...");
startActivity(shareIntent);
}
private void prefillFormFromHistory(JSONObject latestEntry) {
if (latestEntry == null) return;
for (Map.Entry<String, View> entry : inputViews.entrySet()) {

View file

@ -1,4 +1,3 @@
// FILSTI: app\src\main\java\com\kbs\kbsintranett\WordPressApiService.java
package com.kbs.kbsintranett;
import com.google.gson.JsonElement;
@ -14,7 +13,7 @@ import retrofit2.http.Path;
import retrofit2.http.Multipart;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Query; // NYTT
import retrofit2.http.Query;
public interface WordPressApiService {
// 1. Hent nyheter
@ -50,11 +49,15 @@ public interface WordPressApiService {
@GET("wp-json/kbs/v1/calendar/events")
Call<List<CalendarEvent>> getCalendarEvents();
// 8. HENT INNSENDINGER (Entries) - NYTT
// 8. HENT INNSENDINGER (Entries) - LISTE
@GET("wp-json/gf/v2/entries")
Call<GravityEntryResponse> getEntries(
@Query("form_ids") int formId,
@Query("search") String searchJson,
@Query("paging[page_size]") int pageSize
);
// 9. HENT ÉN ENKELT INNSENDING (Ny! Brukes for å hente detaljer fra underskjema)
@GET("wp-json/gf/v2/entries/{entry_id}")
Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId);
}

View file

@ -477,6 +477,7 @@ import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RadioButton;
@ -496,6 +497,8 @@ import androidx.fragment.app.Fragment;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import org.json.JSONArray;
import org.json.JSONException;
@ -518,7 +521,6 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
@ -548,10 +550,11 @@ public class FormsFragment extends Fragment {
private LinearLayout formContainer;
private LinearLayout historyContainer;
private View historyWrapper; // Wrapper for historikk-modulen som skal skjules
private View historyWrapper; // Wrapper for historikk-modulen
private TextView txtStatus;
private TextView lblHistory;
private ProgressBar loadingSpinner;
private ImageView btnToggleHistory;
// --- HOVEDSKJEMA STATE ---
private Map<String, View> fieldWrappers = new HashMap<>();
@ -585,7 +588,6 @@ public class FormsFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
filePickerLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
@ -597,7 +599,6 @@ public class FormsFragment extends Fragment {
}
}
);
takePictureLauncher = registerForActivityResult(
new ActivityResultContracts.TakePicture(),
success -> {
@ -608,7 +609,6 @@ public class FormsFragment extends Fragment {
}
}
);
requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
isGranted -> {
@ -625,16 +625,19 @@ public class FormsFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_forms, container, false);
formContainer = view.findViewById(R.id.form_container);
historyContainer = view.findViewById(R.id.historyContainer);
historyWrapper = view.findViewById(R.id.history_wrapper); // Ny referanse
historyWrapper = view.findViewById(R.id.history_wrapper);
txtStatus = view.findViewById(R.id.txt_status);
lblHistory = view.findViewById(R.id.lbl_history);
loadingSpinner = view.findViewById(R.id.loading_spinner);
// --- FIKS FOR NULLPOINTER EXCEPTION ---
// Vi sjekker om LayoutTransition finnes, hvis ikke lager vi en ny.
btnToggleHistory = view.findViewById(R.id.btn_toggle_history);
if (btnToggleHistory != null) {
btnToggleHistory.setOnClickListener(v -> toggleHistoryVisibility());
}
// --- FIKS FOR NULLPOINTER EXCEPTION PÅ LAYOUTTRANSITION ---
if (view instanceof ViewGroup) {
LayoutTransition transition = ((ViewGroup) view).getLayoutTransition();
if (transition == null) {
@ -643,7 +646,7 @@ public class FormsFragment extends Fragment {
}
transition.enableTransitionType(LayoutTransition.CHANGING);
}
// --------------------------------------
// ----------------------------------------------------------
if (formContainer == null) {
formContainer = new LinearLayout(getContext());
@ -661,32 +664,42 @@ public class FormsFragment extends Fragment {
// --- UI LOGIKK FOR DELT SKJERM ---
// Kalles når brukeren interagerer med et felt i skjemaet
private void expandFormModule() {
if (historyWrapper != null && historyWrapper.getVisibility() == View.VISIBLE) {
historyWrapper.setVisibility(View.GONE);
if (btnToggleHistory != null) {
btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
}
}
}
private void toggleHistoryVisibility() {
if (historyWrapper == null || btnToggleHistory == null) return;
if (historyWrapper.getVisibility() == View.VISIBLE) {
// Skjul historikk
historyWrapper.setVisibility(View.GONE);
btnToggleHistory.setImageResource(android.R.drawable.arrow_down_float);
} else {
// Vis historikk
historyWrapper.setVisibility(View.VISIBLE);
btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
}
}
private void attachInteractionListener(View view) {
if (view == null) return;
// Touch listener fanger opp klikk før tastaturet kommer opp
view.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
expandFormModule();
}
return false; // Return false to allow normal processing
return false;
});
// Focus listener for edittexts etc
view.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
expandFormModule();
}
});
// Click listener for buttons/checkboxes
if (view.isClickable()) {
view.setOnClickListener(v -> expandFormModule());
}
@ -736,7 +749,10 @@ public class FormsFragment extends Fragment {
updateStatus("");
// Reset visibility of history on new load
if (historyWrapper != null) historyWrapper.setVisibility(View.VISIBLE);
if (historyWrapper != null) {
historyWrapper.setVisibility(View.VISIBLE);
if (btnToggleHistory != null) btnToggleHistory.setImageResource(android.R.drawable.arrow_up_float);
}
TextView title = new TextView(getContext());
title.setText(getCleanTitle(form.title));
@ -755,7 +771,6 @@ public class FormsFragment extends Fragment {
}
if (form.fields == null) return;
for (GravityField field : form.fields) {
if ("hidden".equals(field.type) || field.isHidden || "hidden".equals(field.visibility)) {
continue;
@ -869,7 +884,8 @@ public class FormsFragment extends Fragment {
childFormId = Integer.parseInt(field.gpnfForm);
} catch (NumberFormatException e) { e.printStackTrace(); }
}
openChildFormDialog(childFormId);
// NYTT: Sender med felt-ID fra hovedskjemaet (f.eks "25")
openChildFormDialog(childFormId, field.id);
});
container.addView(btnAdd);
@ -887,7 +903,8 @@ public class FormsFragment extends Fragment {
container.addView(totalAmountView);
}
private void openChildFormDialog(int childFormId) {
// NY SIGNATUR: Tar imot parentFieldId
private void openChildFormDialog(int childFormId, String parentFieldId) {
if (getActivity() == null) return;
ProgressBar pBar = new ProgressBar(getContext());
AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
@ -895,13 +912,12 @@ public class FormsFragment extends Fragment {
.setMessage("Laster skjema...")
.setCancelable(false)
.show();
RetrofitClient.getApiService().getForm(childFormId).enqueue(new retrofit2.Callback<GravityForm>() {
@Override
public void onResponse(retrofit2.Call<GravityForm> call, retrofit2.Response<GravityForm> response) {
loadingDialog.dismiss();
if (response.isSuccessful() && response.body() != null) {
showChildFormDialog(response.body());
showChildFormDialog(response.body(), parentFieldId);
} else {
Toast.makeText(getContext(), "Kunne ikke hente underskjema", Toast.LENGTH_SHORT).show();
}
@ -915,9 +931,9 @@ public class FormsFragment extends Fragment {
});
}
private void showChildFormDialog(GravityForm childForm) {
// NY SIGNATUR: Tar imot parentFieldId
private void showChildFormDialog(GravityForm childForm, String parentFieldId) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
childInputViews.clear();
childRequiredFieldsMap.clear();
childFileUploads.clear();
@ -927,10 +943,8 @@ public class FormsFragment extends Fragment {
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(30, 30, 30, 30);
scrollView.addView(layout);
for (GravityField field : childForm.fields) {
if ("hidden".equals(field.type) || field.isHidden) continue;
LinearLayout wrapper = new LinearLayout(getContext());
wrapper.setOrientation(LinearLayout.VERTICAL);
wrapper.setPadding(0, 10, 0, 20);
@ -941,7 +955,6 @@ public class FormsFragment extends Fragment {
label.setText(lText);
label.setTypeface(null, Typeface.BOLD);
wrapper.addView(label);
if ("fileupload".equals(field.type)) {
renderFileUploadField(wrapper, field, childInputViews, childRequiredFieldsMap, true);
} else if ("product".equals(field.type)) {
@ -964,11 +977,12 @@ public class FormsFragment extends Fragment {
dialog.show();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
submitChildForm(childForm.id, dialog);
submitChildForm(childForm.id, dialog, parentFieldId);
});
}
private void submitChildForm(int childFormId, AlertDialog dialog) {
// NY SIGNATUR: Tar imot parentFieldId og sender den som meta
private void submitChildForm(int childFormId, AlertDialog dialog, String parentFieldId) {
JSONObject inputValues = new JSONObject();
for (Map.Entry<String, View> entry : childInputViews.entrySet()) {
String val = getInputValueGeneric(entry.getValue());
@ -992,6 +1006,15 @@ public class FormsFragment extends Fragment {
textParts.put(key, RequestBody.create(MultipartBody.FORM, val));
}
}
// --- HER ER FIKSEN FOR BUGGEN ---
// Vi legger ved en ekstra parameter som forteller Gravity Forms at dette
// er en nested entry som hører til et bestemt felt (f.eks "25").
if (parentFieldId != null) {
textParts.put("gpnf_entry_nested_form_field", RequestBody.create(MultipartBody.FORM, parentFieldId));
}
// ---------------------------------
for (Map.Entry<String, Uri> fileEntry : childFileUploads.entrySet()) {
String fieldId = fileEntry.getKey();
Uri uri = fileEntry.getValue();
@ -1015,7 +1038,6 @@ public class FormsFragment extends Fragment {
// ID 3 = Beskrivelse, ID 4 = Beløp
String desc = getInputValueGeneric(childInputViews.get("3"));
String price = getInputValueGeneric(childInputViews.get("4"));
addNestedEntry(entryId, desc, price);
dialog.dismiss();
} else {
@ -1156,7 +1178,6 @@ public class FormsFragment extends Fragment {
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
return FileProvider.getUriForFile(requireContext(), "com.kbs.kbsintranett.fileprovider", image);
} catch (IOException e) {
e.printStackTrace();
@ -1258,7 +1279,6 @@ public class FormsFragment extends Fragment {
public void onTextChanged(CharSequence s, int start, int before, int count) {}
public void afterTextChanged(Editable s) { evaluateAllConditionalLogic(); }
});
attachInteractionListener(input); // Add listener
container.addView(input);
@ -1300,7 +1320,6 @@ public class FormsFragment extends Fragment {
expandFormModule();
evaluateAllConditionalLogic();
});
container.addView(group);
views.put(field.id, group);
req.put(field.id, field.isRequired);
@ -1315,7 +1334,6 @@ public class FormsFragment extends Fragment {
}
return false;
});
List<String> labels = new ArrayList<>();
labels.add("- Velg -");
if (field.choices != null) {
@ -1335,12 +1353,10 @@ public class FormsFragment extends Fragment {
String inputId = (field.inputs != null && !field.inputs.isEmpty()) ? field.inputs.get(0).id : field.id;
checkBox.setTag("1");
checkBox.setOnCheckedChangeListener((b, c) -> {
expandFormModule();
evaluateAllConditionalLogic();
});
container.addView(checkBox);
views.put(inputId, checkBox);
req.put(inputId, field.isRequired);
@ -1353,12 +1369,12 @@ public class FormsFragment extends Fragment {
CheckBox checkBox = new CheckBox(getContext());
checkBox.setText(inputDef.label);
String value = "1"; // Fallback
String value = "1";
// Fallback
if (field.choices != null && i < field.choices.size()) {
value = field.choices.get(i).value;
}
checkBox.setTag(value);
checkBox.setOnCheckedChangeListener((b, c) -> {
expandFormModule();
evaluateAllConditionalLogic();
@ -1442,7 +1458,6 @@ public class FormsFragment extends Fragment {
}
attachInteractionListener(subInput);
container.addView(subInput);
views.put(subField.id, subInput);
req.put(subField.id, isSubRequired);
@ -1511,7 +1526,8 @@ public class FormsFragment extends Fragment {
aggregatedResult = aggregatedResult && ruleMatch;
if (!aggregatedResult) break;
} else {
aggregatedResult = aggregatedResult || ruleMatch;
aggregatedResult = aggregatedResult ||
ruleMatch;
if (aggregatedResult) break;
}
}
@ -1555,7 +1571,8 @@ public class FormsFragment extends Fragment {
if (view instanceof CheckBox) {
CheckBox cb = (CheckBox) view;
if (cb.isChecked()) {
return cb.getTag() != null ? cb.getTag().toString() : "1";
return cb.getTag() != null ?
cb.getTag().toString() : "1";
}
return "";
}
@ -1632,11 +1649,11 @@ public class FormsFragment extends Fragment {
String url = BASE_URL_GF + "/forms/" + formId + "/submissions";
Request request = new Request.Builder().url(url).post(body).header("Cookie", cookie).build();
client.newCall(request).enqueue(new okhttp3.Callback() {
public void onFailure(Call call, IOException e) {
public void onFailure(okhttp3.Call call, IOException e) {
Log.e(TAG, "JSON submit failed", e);
updateStatus("Feil: " + e.getMessage());
}
public void onResponse(Call call, Response response) {
public void onResponse(okhttp3.Call call, Response response) {
Log.d(TAG, "JSON response code: " + response.code());
if (response.isSuccessful()) {
if (getActivity() != null) getActivity().runOnUiThread(() -> {
@ -1720,13 +1737,16 @@ public class FormsFragment extends Fragment {
InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
String fileName = getFileName(uri);
RequestBody requestBody = new RequestBody() {
@Override public MediaType contentType() { return MediaType.parse("application/octet-stream"); }
@Override public MediaType contentType() { return MediaType.parse("application/octet-stream");
}
@Override public void writeTo(BufferedSink sink) throws IOException {
try (Source source = Okio.source(inputStream)) { sink.writeAll(source); }
try (Source source = Okio.source(inputStream)) { sink.writeAll(source);
}
}
};
return MultipartBody.Part.createFormData(partName, fileName, requestBody);
} catch (Exception e) { return null; }
} catch (Exception e) { return null;
}
}
private void fetchFormEntries() {
@ -1746,12 +1766,12 @@ public class FormsFragment extends Fragment {
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) {
Log.e(TAG, "Kunne ikke hente historikk", e);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
public void onResponse(@NonNull okhttp3.Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
String jsonStr = response.body().string();
try {
@ -1761,7 +1781,8 @@ public class FormsFragment extends Fragment {
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
showHistory(entries);
if (formId == ID_ANSATTEOPPLYSNINGER && entries.length() > 0) {
if (formId == ID_ANSATTEOPPLYSNINGER && entries.length() > 0)
{
try {
prefillFormFromHistory(entries.getJSONObject(0));
} catch (JSONException e) { e.printStackTrace(); }
@ -1769,7 +1790,8 @@ public class FormsFragment extends Fragment {
});
}
}
} catch (JSONException e) { e.printStackTrace(); }
} catch (JSONException e) { e.printStackTrace();
}
}
}
});
@ -1795,16 +1817,13 @@ public class FormsFragment extends Fragment {
// Prøv å finne en bedre tittel enn bare dato (f.eks Prosjektnavn eller Sted)
String titleText = "Innsendt: " + date;
TextView item = new TextView(getContext());
item.setText(titleText);
item.setPadding(10, 20, 10, 20);
item.setBackgroundResource(android.R.drawable.list_selector_background);
item.setTextSize(14);
// Add click listener to show details
item.setOnClickListener(v -> showEntryDetails(entry));
View line = new View(getContext());
line.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
line.setBackgroundColor(Color.LTGRAY);
@ -1812,57 +1831,209 @@ public class FormsFragment extends Fragment {
historyContainer.addView(item);
historyContainer.addView(line);
}
} catch (JSONException e) { e.printStackTrace(); }
} catch (JSONException e) { e.printStackTrace();
}
}
// NY METODE: Vis detaljer i dialog
// NY METODE: Vis detaljer i dialog med delingsknapp
private void showEntryDetails(JSONObject entry) {
StringBuilder details = new StringBuilder();
// HVIS dette er Form 16 (Refusjon), må vi først hente vedleggene (som ligger i Form 18)
if (formId == ID_REFUSJON_UTLEGG) {
Log.d(TAG, "Form 16 detected. Checking for child entries...");
String nestedIds = entry.optString("25"); // Felt 25 i Form 16 inneholder ID-ene til vedleggene
if (!nestedIds.isEmpty()) {
Log.d(TAG, "Nested IDs found: " + nestedIds);
List<String> ids = new ArrayList<>();
// ROBUST PARSING AV ID-LISTE
if (nestedIds.startsWith("[") && nestedIds.endsWith("]")) {
// Dette er en JSON-array streng (f.eks [101, 102])
try {
JSONArray jsonArray = new JSONArray(nestedIds);
for(int i=0; i<jsonArray.length(); i++) {
ids.add(jsonArray.getString(i));
}
} catch(JSONException e) {
Log.e(TAG, "Failed to parse nested IDs as JSON array", e);
}
} else {
// Dette er en komma-separert streng (f.eks "101, 102")
for (String id : nestedIds.split(",")) {
ids.add(id.trim());
}
}
if (!ids.isEmpty()) {
// Vis en "Laster..." dialog mens vi henter data
AlertDialog loadingDialog = new AlertDialog.Builder(getContext())
.setMessage("Henter vedleggsdetaljer...")
.setCancelable(false)
.show();
// Start rekursiv henting
StringBuilder accumulatedHtml = new StringBuilder();
StringBuilder accumulatedText = new StringBuilder();
// Legg først til basis-info fra hovedskjemaet
appendBasicInfo(entry, accumulatedHtml, accumulatedText);
fetchChildEntriesRecursive(ids, 0, accumulatedHtml, accumulatedText, loadingDialog);
return; // Stopp her, dialogen vises når data er hentet
}
} else {
Log.d(TAG, "No nested IDs found in field 25.");
}
}
// --- STANDARD VISNING (For alle andre skjemaer) ---
StringBuilder htmlBuilder = new StringBuilder();
StringBuilder textBuilder = new StringBuilder();
appendBasicInfo(entry, htmlBuilder, textBuilder);
showFinalDialog(htmlBuilder, textBuilder);
}
// Hjelpemetode for å legge til info fra hovedskjemaet
private void appendBasicInfo(JSONObject entry, StringBuilder html, StringBuilder text) {
try {
// Loop gjennom alle felter i entry og match med skjema-definisjon
// Merk: Entry keys er felt-IDer (f.eks "1", "3.2")
String date = entry.optString("date_created");
html.append("<b>Innsendt:</b> ").append(date).append("<br><br>");
text.append("Innsendt: ").append(date).append("\n\n");
// Dato
details.append("<b>Innsendt:</b> ").append(entry.optString("date_created")).append("<br><br>");
// Iterer gjennom feltene i skjema-definisjonen for å få riktig rekkefølge
if (currentForm != null && currentForm.fields != null) {
for (GravityField field : currentForm.fields) {
if ("section".equals(field.type) || "html".equals(field.type) || "captcha".equals(field.type)) continue;
// Hopp over felt 25 (Vedleggs-IDer) i Form 16, siden vi viser det bedre senere
if (formId == ID_REFUSJON_UTLEGG && "25".equals(field.id)) continue;
String value = "";
if (field.inputs != null && !field.inputs.isEmpty()) {
// Composite fields (Address, Name)
for (GravityField input : field.inputs) {
String subVal = entry.optString(input.id);
if (!subVal.isEmpty()) {
value += " " + subVal;
}
if (!subVal.isEmpty()) value += " " + subVal;
}
} else {
// Standard field
value = entry.optString(String.valueOf(field.id));
}
if (!value.trim().isEmpty()) {
// For filopplastinger er verdien ofte en URL.
if ("fileupload".equals(field.type)) {
value = "(Vedlegg)";
// Håndter filopplastinger (single/multiple)
// Her antar vi enkel URL, men for sikkerhets skyld bruker vi extractUrl
String cleanUrl = extractUrl(value);
if (cleanUrl.startsWith("http")) {
html.append("<b>").append(field.label).append(":</b><br>")
.append("<a href=\"").append(cleanUrl).append("\">Åpne fil</a><br><br>");
text.append(field.label).append(":\n").append(cleanUrl).append("\n\n");
} else {
html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
text.append(field.label).append(":\n").append(value).append("\n\n");
}
} else {
html.append("<b>").append(field.label).append(":</b><br>").append(value).append("<br><br>");
text.append(field.label).append(":\n").append(value).append("\n\n");
}
details.append("<b>").append(field.label).append(":</b><br>")
.append(value).append("<br><br>");
}
}
}
} catch (Exception e) {}
}
} catch (Exception e) {
details.append("Kunne ikke vise detaljer.");
// Rekursiv metode for å hente barn-oppføringer (Vedlegg)
private void fetchChildEntriesRecursive(List<String> ids, int index, StringBuilder html, StringBuilder text, AlertDialog loader) {
if (index >= ids.size()) {
// Alle ferdige! Vis dialogen.
loader.dismiss();
showFinalDialog(html, text);
return;
}
String entryId = ids.get(index);
Log.d(TAG, "Fetching child entry ID: " + entryId);
RetrofitClient.getApiService().getSingleEntry(entryId).enqueue(new retrofit2.Callback<JsonElement>() {
@Override
public void onResponse(retrofit2.Call<JsonElement> call, retrofit2.Response<JsonElement> response) {
if (response.isSuccessful() && response.body() != null) {
try {
JsonObject json = response.body().getAsJsonObject();
// I Form 18: Felt 1 = Fil, Felt 3 = Beskrivelse, Felt 4 = Beløp
// Parse Description and Price
String desc = json.has("3") ? json.get("3").getAsString() : "Uten beskrivelse";
String price = json.has("4") ? json.get("4").getAsString() : "";
html.append("<b>Vedlegg ").append(index + 1).append(":</b><br>");
text.append("Vedlegg ").append(index + 1).append(":\n");
html.append(desc).append(" (").append(price).append(")<br>");
text.append(desc).append(" (").append(price).append(")\n");
// Parse File Field (ID 1) - Can be multiple files!
if (json.has("1")) {
JsonElement fileEl = json.get("1");
if (fileEl.isJsonArray()) {
// It is a real JSON array
JsonArray arr = fileEl.getAsJsonArray();
for (int i = 0; i < arr.size(); i++) {
String url = arr.get(i).getAsString().replace("\\/", "/");
html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
text.append(url).append("\n");
}
} else {
// It is a string. Check if it's a JSON string array like "[\"http...\"]"
String rawString = fileEl.getAsString();
if (rawString.startsWith("[") && rawString.endsWith("]")) {
try {
JSONArray arr = new JSONArray(rawString);
for (int i = 0; i < arr.length(); i++) {
String url = arr.getString(i).replace("\\/", "/");
html.append("<a href=\"").append(url).append("\">Åpne fil ").append(i+1).append("</a><br>");
text.append(url).append("\n");
}
} catch (JSONException ex) {
// Fallback: simple cleanup
String clean = extractUrl(rawString);
html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
text.append(clean).append("\n");
}
} else {
// Just a plain URL string
String clean = extractUrl(rawString);
if(clean.startsWith("http")) {
html.append("<a href=\"").append(clean).append("\">Åpne fil</a><br>");
text.append(clean).append("\n");
}
}
}
}
html.append("<br>");
text.append("\n");
} catch (Exception e) {
Log.e(TAG, "Error parsing child entry", e);
}
} else {
Log.e(TAG, "Failed to fetch child entry: " + response.code());
}
// Gå til neste (uansett om denne feilet eller ei)
fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
}
@Override
public void onFailure(retrofit2.Call<JsonElement> call, Throwable t) {
Log.e(TAG, "Network error fetching child entry", t);
// Hopp over ved feil
fetchChildEntriesRecursive(ids, index + 1, html, text, loader);
}
});
}
private void showFinalDialog(StringBuilder htmlBuilder, StringBuilder textBuilder) {
ScrollView scroll = new ScrollView(getContext());
TextView text = new TextView(getContext());
text.setText(Html.fromHtml(details.toString(), Html.FROM_HTML_MODE_COMPACT));
text.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
text.setText(Html.fromHtml(htmlBuilder.toString(), Html.FROM_HTML_MODE_COMPACT));
text.setPadding(40, 40, 40, 40);
scroll.addView(text);
@ -1870,9 +2041,31 @@ public class FormsFragment extends Fragment {
.setTitle("Detaljer")
.setView(scroll)
.setPositiveButton("Lukk", null)
.setNeutralButton("Del", (d, w) -> shareEntryDetails(textBuilder.toString()))
.create()
.show();
}
// Hjelpemetode for å rydde opp i URLer fra JSON (f.eks ["http://..."] -> http://...)
private String extractUrl(String rawValue) {
if (rawValue == null) return "";
String clean = rawValue.replace("[", "")
.replace("]", "")
.replace("\"", "")
.replace("\\/", "/");
return clean.trim();
}
private void shareEntryDetails(String text) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, text);
sendIntent.setType("text/plain");
Intent shareIntent = Intent.createChooser(sendIntent, "Del innsending via...");
startActivity(shareIntent);
}
private void prefillFormFromHistory(JSONObject latestEntry) {
if (latestEntry == null) return;
for (Map.Entry<String, View> entry : inputViews.entrySet()) {
@ -1933,7 +2126,8 @@ public class FormsFragment extends Fragment {
String id;
String description;
String price;
NestedEntry(String id, String d, String p) { this.id = id; this.description = d; this.price = p; }
NestedEntry(String id, String d, String p) { this.id = id; this.description = d; this.price = p;
}
}
private void updateStatus(String msg) {
@ -1968,6 +2162,7 @@ public class FormsListFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_forms_list, container, false);
LinearLayout formsContainer = view.findViewById(R.id.forms_container);
// Legger til knappene for de ulike skjemaene
addFormButton(formsContainer, "1. Ansatteopplysninger", 1);
addFormButton(formsContainer, "4. RUH (Rapport om uønsket hendelse)", 4);
addFormButton(formsContainer, "9. Sikkerhetskurs / Kompetansebevis", 9);
@ -1999,7 +2194,8 @@ public class FormsListFragment extends Fragment {
btn.setOnClickListener(v -> {
Bundle bundle = new Bundle();
bundle.putInt("formId", formId);
Navigation.findNavController(v).navigate(R.id.nav_forms, bundle);
// HER VAR FEILEN: Endret R.id.nav_forms til riktig action ID
Navigation.findNavController(v).navigate(R.id.action_formsListFragment_to_formsDetailFragment, bundle);
});
container.addView(btn);
@ -3034,7 +3230,6 @@ public class UserManager {
============================================================
FILSTI: app\src\main\java\com\kbs\kbsintranett\WordPressApiService.java
============================================================
// FILSTI: app\src\main\java\com\kbs\kbsintranett\WordPressApiService.java
package com.kbs.kbsintranett;
import com.google.gson.JsonElement;
@ -3050,7 +3245,7 @@ import retrofit2.http.Path;
import retrofit2.http.Multipart;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Query; // NYTT
import retrofit2.http.Query;
public interface WordPressApiService {
// 1. Hent nyheter
@ -3086,13 +3281,17 @@ public interface WordPressApiService {
@GET("wp-json/kbs/v1/calendar/events")
Call<List<CalendarEvent>> getCalendarEvents();
// 8. HENT INNSENDINGER (Entries) - NYTT
// 8. HENT INNSENDINGER (Entries) - LISTE
@GET("wp-json/gf/v2/entries")
Call<GravityEntryResponse> getEntries(
@Query("form_ids") int formId,
@Query("search") String searchJson,
@Query("paging[page_size]") int pageSize
);
// 9. HENT ÉN ENKELT INNSENDING (Ny! Brukes for å hente detaljer fra underskjema)
@GET("wp-json/gf/v2/entries/{entry_id}")
Call<JsonElement> getSingleEntry(@Path("entry_id") String entryId);
}
============================================================
@ -3316,6 +3515,7 @@ FILSTI: app\src\main\res\layout\fragment_forms.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@ -3380,10 +3580,22 @@ FILSTI: app\src\main\res\layout\fragment_forms.xml
<TextView
android:id="@+id/txt_status"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#666666"
android:textSize="12sp" />
android:textSize="12sp"
android:text="" />
<ImageView
android:id="@+id/btn_toggle_history"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@android:drawable/arrow_up_float"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="4dp"
android:contentDescription="Vis/Skjul historikk"
app:tint="#666666" />
</LinearLayout>
<ScrollView