nidus-sync/ts/components/CommunicationColumnList.vue
Eli Ribble a82732a49c
Return communication database rows from communication API
This is a pretty big refactor of how communication works to start moving
us in the direction we want to go long-term. This adds the new
communication row and migrates existing reports to add rows for
communication.

There's also a bunch of automatic fixes from the new linter. I should
have added them separately, but whatever.
2026-05-01 21:00:23 +00:00

140 lines
3.2 KiB
Vue

<style scoped>
.report-card {
cursor: pointer;
transition: background-color 0.2s;
}
.report-card:hover {
background-color: $secondary;
}
.report-card.active {
background-color: $primary;
color: white;
}
.reports-list {
overflow-y: auto;
max-height: 100vh;
}
</style>
<template>
<div class="card shadow-sm h-100 reports-list">
<div class="card-header bg-light pane-header">
<div class="input-group input-group-sm">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input
type="text"
class="form-control"
placeholder="Filter reports..."
v-model="searchFilter"
/>
</div>
</div>
<div class="card-body scroll-pane">
<div class="mb-3">
<button
class="btn btn-sm"
:class="
typeFilter === 'all' ? 'btn-primary' : 'btn-outline-secondary'
"
@click="typeFilter = 'all'"
>
All
</button>
<button
class="btn btn-sm"
:class="
typeFilter === 'nuisance' ? 'btn-danger' : 'btn-outline-secondary'
"
@click="typeFilter = 'nuisance'"
>
<i class="bi bi-mosquito"></i>Nuisance
</button>
<button
class="btn btn-sm"
:class="typeFilter === 'water' ? 'btn-info' : 'btn-outline-secondary'"
@click="typeFilter = 'water'"
>
<i class="bi bi-droplet"></i> Water
</button>
</div>
<div class="list-group list-group-flush">
<div class="loading list-group-item" v-if="loading || all == null">
Loading...
</div>
<div
v-else-if="all.length > 0"
v-for="comm in filteredCommunications"
:key="comm.id"
class="list-group-item report-card p-3"
:class="{
active: selectedId && selectedId === comm.id,
}"
@click="handleClick(comm.id)"
>
<ListCardCommunication :comm="comm" />
</div>
</div>
</div>
<div
v-if="filteredCommunications.length === 0"
class="text-center text-muted p-4"
>
<i class="bi bi-inbox fs-1"></i>
<p class="mt-2">No reports found</p>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from "vue";
import ListCardCommunication from "@/components/ListCardCommunication.vue";
import { Communication, LogEntry, PublicReport } from "@/type/api";
interface Props {
all: Communication[] | null;
loading: boolean;
selectedId?: string | null;
}
interface Emits {
(e: "deselect", id: string): void;
(e: "select", id: string): void;
}
const props = withDefaults(defineProps<Props>(), {
selectedId: null,
});
const emit = defineEmits<Emits>();
const handleClick = (id: string) => {
if (props.selectedId == null) {
emit("select", id);
} else if (props.selectedId == id) {
emit("deselect", id);
} else {
emit("select", id);
}
};
const searchFilter = ref<string>("");
const typeFilter = ref<string>("all");
function selectCommunication(communication: Communication) {
// Emit both events - one for general use, one for v-model
console.log("selected", communication);
emit("select", communication.id);
//emit("update:selectedItem", communication);
//messageText.value = "";
//updateMap();
}
// Computed properties
const filteredCommunications = computed((): Communication[] => {
if (props.all == null) {
return [];
}
return props.all;
});
</script>