Add attachment handling to SelfServeTasks with modal support in task table

- Introduced attachment management (list, upload, delete, download) via `authenticatedRequest`.
- Added `SelfServeTaskAttachmentsModal` for viewing and managing task attachments.
- Updated `SelfServeTasksTable` to include attachment actions with modal integration.
- Adjusted default nearest department evaluation ID in `MyWashStart.vue`.
This commit is contained in:
Jeppe Bundgaard
2026-01-09 11:05:22 +01:00
parent e2492b63b9
commit 7962c805e0
3 changed files with 53 additions and 6 deletions
@@ -4,6 +4,7 @@ const { loadList, loadSwitch, metaCurrentPage, metaItemsPerPage, setList } = use
import EditableTableColumn from "@/components/displays/buttons/EditableTableColumn.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import { ref } from "vue";
import SelfServeTaskAttachmentsModal from "@/components/displays/department/tables/SelfServeTaskAttachmentsModal.vue";
// Define the props
const props = defineProps({
@@ -16,6 +17,16 @@ const props = defineProps({
const draggingIndex = ref(null);
const dragOverIndex = ref(null);
const selectedTaskForAttachments = ref(null);
const openAttachmentsModal = (task) => {
selectedTaskForAttachments.value = task;
};
const closeAttachmentsModal = () => {
selectedTaskForAttachments.value = null;
};
const onDragStart = (event, index) => {
draggingIndex.value = index;
event.dataTransfer.effectAllowed = 'move';
@@ -149,15 +160,27 @@ const onDrop = async (event, newIndex) => {
/>
<!-- Actions -->
<td class="has-text-right">
<button class="button is-small is-danger is-light" @click="SessionUser.objects.self_serve_tasks.functions.showDeleteObjectForm(object.id, loadList)">
<span class="icon is-small">
<i class="fas fa-trash"></i>
</span>
</button>
<div class="buttons is-right">
<button class="button is-small is-link is-light" @click="openAttachmentsModal(object)" title="Vedhæftede filer">
<span class="icon is-small">
<i class="fas fa-paperclip"></i>
</span>
</button>
<button class="button is-small is-danger is-light" @click="SessionUser.objects.self_serve_tasks.functions.showDeleteObjectForm(object.id, loadList)">
<span class="icon is-small">
<i class="fas fa-trash"></i>
</span>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<SelfServeTaskAttachmentsModal
v-if="selectedTaskForAttachments"
:task="selectedTaskForAttachments"
@closeModal="closeAttachmentsModal"
/>
</template>
<style scoped>
@@ -1,6 +1,7 @@
<script>
import { ObjectsGlobal } from "@/components/session/token/SessionUser/Objects/ObjectsGlobal.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import {authenticatedRequest} from "@/components/session/authenticatedRequest.vue";
/**
* The SelfServeTasks object
@@ -199,6 +200,29 @@ export const SelfServeTasks = {
delete: async (id) => {
return ObjectsGlobal.delete.object(SelfServeTasks.meta.endpoint, id);
},
attachments: {
list: async (taskId) => {
return authenticatedRequest(SelfServeTasks.meta.endpoint + "/attachments", "GET", { id: taskId })
.then(response => response.data);
},
delete: async (taskId, attachmentId) => {
return authenticatedRequest(SelfServeTasks.meta.endpoint + "/attachments", "DELETE", { task_id: taskId, attachment_id: attachmentId })
.then(response => response.data);
},
upload: async (taskId, base64File, fileName) => {
return authenticatedRequest(SelfServeTasks.meta.endpoint + "/attachments/upload", "POST", {
task_id: taskId,
base64_file: base64File,
file_name: fileName
}).then(response => response.data);
},
download: async (taskId, attachmentId) => {
return authenticatedRequest(SelfServeTasks.meta.endpoint + "/attachments/download", "GET", {
task_id: taskId,
attachment_id: attachmentId
}).then(response => response.data);
}
},
functions: {
showDeleteObjectForm: (id, onAfterSubmit = null) => {
return ObjectsGlobal.showDeleteObjectForm(SelfServeTasks, id, onAfterSubmit);
@@ -32,7 +32,7 @@ const guestDepartments = ref([]);
const licensePlateInput = ref(null);
const customerNumberInput = ref(null);
const nearestDepartment = ref(null);
const forceNearestDepartmentEvaluationId = ref(0); // When this is set, force nearest department to be the one with this ID
const forceNearestDepartmentEvaluationId = ref(6); // When this is set, force nearest department to be the one with this ID
const lastDepartmentFetchTime = ref(null); // Used to make sure we don't fetch too often
const fetchIntervalMs = 10 * 1000; // 10 seconds