|
|
@@ -0,0 +1,241 @@
|
|
|
+<template>
|
|
|
+ <q-dialog ref="dialogRef" persistent maximized transition-show="slide-left" transition-hide="slide-right">
|
|
|
+ <div class="bg-page full-height column no-shadow support-dialog">
|
|
|
+
|
|
|
+ <div class="row items-center q-px-md q-pt-md q-pb-sm bg-white shadow-profile bg-surface">
|
|
|
+ <q-btn v-close-popup icon="mdi-chevron-left" flat round dense color="primary" />
|
|
|
+ <q-space />
|
|
|
+ <span class="text-subtitle1 text-primary font16 fontbold gradient-diarista">{{ $t('support_request.title') }}</span>
|
|
|
+ <q-space />
|
|
|
+ <div style="width: 32px"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col column no-wrap q-px-md q-pt-lg overflow-auto">
|
|
|
+ <span class="text-grey-7 q-mb-lg font12 fontregular" style="line-height: 1.4;">
|
|
|
+ {{ $t('support_request.subtitle') }}
|
|
|
+ </span>
|
|
|
+
|
|
|
+ <q-form ref="formRef" class="column no-wrap q-gutter-y-md support-form" @submit="onSubmit">
|
|
|
+ <div>
|
|
|
+ <div class="text-text font12 fontbold q-mb-xs">{{ $t('support_request.fields.category') }}</div>
|
|
|
+ <q-select
|
|
|
+ v-model="form.category"
|
|
|
+ :options="categoryOptions"
|
|
|
+ :rules="[inputRules.required]"
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ input-class="text-text"
|
|
|
+ popup-content-class="text-text"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <div class="text-text font12 fontbold q-mb-xs">{{ $t('support_request.fields.title') }}</div>
|
|
|
+ <q-input
|
|
|
+ v-model="form.title"
|
|
|
+ :rules="[inputRules.required, inputRules.max(150)]"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ counter
|
|
|
+ maxlength="150"
|
|
|
+ input-class="text-text"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <div class="text-text font12 fontbold q-mb-xs">{{ $t('support_request.fields.message') }}</div>
|
|
|
+ <q-input
|
|
|
+ v-model="form.message"
|
|
|
+ type="textarea"
|
|
|
+ :rules="[inputRules.required]"
|
|
|
+ outlined
|
|
|
+ autogrow
|
|
|
+ input-class="text-text"
|
|
|
+ :input-style="{ minHeight: '90px' }"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="text-grey-6 font10 fontregular row items-center q-gutter-x-xs">
|
|
|
+ <q-icon name="mdi-information-outline" size="14px" />
|
|
|
+ <span>{{ $t('support_request.chat_attached_note') }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-separator class="q-my-sm" />
|
|
|
+
|
|
|
+ <span class="text-dark font12 fontbold">{{ $t('support_request.contact_title') }}</span>
|
|
|
+ <span class="text-grey-6 font10 fontregular" style="line-height: 1.4;">
|
|
|
+ {{ $t('support_request.contact_subtitle') }}
|
|
|
+ </span>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <div class="text-text font12 fontbold q-mb-xs">{{ $t('support_request.fields.name') }}</div>
|
|
|
+ <q-input
|
|
|
+ v-model="form.name"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ input-class="text-text"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div class="text-text font12 fontbold q-mb-xs">{{ $t('support_request.fields.email') }}</div>
|
|
|
+ <q-input
|
|
|
+ v-model="form.email"
|
|
|
+ type="email"
|
|
|
+ :rules="form.email ? [inputRules.email] : []"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ input-class="text-text"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div class="text-text font12 fontbold q-mb-xs">{{ $t('support_request.fields.phone') }}</div>
|
|
|
+ <q-input
|
|
|
+ v-model="form.phone"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ input-class="text-text"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-btn
|
|
|
+ type="submit"
|
|
|
+ unelevated
|
|
|
+ rounded
|
|
|
+ no-caps
|
|
|
+ color="primary"
|
|
|
+ class="full-width save-btn q-mt-md"
|
|
|
+ padding="10px 12px"
|
|
|
+ :label="$t('support_request.actions.send')"
|
|
|
+ :loading="loading"
|
|
|
+ />
|
|
|
+ </q-form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </q-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, reactive } from 'vue';
|
|
|
+import { useDialogPluginComponent, useQuasar } from 'quasar';
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
+import { useInputRules } from 'src/composables/useInputRules';
|
|
|
+import { userStore } from 'src/stores/user';
|
|
|
+import { chatbotStore } from 'src/stores/chatbot';
|
|
|
+import { createSupportRequest } from 'src/api/supportRequest';
|
|
|
+
|
|
|
+defineEmits([...useDialogPluginComponent.emits]);
|
|
|
+
|
|
|
+const { dialogRef, onDialogOK } = useDialogPluginComponent();
|
|
|
+const $q = useQuasar();
|
|
|
+const { t } = useI18n();
|
|
|
+const { inputRules } = useInputRules();
|
|
|
+const user = userStore();
|
|
|
+const chatbot = chatbotStore();
|
|
|
+
|
|
|
+const formRef = ref(null);
|
|
|
+const loading = ref(false);
|
|
|
+
|
|
|
+const form = reactive({
|
|
|
+ category: null,
|
|
|
+ title: '',
|
|
|
+ message: '',
|
|
|
+ name: user.user?.name || '',
|
|
|
+ email: user.user?.email || '',
|
|
|
+ phone: user.user?.phone || '',
|
|
|
+});
|
|
|
+
|
|
|
+const categoryOptions = [
|
|
|
+ { label: t('support_request.categories.payment'), value: 'payment' },
|
|
|
+ { label: t('support_request.categories.schedule'), value: 'schedule' },
|
|
|
+ { label: t('support_request.categories.account'), value: 'account' },
|
|
|
+ { label: t('support_request.categories.other'), value: 'other' },
|
|
|
+];
|
|
|
+
|
|
|
+const buildTranscript = () => {
|
|
|
+ const lines = chatbot.messages.map((msg) => {
|
|
|
+ const who = msg.role === 'user' ? t('support_request.transcript.you') : t('support_request.transcript.bot');
|
|
|
+ return `${who}: ${msg.text}`;
|
|
|
+ });
|
|
|
+ return lines.join('\n');
|
|
|
+};
|
|
|
+
|
|
|
+const buildMessage = () => {
|
|
|
+ const transcript = buildTranscript();
|
|
|
+ const header = t('support_request.transcript.chat_header');
|
|
|
+ const userHeader = t('support_request.transcript.user_message_header');
|
|
|
+ const parts = [];
|
|
|
+ if (transcript.trim()) {
|
|
|
+ parts.push(`${header}\n${transcript}`);
|
|
|
+ }
|
|
|
+ parts.push(`${userHeader}\n${form.message.trim()}`);
|
|
|
+ return parts.join('\n\n');
|
|
|
+};
|
|
|
+
|
|
|
+const onSubmit = async () => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ await createSupportRequest({
|
|
|
+ category: form.category,
|
|
|
+ title: form.title.trim(),
|
|
|
+ message: buildMessage(),
|
|
|
+ name: form.name?.trim() || null,
|
|
|
+ email: form.email?.trim() || null,
|
|
|
+ phone: form.phone?.trim() || null,
|
|
|
+ });
|
|
|
+ $q.notify({ type: 'positive', message: t('support_request.success') });
|
|
|
+ onDialogOK(true);
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ $q.notify({ type: 'negative', message: t('support_request.error') });
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+// Fonte consistente entre os dois apps e alinhada às telas (o dialog é teleportado
|
|
|
+// para fora do .q-page, então não herda a fonte definida lá).
|
|
|
+.support-dialog {
|
|
|
+ font-family: "Inter", sans-serif;
|
|
|
+
|
|
|
+ // Inputs, textareas e o texto interno dos campos NÃO herdam font-family do navegador
|
|
|
+ // por padrão. Sem isso, os valores dos campos caem na fonte do body (Roboto) e ficam
|
|
|
+ // diferentes dos labels — e diferentes entre os apps. Aqui travamos tudo na mesma fonte.
|
|
|
+ :deep(.q-field),
|
|
|
+ :deep(.q-field__native),
|
|
|
+ :deep(.q-field__input),
|
|
|
+ :deep(.q-field__label),
|
|
|
+ :deep(.q-field__prefix),
|
|
|
+ :deep(.q-field__suffix),
|
|
|
+ :deep(input),
|
|
|
+ :deep(textarea),
|
|
|
+ :deep(.q-item__label),
|
|
|
+ :deep(.q-btn),
|
|
|
+ :deep(.q-btn__content) {
|
|
|
+ font-family: "Inter", sans-serif;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.support-form {
|
|
|
+ // A seta do select herda cor clara/branca no tema dos apps e fica invisível;
|
|
|
+ // forçamos uma cor visível para deixar explícito que é um campo de seleção.
|
|
|
+ :deep(.q-select__dropdown-icon) {
|
|
|
+ color: #555555 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.q-field__native),
|
|
|
+ :deep(.q-field__native span),
|
|
|
+ :deep(.q-field__input),
|
|
|
+ :deep(textarea.q-field__native) {
|
|
|
+ color: #555555 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.q-field__native::placeholder),
|
|
|
+ :deep(.q-field__input::placeholder) {
|
|
|
+ color: #9e9e9e !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|