|
@@ -13,7 +13,6 @@
|
|
|
@submit="onOKClick"
|
|
@submit="onOKClick"
|
|
|
>
|
|
>
|
|
|
<q-card-section class="q-pt-sm" style="flex: 1; overflow-y: auto; min-height: 0">
|
|
<q-card-section class="q-pt-sm" style="flex: 1; overflow-y: auto; min-height: 0">
|
|
|
- <!-- Tabs: only shown when editing -->
|
|
|
|
|
<CustomTabComponent
|
|
<CustomTabComponent
|
|
|
v-if="card?.id"
|
|
v-if="card?.id"
|
|
|
v-model:active-tab="currentTab"
|
|
v-model:active-tab="currentTab"
|
|
@@ -24,6 +23,7 @@
|
|
|
<!-- Tab: Atividade -->
|
|
<!-- Tab: Atividade -->
|
|
|
<div v-show="currentTab === 'atividade'">
|
|
<div v-show="currentTab === 'atividade'">
|
|
|
<div class="row q-col-gutter-sm">
|
|
<div class="row q-col-gutter-sm">
|
|
|
|
|
+ <!-- 1. Título -->
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.title"
|
|
v-model="form.title"
|
|
|
label="Título da Tarefa"
|
|
label="Título da Tarefa"
|
|
@@ -31,6 +31,19 @@
|
|
|
:rules="[val => !!val || 'Campo obrigatório']"
|
|
:rules="[val => !!val || 'Campo obrigatório']"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
|
|
+ <!-- 2. Destino (primeiro campo de configuração) -->
|
|
|
|
|
+ <DefaultSelect
|
|
|
|
|
+ v-model="form.scope"
|
|
|
|
|
+ label="Destino"
|
|
|
|
|
+ :options="scopeOptions"
|
|
|
|
|
+ emit-value
|
|
|
|
|
+ map-options
|
|
|
|
|
+ class="col-6"
|
|
|
|
|
+ :rules="[val => !!val || 'Campo obrigatório']"
|
|
|
|
|
+ @update:model-value="onScopeChange"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Prazo ao lado do Destino -->
|
|
|
<DefaultInputDatePicker
|
|
<DefaultInputDatePicker
|
|
|
v-model="form.due_date_display"
|
|
v-model="form.due_date_display"
|
|
|
v-model:untreated-date="form.due_date"
|
|
v-model:untreated-date="form.due_date"
|
|
@@ -38,23 +51,27 @@
|
|
|
class="col-6"
|
|
class="col-6"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
|
|
+ <!-- Responsável — só aparece quando destino é Interno -->
|
|
|
<DefaultSelect
|
|
<DefaultSelect
|
|
|
- v-model="form.priority"
|
|
|
|
|
- label="Prioridade"
|
|
|
|
|
- :options="priorityOptions"
|
|
|
|
|
|
|
+ v-if="form.scope === 'internal'"
|
|
|
|
|
+ v-model="form.responsible_user_id"
|
|
|
|
|
+ label="Responsável"
|
|
|
|
|
+ :options="userOptions"
|
|
|
emit-value
|
|
emit-value
|
|
|
map-options
|
|
map-options
|
|
|
- class="col-6"
|
|
|
|
|
- :rules="[val => !!val || 'Campo obrigatório']"
|
|
|
|
|
|
|
+ clearable
|
|
|
|
|
+ class="col-12"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
|
|
+ <!-- 3. Prioridade + Fase -->
|
|
|
<DefaultSelect
|
|
<DefaultSelect
|
|
|
- v-model="form.responsible_user_id"
|
|
|
|
|
- label="Responsável"
|
|
|
|
|
- :options="userOptions"
|
|
|
|
|
|
|
+ v-model="form.priority"
|
|
|
|
|
+ label="Prioridade"
|
|
|
|
|
+ :options="priorityOptions"
|
|
|
emit-value
|
|
emit-value
|
|
|
map-options
|
|
map-options
|
|
|
- class="col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
|
|
+ :rules="[val => !!val || 'Campo obrigatório']"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultSelect
|
|
<DefaultSelect
|
|
@@ -67,22 +84,14 @@
|
|
|
:rules="[val => !!val || 'Campo obrigatório']"
|
|
:rules="[val => !!val || 'Campo obrigatório']"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
|
|
+ <!-- 4. Setor -->
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.sector"
|
|
v-model="form.sector"
|
|
|
label="Setor"
|
|
label="Setor"
|
|
|
class="col-6"
|
|
class="col-6"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
- <DefaultSelect
|
|
|
|
|
- v-model="form.scope"
|
|
|
|
|
- label="Destino"
|
|
|
|
|
- :options="scopeOptions"
|
|
|
|
|
- emit-value
|
|
|
|
|
- map-options
|
|
|
|
|
- class="col-6"
|
|
|
|
|
- :rules="[val => !!val || 'Campo obrigatório']"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
|
|
+ <!-- 5. Descrição -->
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.description"
|
|
v-model="form.description"
|
|
|
label="Descrição"
|
|
label="Descrição"
|
|
@@ -124,10 +133,7 @@
|
|
|
@delete="onDeleteComment(reply)"
|
|
@delete="onDeleteComment(reply)"
|
|
|
/>
|
|
/>
|
|
|
</template>
|
|
</template>
|
|
|
- <div
|
|
|
|
|
- v-else
|
|
|
|
|
- class="flex flex-center full-height text-grey-5 text-body2"
|
|
|
|
|
- >
|
|
|
|
|
|
|
+ <div v-else class="flex flex-center full-height text-grey-5 text-body2">
|
|
|
Nenhum comentário registrado.
|
|
Nenhum comentário registrado.
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -135,18 +141,8 @@
|
|
|
</q-card-section>
|
|
</q-card-section>
|
|
|
|
|
|
|
|
<q-card-actions align="right" class="q-px-md q-pb-md" style="flex-shrink: 0">
|
|
<q-card-actions align="right" class="q-px-md q-pb-md" style="flex-shrink: 0">
|
|
|
- <q-btn
|
|
|
|
|
- outline
|
|
|
|
|
- color="primary"
|
|
|
|
|
- label="Cancelar"
|
|
|
|
|
- @click="onDialogCancel"
|
|
|
|
|
- />
|
|
|
|
|
- <q-btn
|
|
|
|
|
- color="primary"
|
|
|
|
|
- label="Salvar"
|
|
|
|
|
- type="submit"
|
|
|
|
|
- :loading="loading"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
|
|
|
|
|
+ <q-btn color="primary" label="Salvar" type="submit" :loading="loading" />
|
|
|
</q-card-actions>
|
|
</q-card-actions>
|
|
|
</q-form>
|
|
</q-form>
|
|
|
</q-card>
|
|
</q-card>
|
|
@@ -177,14 +173,8 @@ import { getUsersByUnit } from "src/api/user";
|
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
|
|
|
|
|
|
const { card, initialPhase } = defineProps({
|
|
const { card, initialPhase } = defineProps({
|
|
|
- card: {
|
|
|
|
|
- type: Object,
|
|
|
|
|
- default: null,
|
|
|
|
|
- },
|
|
|
|
|
- initialPhase: {
|
|
|
|
|
- type: String,
|
|
|
|
|
- default: "a_fazer",
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ card: { type: Object, default: null },
|
|
|
|
|
+ initialPhase: { type: String, default: "a_fazer" },
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
@@ -192,35 +182,35 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
|
|
|
|
|
|
|
|
const $q = useQuasar();
|
|
const $q = useQuasar();
|
|
|
|
|
|
|
|
-const formRef = ref(null);
|
|
|
|
|
-const loading = ref(false);
|
|
|
|
|
|
|
+const formRef = ref(null);
|
|
|
|
|
+const loading = ref(false);
|
|
|
const currentTab = ref("atividade");
|
|
const currentTab = ref("atividade");
|
|
|
const userOptions = ref([]);
|
|
const userOptions = ref([]);
|
|
|
-const replies = ref([]);
|
|
|
|
|
|
|
+const replies = ref([]);
|
|
|
|
|
|
|
|
const tabs = computed(() => [
|
|
const tabs = computed(() => [
|
|
|
- { name: "atividade", label: "Atividade" },
|
|
|
|
|
|
|
+ { name: "atividade", label: "Atividade" },
|
|
|
{ name: "comentarios", label: "Comentários" },
|
|
{ name: "comentarios", label: "Comentários" },
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
const priorityOptions = [
|
|
const priorityOptions = [
|
|
|
- { label: "Alta", value: "alta" },
|
|
|
|
|
|
|
+ { label: "Alta", value: "alta" },
|
|
|
{ label: "Normal", value: "normal" },
|
|
{ label: "Normal", value: "normal" },
|
|
|
- { label: "Baixa", value: "baixa" },
|
|
|
|
|
|
|
+ { label: "Baixa", value: "baixa" },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
const phaseOptions = [
|
|
const phaseOptions = [
|
|
|
- { label: "A Fazer", value: "a_fazer" },
|
|
|
|
|
- { label: "Em Progresso", value: "em_progresso" },
|
|
|
|
|
- { label: "Em Revisão", value: "em_revisao" },
|
|
|
|
|
- { label: "Concluído", value: "concluido" },
|
|
|
|
|
|
|
+ { label: "A Fazer", value: "a_fazer" },
|
|
|
|
|
+ { label: "Em Progresso", value: "em_progresso" },
|
|
|
|
|
+ { label: "Em Revisão", value: "em_revisao" },
|
|
|
|
|
+ { label: "Concluído", value: "concluido" },
|
|
|
{ label: "Demandas Especiais", value: "demandas_especiais" },
|
|
{ label: "Demandas Especiais", value: "demandas_especiais" },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
-// Franquias só podem criar interno ou enviar para Matriz (scope=specific, sem unit_id alvo)
|
|
|
|
|
|
|
+// Franchisee: only Internal or send to Matriz (scope=specific, target=null)
|
|
|
const scopeOptions = [
|
|
const scopeOptions = [
|
|
|
{ label: "Interno", value: "internal" },
|
|
{ label: "Interno", value: "internal" },
|
|
|
- { label: "Matriz", value: "specific" },
|
|
|
|
|
|
|
+ { label: "Matriz", value: "specific" },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
const formatDisplayDate = (rawDate) => {
|
|
const formatDisplayDate = (rawDate) => {
|
|
@@ -230,27 +220,37 @@ const formatDisplayDate = (rawDate) => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const form = ref({
|
|
const form = ref({
|
|
|
- title: card?.title ?? null,
|
|
|
|
|
- priority: card?.priority ?? "normal",
|
|
|
|
|
- phase: card?.phase ?? initialPhase,
|
|
|
|
|
- scope: card?.scope ?? "internal",
|
|
|
|
|
- sector: card?.sector ?? null,
|
|
|
|
|
- description: card?.description ?? null,
|
|
|
|
|
- due_date: card?.due_date ?? null,
|
|
|
|
|
- due_date_display: formatDisplayDate(card?.due_date),
|
|
|
|
|
|
|
+ title: card?.title ?? null,
|
|
|
|
|
+ priority: card?.priority ?? "normal",
|
|
|
|
|
+ phase: card?.phase ?? initialPhase,
|
|
|
|
|
+ scope: card?.scope ?? "internal",
|
|
|
|
|
+ sector: card?.sector ?? null,
|
|
|
|
|
+ description: card?.description ?? null,
|
|
|
|
|
+ due_date: card?.due_date ?? null,
|
|
|
|
|
+ due_date_display: formatDisplayDate(card?.due_date),
|
|
|
responsible_user_id: card?.responsible_user_id ?? null,
|
|
responsible_user_id: card?.responsible_user_id ?? null,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+// Clear responsible when switching away from internal
|
|
|
|
|
+const onScopeChange = (val) => {
|
|
|
|
|
+ if (val !== "internal") {
|
|
|
|
|
+ form.value.responsible_user_id = null;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const buildPayload = () => ({
|
|
const buildPayload = () => ({
|
|
|
- title: form.value.title,
|
|
|
|
|
- priority: form.value.priority,
|
|
|
|
|
- phase: form.value.phase,
|
|
|
|
|
- scope: form.value.scope,
|
|
|
|
|
- sector: form.value.sector || null,
|
|
|
|
|
- description: form.value.description || null,
|
|
|
|
|
- due_date: form.value.due_date || null,
|
|
|
|
|
- responsible_user_id: form.value.responsible_user_id || null,
|
|
|
|
|
- target_unit_id: null, // Backend resolves based on scope + franchisee unit
|
|
|
|
|
|
|
+ title: form.value.title,
|
|
|
|
|
+ priority: form.value.priority,
|
|
|
|
|
+ phase: form.value.phase,
|
|
|
|
|
+ scope: form.value.scope,
|
|
|
|
|
+ sector: form.value.sector || null,
|
|
|
|
|
+ description: form.value.description || null,
|
|
|
|
|
+ due_date: form.value.due_date || null,
|
|
|
|
|
+ // Only internal tasks carry a responsible
|
|
|
|
|
+ responsible_user_id: form.value.scope === "internal"
|
|
|
|
|
+ ? (form.value.responsible_user_id || null)
|
|
|
|
|
+ : null,
|
|
|
|
|
+ target_unit_id: null, // backend resolves from scope + unit header
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const loadUsers = async () => {
|
|
const loadUsers = async () => {
|
|
@@ -268,12 +268,8 @@ const loadReplies = async () => {
|
|
|
const onAddComment = () => {
|
|
const onAddComment = () => {
|
|
|
$q.dialog({
|
|
$q.dialog({
|
|
|
title: "Adicionar Comentário",
|
|
title: "Adicionar Comentário",
|
|
|
- prompt: {
|
|
|
|
|
- model: "",
|
|
|
|
|
- type: "textarea",
|
|
|
|
|
- label: "Comentário",
|
|
|
|
|
- },
|
|
|
|
|
- ok: { label: "Salvar", color: "primary" },
|
|
|
|
|
|
|
+ prompt: { model: "", type: "textarea", label: "Comentário" },
|
|
|
|
|
+ ok: { label: "Salvar", color: "primary" },
|
|
|
cancel: { label: "Cancelar", color: "primary", outline: true },
|
|
cancel: { label: "Cancelar", color: "primary", outline: true },
|
|
|
}).onOk(async (text) => {
|
|
}).onOk(async (text) => {
|
|
|
if (!text?.trim()) return;
|
|
if (!text?.trim()) return;
|
|
@@ -285,12 +281,8 @@ const onAddComment = () => {
|
|
|
const onEditComment = (reply) => {
|
|
const onEditComment = (reply) => {
|
|
|
$q.dialog({
|
|
$q.dialog({
|
|
|
title: "Editar Comentário",
|
|
title: "Editar Comentário",
|
|
|
- prompt: {
|
|
|
|
|
- model: reply.reply,
|
|
|
|
|
- type: "textarea",
|
|
|
|
|
- label: "Comentário",
|
|
|
|
|
- },
|
|
|
|
|
- ok: { label: "Salvar", color: "primary" },
|
|
|
|
|
|
|
+ prompt: { model: reply.reply, type: "textarea", label: "Comentário" },
|
|
|
|
|
+ ok: { label: "Salvar", color: "primary" },
|
|
|
cancel: { label: "Cancelar", color: "primary", outline: true },
|
|
cancel: { label: "Cancelar", color: "primary", outline: true },
|
|
|
}).onOk(async (text) => {
|
|
}).onOk(async (text) => {
|
|
|
if (!text?.trim()) return;
|
|
if (!text?.trim()) return;
|
|
@@ -301,10 +293,10 @@ const onEditComment = (reply) => {
|
|
|
|
|
|
|
|
const onDeleteComment = (reply) => {
|
|
const onDeleteComment = (reply) => {
|
|
|
$q.dialog({
|
|
$q.dialog({
|
|
|
- title: "Excluir Comentário",
|
|
|
|
|
|
|
+ title: "Excluir Comentário",
|
|
|
message: "Tem certeza que deseja excluir este comentário?",
|
|
message: "Tem certeza que deseja excluir este comentário?",
|
|
|
- cancel: { outline: true, color: "primary", label: "Cancelar" },
|
|
|
|
|
- ok: { color: "negative", label: "Excluir" },
|
|
|
|
|
|
|
+ cancel: { outline: true, color: "primary", label: "Cancelar" },
|
|
|
|
|
+ ok: { color: "negative", label: "Excluir" },
|
|
|
}).onOk(async () => {
|
|
}).onOk(async () => {
|
|
|
await deleteKanbanReply(card.id, reply.id);
|
|
await deleteKanbanReply(card.id, reply.id);
|
|
|
loadReplies();
|
|
loadReplies();
|