Overhaul settings and implement user preferences. Also implements dark theme toggle as part of the user settings.
This commit is contained in:
parent
b84f0907a8
commit
727746030c
17 changed files with 760 additions and 378 deletions
|
|
@ -1,202 +1,220 @@
|
|||
<template>
|
||||
<q-page padding>
|
||||
<div
|
||||
class="q-gutter-md"
|
||||
style="max-width: 800px; margin: auto;"
|
||||
>
|
||||
<h5 class="q-mt-none q-mb-md">
|
||||
Settings
|
||||
</h5>
|
||||
<q-card class="q-mb-md">
|
||||
<q-card-section>
|
||||
<div class="text-h6">
|
||||
Application Settings
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<q-card-section>
|
||||
<div class="text-h6">
|
||||
Mantis Summary Prompt
|
||||
</div>
|
||||
<div class="text-caption text-grey q-mb-sm">
|
||||
Edit the prompt used to generate Mantis summaries. Use $DATE and $MANTIS_TICKETS as placeholders.
|
||||
</div>
|
||||
<q-input
|
||||
v-model="mantisPrompt"
|
||||
type="textarea"
|
||||
filled
|
||||
autogrow
|
||||
label="Mantis Prompt"
|
||||
:loading="loadingPrompt"
|
||||
:disable="savingPrompt"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
<q-btn
|
||||
label="Save Prompt"
|
||||
color="primary"
|
||||
@click="saveMantisPrompt"
|
||||
:loading="savingPrompt"
|
||||
:disable="!mantisPrompt || loadingPrompt"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
<q-separator />
|
||||
|
||||
<q-card
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<q-card-section>
|
||||
<div class="text-h6">
|
||||
Email Summary Prompt
|
||||
<q-card-section v-if="loading">
|
||||
<q-spinner-dots size="2em" /> Loading settings...
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section v-else-if="loadError">
|
||||
<q-banner
|
||||
inline-actions
|
||||
class="text-white bg-red"
|
||||
>
|
||||
<template #avatar>
|
||||
<q-icon name="error" />
|
||||
</template>
|
||||
{{ loadError }}
|
||||
<template #action>
|
||||
<q-btn
|
||||
flat
|
||||
color="white"
|
||||
label="Retry"
|
||||
@click="loadSettings"
|
||||
/>
|
||||
</template>
|
||||
</q-banner>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section v-else>
|
||||
<q-form @submit.prevent="saveSettings">
|
||||
<div
|
||||
v-for="(group, groupName) in settings"
|
||||
:key="groupName"
|
||||
class="q-mb-lg"
|
||||
>
|
||||
<div class="text-h6 q-mb-sm">
|
||||
{{ groupName }}
|
||||
</div>
|
||||
<div
|
||||
v-for="setting in group"
|
||||
:key="setting.key"
|
||||
class="q-mb-md"
|
||||
>
|
||||
<q-input
|
||||
v-model="settingValues[setting.key]"
|
||||
:label="setting.name"
|
||||
:type="setting.type || 'text'"
|
||||
outlined
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
|
||||
<q-separator class="q-my-md" />
|
||||
</div>
|
||||
<div class="text-caption text-grey q-mb-sm">
|
||||
Edit the prompt used to generate Email summaries. Use $EMAIL_DATA as a placeholder for the JSON email array.
|
||||
|
||||
<div class="row justify-end">
|
||||
<q-btn
|
||||
label="Save Settings"
|
||||
type="submit"
|
||||
color="primary"
|
||||
:loading="saving"
|
||||
:disable="loading || saving"
|
||||
/>
|
||||
</div>
|
||||
<q-input
|
||||
v-model="emailPrompt"
|
||||
type="textarea"
|
||||
filled
|
||||
autogrow
|
||||
label="Email Prompt"
|
||||
:loading="loadingEmailPrompt"
|
||||
:disable="savingEmailPrompt"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
<q-btn
|
||||
label="Save Prompt"
|
||||
color="primary"
|
||||
@click="saveEmailPrompt"
|
||||
:loading="savingEmailPrompt"
|
||||
:disable="!emailPrompt || loadingEmailPrompt"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'boot/axios';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import axios from 'boot/axios'; // Import axios
|
||||
|
||||
const $q = useQuasar();
|
||||
|
||||
const mantisPrompt = ref('');
|
||||
const loadingPrompt = ref(false);
|
||||
const savingPrompt = ref(false);
|
||||
// Define the structure of settings
|
||||
const settings = ref({
|
||||
Mantis: [
|
||||
{
|
||||
name: 'Mantis API Key',
|
||||
key: 'MANTIS_API_KEY',
|
||||
},
|
||||
{
|
||||
name: 'Mantis API Endpoint',
|
||||
key: 'MANTIS_API_ENDPOINT'
|
||||
},
|
||||
{
|
||||
name: 'Mantis Prompt',
|
||||
key: 'MANTIS_PROMPT',
|
||||
type: 'textarea'
|
||||
}
|
||||
],
|
||||
Gemini: [
|
||||
{
|
||||
name: 'Gemini API Key',
|
||||
key: 'GEMINI_API_KEY'
|
||||
}
|
||||
],
|
||||
Database: [
|
||||
{
|
||||
name: 'MySQL Host',
|
||||
key: 'MYSQL_HOST'
|
||||
},
|
||||
{
|
||||
name: 'MySQL Port',
|
||||
key: 'MYSQL_PORT'
|
||||
},
|
||||
{
|
||||
name: 'MySQL User',
|
||||
key: 'MYSQL_USER'
|
||||
},
|
||||
{
|
||||
name: 'MySQL Password',
|
||||
key: 'MYSQL_PASSWORD'
|
||||
},
|
||||
{
|
||||
name: 'MySQL Database',
|
||||
key: 'MYSQL_DATABASE'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const fetchMantisPrompt = async() =>
|
||||
// Reactive state for setting values, loading, saving, and errors
|
||||
const settingValues = ref({});
|
||||
const loading = ref(true);
|
||||
const saving = ref(false);
|
||||
const loadError = ref(null);
|
||||
|
||||
// Function to load settings from the server
|
||||
async function loadSettings()
|
||||
{
|
||||
loadingPrompt.value = true;
|
||||
loading.value = true;
|
||||
loadError.value = null;
|
||||
settingValues.value = {}; // Reset values
|
||||
const allSettingKeys = Object.values(settings.value).flat().map(s => s.key);
|
||||
|
||||
try
|
||||
{
|
||||
const response = await axios.get('/api/settings/mantisPrompt');
|
||||
mantisPrompt.value = response.data.value || ''; // Handle case where setting might not exist yet
|
||||
const responses = await Promise.all(allSettingKeys.map(key => axios.get(`/api/settings/${key}`, {
|
||||
validateStatus: status => status === 200 || status === 404 // Accept 404 as a valid response
|
||||
})));
|
||||
responses.forEach((response, index) =>
|
||||
{
|
||||
const key = allSettingKeys[index];
|
||||
//If the response status is 404, set the value to an empty string
|
||||
if (response.status === 404)
|
||||
{
|
||||
settingValues.value[key] = '';
|
||||
return;
|
||||
}
|
||||
settingValues.value[key] = response.data;
|
||||
});
|
||||
}
|
||||
catch (error)
|
||||
catch (err)
|
||||
{
|
||||
console.error('Error fetching Mantis prompt:', error);
|
||||
console.error('Error loading settings:', err);
|
||||
loadError.value = err.response?.data?.error || 'Failed to load settings. Please check the console.';
|
||||
$q.notify({
|
||||
color: 'negative',
|
||||
message: 'Failed to load Mantis prompt setting.',
|
||||
icon: 'report_problem'
|
||||
icon: 'error',
|
||||
message: loadError.value,
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
loadingPrompt.value = false;
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const saveMantisPrompt = async() =>
|
||||
// Function to save settings to the server
|
||||
async function saveSettings()
|
||||
{
|
||||
savingPrompt.value = true;
|
||||
saving.value = true;
|
||||
loadError.value = null; // Clear previous load errors
|
||||
|
||||
const allSettingKeys = Object.keys(settingValues.value);
|
||||
const requests = allSettingKeys.map(key =>
|
||||
axios.put(`/api/settings/${key}`, { value: settingValues.value[key] })
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
await axios.put('/api/settings/mantisPrompt', { value: mantisPrompt.value });
|
||||
await Promise.all(requests);
|
||||
$q.notify({
|
||||
color: 'positive',
|
||||
message: 'Mantis prompt updated successfully.',
|
||||
icon: 'check_circle'
|
||||
icon: 'check_circle',
|
||||
message: 'Settings saved successfully!',
|
||||
});
|
||||
}
|
||||
catch (error)
|
||||
catch (err)
|
||||
{
|
||||
console.error('Error saving Mantis prompt:', error);
|
||||
console.error('Error saving settings:', err);
|
||||
const errorMessage = err.response?.data?.error || 'Failed to save settings. Please try again.';
|
||||
$q.notify({
|
||||
color: 'negative',
|
||||
message: 'Failed to save Mantis prompt setting.',
|
||||
icon: 'report_problem'
|
||||
icon: 'error',
|
||||
message: errorMessage,
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
savingPrompt.value = false;
|
||||
saving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const emailPrompt = ref('');
|
||||
const loadingEmailPrompt = ref(false);
|
||||
const savingEmailPrompt = ref(false);
|
||||
|
||||
const fetchEmailPrompt = async() =>
|
||||
{
|
||||
loadingEmailPrompt.value = true;
|
||||
try
|
||||
{
|
||||
const response = await axios.get('/api/settings/emailPrompt');
|
||||
emailPrompt.value = response.data.value || ''; // Handle case where setting might not exist yet
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Error fetching Email prompt:', error);
|
||||
$q.notify({
|
||||
color: 'negative',
|
||||
message: 'Failed to load Email prompt setting.',
|
||||
icon: 'report_problem'
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
loadingEmailPrompt.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const saveEmailPrompt = async() =>
|
||||
{
|
||||
savingEmailPrompt.value = true;
|
||||
try
|
||||
{
|
||||
await axios.put('/api/settings/emailPrompt', { value: emailPrompt.value });
|
||||
$q.notify({
|
||||
color: 'positive',
|
||||
message: 'Email prompt updated successfully.',
|
||||
icon: 'check_circle'
|
||||
});
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Error saving Email prompt:', error);
|
||||
$q.notify({
|
||||
color: 'negative',
|
||||
message: 'Failed to save Email prompt setting.',
|
||||
icon: 'report_problem'
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
savingEmailPrompt.value = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Load settings when the component is mounted
|
||||
onMounted(() =>
|
||||
{
|
||||
fetchMantisPrompt();
|
||||
fetchEmailPrompt();
|
||||
loadSettings();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Add any specific styles if needed */
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue