// Day not found
utils.JSONResponse(w, http.StatusOK, []any{})
}
+
+// DeleteDay deletes all data of the specified day
+// Also delete files, that might be uploaded
+func DeleteDay(w http.ResponseWriter, r *http.Request) {
+ utils.UsersFileMutex.Lock()
+ defer utils.UsersFileMutex.Unlock()
+
+ // Get user ID from context
+ userID, ok := r.Context().Value(utils.UserIDKey).(int)
+ if !ok {
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
+ return
+ }
+
+ // Get parameters from URL
+ year, err := strconv.Atoi(r.URL.Query().Get("year"))
+ if err != nil {
+ http.Error(w, "Invalid year parameter", http.StatusBadRequest)
+ return
+ }
+ month, err := strconv.Atoi(r.URL.Query().Get("month"))
+ if err != nil {
+ http.Error(w, "Invalid month parameter", http.StatusBadRequest)
+ return
+ }
+ dayValue, err := strconv.Atoi(r.URL.Query().Get("day"))
+ if err != nil {
+ http.Error(w, "Invalid day parameter", http.StatusBadRequest)
+ return
+ }
+
+ // Get month data
+ content, err := utils.GetMonth(userID, year, month)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("Error retrieving month data: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ days, ok := content["days"].([]any)
+ if !ok {
+ utils.JSONResponse(w, http.StatusOK, map[string]bool{"success": true})
+ return
+ }
+
+ for i, dayInterface := range days {
+ day, ok := dayInterface.(map[string]any)
+ if !ok {
+ continue
+ }
+
+ dayNum, ok := day["day"].(float64)
+ if !ok || int(dayNum) != dayValue {
+ continue
+ }
+
+ // Delete associated files before removing the day entry
+ if filesList, ok := day["files"].([]any); ok && len(filesList) > 0 {
+ for _, fileInterface := range filesList {
+ file, ok := fileInterface.(map[string]any)
+ if !ok {
+ continue
+ }
+
+ if fileID, ok := file["uuid_filename"].(string); ok {
+ // Delete the actual file from the filesystem
+ err := utils.RemoveFile(userID, fileID)
+ if err != nil {
+ utils.Logger.Printf("Warning: Failed to delete file %s for user %d: %v", fileID, userID, err)
+ // Continue with deletion even if file removal fails
+ }
+ }
+ }
+ }
+
+ // Remove the day from the days array
+ days = append(days[:i], days[i+1:]...)
+ content["days"] = days
+
+ if err := utils.WriteMonth(userID, year, month, content); err != nil {
+ http.Error(w, fmt.Sprintf("Error writing month data: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ utils.JSONResponse(w, http.StatusOK, map[string]bool{"success": true})
+ return
+ }
+
+ utils.JSONResponse(w, http.StatusOK, map[string]bool{"success": true})
+}
mux.HandleFunc("GET /logs/deleteFile", middleware.RequireAuth(handlers.DeleteFile))
mux.HandleFunc("GET /logs/getHistory", middleware.RequireAuth(handlers.GetHistory))
mux.HandleFunc("GET /logs/bookmarkDay", middleware.RequireAuth(handlers.BookmarkDay))
+ mux.HandleFunc("GET /logs/deleteDay", middleware.RequireAuth(handlers.DeleteDay))
// Create a handler chain with Logger and CORS middleware
// Logger middleware will be executed first, then CORS
faQuestionCircle,
faClockRotateLeft,
faArrowLeft,
- faArrowRight
+ faArrowRight,
+ faTrash
} from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
import { v4 as uuidv4 } from 'uuid';
tinyMDE.setContent(currentLog);
tinyMDE.setSelection({ row: 0, col: 0 });
}
+
+ function showDeleteDayModal() {
+ const modal = new bootstrap.Modal(document.getElementById('modalDeleteDay'));
+ modal.show();
+ }
+
+ function deleteDay() {
+ axios
+ .get(API_URL + '/logs/deleteDay', {
+ params: {
+ day: $selectedDate.day,
+ month: $selectedDate.month,
+ year: $selectedDate.year
+ }
+ })
+ .then((response) => {
+ if (response.data.success) {
+ currentLog = '';
+ tinyMDE.setContent(currentLog);
+ savedLog = '';
+ logDateWritten = '';
+
+ selectedTags = [];
+ history = [];
+ filesOfDay = [];
+ images = [];
+ $cal.daysBookmarked = $cal.daysBookmarked.filter((day) => day !== $selectedDate.day);
+ $cal.daysWithFiles = $cal.daysWithFiles.filter((day) => day !== $selectedDate.day);
+ $cal.daysWithLogs = $cal.daysWithLogs.filter((day) => day !== $selectedDate.day);
+ } else {
+ const toast = new bootstrap.Toast(document.getElementById('toastErrorDeletingDay'));
+ toast.show();
+ }
+ })
+ .catch((error) => {
+ console.error(error);
+ const toast = new bootstrap.Toast(document.getElementById('toastErrorDeletingDay'));
+ toast.show();
+ });
+ }
</script>
<DatepickerLogic />
</button>
</div>
{/if}
- <div class="textAreaDelete">delete</div>
+ <div class="textAreaDelete d-flex flex-column justify-content-center">
+ <button class="btn px-0 btn-hover" onclick={() => showDeleteDayModal()}>
+ <Fa icon={faTrash} class="" size="1.5x" fw />
+ </button>
+ </div>
</div>
<div id="log" class="focus-ring">
<div id="toolbar"></div>
<div class="toast-body">Fehler beim Download einer Datei!</div>
</div>
</div>
+
+ <div
+ id="toastErrorDeletingDay"
+ class="toast align-items-center text-bg-danger"
+ role="alert"
+ aria-live="assertive"
+ aria-atomic="true"
+ >
+ <div class="d-flex">
+ <div class="toast-body">Fehler beim Löschen des Tages!</div>
+ </div>
+ </div>
</div>
<div class="modal fade" id="modalConfirmDeleteFile" tabindex="-1">
</div>
</div>
+ <div class="modal fade" id="modalDeleteDay" tabindex="-1">
+ <div class="modal-dialog modal-dialog-centered">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title">Tag vollständig löschen?</h5>
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"
+ ></button>
+ </div>
+ <div class="modal-body">
+ Du löschst <b><u>sämtliche Daten</u></b> vom
+ <b><u>{`${$selectedDate.day}.${$selectedDate.month}.${$selectedDate.year}`}</u></b>!<br
+ /><br />
+ Dies beinhaltet:
+ <ul>
+ {#snippet deleteDayBool(available, description)}
+ <li class={available ? 'text-decoration-underline' : 'text-muted fst-italic'}>
+ {#if available}✔️{:else}❌{/if}
+ {description}
+ </li>
+ {/snippet}
+
+ {#snippet deleteDayCount(item, description)}
+ <li class={item.length > 0 ? 'text-decoration-underline' : 'text-muted fst-italic'}>
+ {item.length}
+ {description}
+ </li>
+ {/snippet}
+
+ {@render deleteDayBool(logDateWritten !== '', 'Tagebucheintrag')}
+ {@render deleteDayBool(historyAvailable, 'Verlauf')}
+
+ {@render deleteDayCount(filesOfDay, 'Dateien')}
+ {@render deleteDayCount(selectedTags, 'Tags')}
+ {@render deleteDayBool($cal.daysBookmarked.includes($selectedDate.day), 'Lesezeichen')}
+ </ul>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
+ <button
+ onclick={() => deleteDay()}
+ type="button"
+ class="btn btn-primary"
+ data-bs-dismiss="modal">Löschen</button
+ >
+ </div>
+ </div>
+ </div>
+ </div>
+
<TagModal
bind:this={tagModal}
bind:editTag={newTag}
padding: 0.25em;
}
+ .textAreaDelete {
+ padding: 0.25em;
+ }
+
#log div:focus:not(.notSaved) {
border-color: #90ee90;
box-shadow: 0 0 0 0.25rem #90ee9070;