Add mantis summaries and start of email summaries... Need to figure out a way to get the emails since MS block IMAP :(

This commit is contained in:
Cameron Redmore 2025-04-23 21:12:08 +01:00
parent 2d11d0bd79
commit 2ad9a63582
18 changed files with 1993 additions and 577 deletions

View file

@ -0,0 +1,197 @@
<template>
<q-page padding>
<q-card flat bordered>
<q-card-section class="row items-center justify-between">
<div class="text-h6">Mantis Summaries</div>
<q-btn
label="Generate Today's Summary"
color="primary"
@click="generateSummary"
:loading="generating"
:disable="generating"
/>
</q-card-section>
<q-separator />
<q-card-section v-if="generationError">
<q-banner inline-actions class="text-white bg-red">
<template v-slot:avatar>
<q-icon name="error" />
</template>
{{ generationError }}
</q-banner>
</q-card-section>
<q-card-section v-if="loading">
<q-spinner-dots size="40px" color="primary" />
<span class="q-ml-md">Loading summaries...</span>
</q-card-section>
<q-card-section v-if="error && !generationError">
<q-banner inline-actions class="text-white bg-red">
<template v-slot:avatar>
<q-icon name="error" />
</template>
{{ error }}
</q-banner>
</q-card-section>
<q-list separator v-if="!loading && !error && summaries.length > 0">
<q-item v-for="summary in summaries" :key="summary.id">
<q-item-section>
<q-item-label class="text-weight-bold">{{ formatDate(summary.summaryDate) }}</q-item-label>
<q-item-label caption>Generated: {{ formatDateTime(summary.generatedAt) }}</q-item-label>
<q-item-label class="q-mt-sm markdown-content" v-html="parseMarkdown(summary.summaryText)"></q-item-label>
</q-item-section>
</q-item>
</q-list>
<q-card-section v-if="totalPages > 1" class="flex flex-center q-mt-md">
<q-pagination
v-model="currentPage"
:max="totalPages"
@update:model-value="fetchSummaries"
direction-links
flat
color="primary"
active-color="primary"
/>
</q-card-section>
<q-card-section v-if="!loading && !error && summaries.length === 0">
<div class="text-center text-grey">No summaries found.</div>
</q-card-section>
</q-card>
</q-page>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue';
import { date, useQuasar } from 'quasar'; // Import useQuasar
import axios from 'axios';
import { marked } from 'marked';
const $q = useQuasar(); // Initialize Quasar plugin usage
const summaries = ref([]);
const loading = ref(true);
const error = ref(null);
const generating = ref(false); // State for generation button
const generationError = ref(null); // State for generation error
const currentPage = ref(1);
const itemsPerPage = ref(10); // Or your desired page size
const totalItems = ref(0);
// Create a custom renderer
const renderer = new marked.Renderer();
const linkRenderer = renderer.link;
renderer.link = (href, title, text) => {
const html = linkRenderer.call(renderer, href, title, text);
// Add target="_blank" to the link
return html.replace(/^<a /, '<a target="_blank" rel="noopener noreferrer" ');
};
const fetchSummaries = async (page = 1) => {
loading.value = true;
error.value = null;
try {
const response = await axios.get(`/api/mantis-summaries`, {
params: {
page: page,
limit: itemsPerPage.value
}
});
summaries.value = response.data.summaries;
totalItems.value = response.data.total;
currentPage.value = page;
} catch (err) {
console.error('Error fetching Mantis summaries:', err);
error.value = err.response?.data?.error || 'Failed to load summaries. Please try again later.';
} finally {
loading.value = false;
}
};
const generateSummary = async () => {
generating.value = true;
generationError.value = null;
error.value = null; // Clear previous loading errors
try {
await axios.post('/api/mantis-summaries/generate');
$q.notify({
color: 'positive',
icon: 'check_circle',
message: 'Summary generation started successfully. It may take a few moments to appear.',
});
// Optionally, refresh the list after a short delay or immediately
// Consider that generation might be async on the backend
setTimeout(() => fetchSummaries(1), 3000); // Refresh after 3 seconds
} catch (err) {
console.error('Error generating Mantis summary:', err);
generationError.value = err.response?.data?.error || 'Failed to start summary generation.';
$q.notify({
color: 'negative',
icon: 'error',
message: generationError.value,
});
} finally {
generating.value = false;
}
};
const formatDate = (dateString) => {
// Assuming dateString is YYYY-MM-DD
return date.formatDate(dateString + 'T00:00:00', 'DD MMMM YYYY');
};
const formatDateTime = (dateTimeString) => {
return date.formatDate(dateTimeString, 'DD MMMM YYYY HH:mm');
};
const parseMarkdown = (markdownText) => {
if (!markdownText) return '';
// Use the custom renderer with marked
return marked(markdownText, { renderer });
};
const totalPages = computed(() => {
return Math.ceil(totalItems.value / itemsPerPage.value);
});
onMounted(() => {
fetchSummaries(currentPage.value);
});
</script>
<style scoped>
.markdown-content :deep(table) {
border-collapse: collapse;
width: 100%;
margin-top: 1em;
margin-bottom: 1em;
}
.markdown-content :deep(th),
.markdown-content :deep(td) {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.markdown-content :deep(th) {
background-color: rgba(0, 0, 0, 0.25);
}
.markdown-content :deep(a) {
color: var(--q-primary);
text-decoration: none;
}
.markdown-content :deep(a:hover) {
text-decoration: underline;
}
/* Add any specific styles if needed */
</style>