|
|
@@ -1,5 +1,12 @@
|
|
|
<script setup>
|
|
|
-import { computed, onMounted, reactive, ref } from "vue";
|
|
|
+import {
|
|
|
+ computed,
|
|
|
+ onBeforeUnmount,
|
|
|
+ onMounted,
|
|
|
+ reactive,
|
|
|
+ ref,
|
|
|
+ watch,
|
|
|
+} from "vue";
|
|
|
import { useRoute, useRouter } from "vue-router";
|
|
|
import BotaoAlterarTema from "../components/BotaoAlterarTema.vue";
|
|
|
import DocumentList from "../components/DocumentList.vue";
|
|
|
@@ -8,21 +15,60 @@ import SearchBox from "../components/SearchBox.vue";
|
|
|
import { ingestDocuments, ingestFile, listDocuments } from "../api/chat.js";
|
|
|
import { useChat } from "../composables/useChat.js";
|
|
|
import { useSearch } from "../composables/useSearch.js";
|
|
|
+import { useAuth } from "../composables/useAuth.js";
|
|
|
|
|
|
const router = useRouter();
|
|
|
const route = useRoute();
|
|
|
const { newConversation } = useChat();
|
|
|
+const { user, logout } = useAuth();
|
|
|
|
|
|
-const { results: searchResults, loading: searchLoading, error: searchError, run: runSearch } = useSearch();
|
|
|
+const {
|
|
|
+ results: searchResults,
|
|
|
+ loading: searchLoading,
|
|
|
+ error: searchError,
|
|
|
+ run: runSearch,
|
|
|
+} = useSearch();
|
|
|
|
|
|
const docs = reactive({
|
|
|
items: [],
|
|
|
loading: false,
|
|
|
- error: ""
|
|
|
+ error: "",
|
|
|
});
|
|
|
+const logoutLoading = ref(false);
|
|
|
|
|
|
+const sidebarRecolhida = ref(false);
|
|
|
const painelAberto = ref("");
|
|
|
const rotaAtiva = computed(() => String(route.name ?? ""));
|
|
|
+const usuarioNome = computed(() => user.value?.Nome || user.value?.Login || "Usuario");
|
|
|
+const usuarioSetor = computed(() => user.value?.Setor || user.value?.Nivel || "");
|
|
|
+const layoutStyle = computed(() => ({
|
|
|
+ "--layout-sidebar-width": sidebarRecolhida.value ? "84px" : "280px",
|
|
|
+}));
|
|
|
+const classeItemSidebarBase = computed(() =>
|
|
|
+ sidebarRecolhida.value
|
|
|
+ ? "w-full flex items-center justify-center gap-0 rounded-xl p-2.5 text-sm font-medium transition-colors !bg-none !bg-transparent !border-0 !transform-none hover:!transform-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
|
+ : "w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-left text-sm font-medium transition-colors !bg-none !bg-transparent !border-0 !transform-none hover:!transform-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
|
+);
|
|
|
+const classeItemSidebarAtivo =
|
|
|
+ "text-gray-900 !bg-gray-200/70 hover:!bg-gray-300/70 dark:text-white dark:!bg-white/10 dark:hover:!bg-white/10";
|
|
|
+const classeItemSidebarInativo =
|
|
|
+ "text-gray-600 hover:text-gray-900 hover:!bg-gray-300/70 dark:text-white/70 dark:hover:text-white dark:hover:!bg-white/10";
|
|
|
+const classeIconAtivo = "text-gray-900 dark:text-white";
|
|
|
+const classeIconInativo = "text-gray-500 dark:text-white/60";
|
|
|
+const classeRotulo = computed(() =>
|
|
|
+ sidebarRecolhida.value ? "sr-only" : "min-w-0 truncate",
|
|
|
+);
|
|
|
+
|
|
|
+function classeItemSidebar(ativo) {
|
|
|
+ return [
|
|
|
+ classeItemSidebarBase.value,
|
|
|
+ ativo ? classeItemSidebarAtivo : classeItemSidebarInativo,
|
|
|
+ ];
|
|
|
+}
|
|
|
+
|
|
|
+function classeIcon(ativo) {
|
|
|
+ return ["h-5 w-5 shrink-0", ativo ? classeIconAtivo : classeIconInativo];
|
|
|
+}
|
|
|
const tituloPagina = computed(() => {
|
|
|
const nome = rotaAtiva.value;
|
|
|
if (nome === "home") return "Nova conversa";
|
|
|
@@ -31,6 +77,12 @@ const tituloPagina = computed(() => {
|
|
|
return "Oráculo";
|
|
|
});
|
|
|
|
|
|
+const tituloModal = computed(() => {
|
|
|
+ if (painelAberto.value === "update") return "Update";
|
|
|
+ if (painelAberto.value === "documentos") return "Documentos";
|
|
|
+ return "";
|
|
|
+});
|
|
|
+
|
|
|
function irPara(nome) {
|
|
|
router.push({ name: nome });
|
|
|
}
|
|
|
@@ -40,11 +92,28 @@ function alternarPainel(nome) {
|
|
|
painelAberto.value = painelAberto.value === n ? "" : n;
|
|
|
}
|
|
|
|
|
|
+function fecharModal() {
|
|
|
+ painelAberto.value = "";
|
|
|
+}
|
|
|
+
|
|
|
async function novaConversa() {
|
|
|
newConversation();
|
|
|
await router.push({ name: "home" });
|
|
|
}
|
|
|
|
|
|
+async function encerrarSessao() {
|
|
|
+ if (logoutLoading.value) return;
|
|
|
+
|
|
|
+ logoutLoading.value = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await logout();
|
|
|
+ await router.replace({ name: "login" });
|
|
|
+ } finally {
|
|
|
+ logoutLoading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
async function loadDocs() {
|
|
|
docs.loading = true;
|
|
|
docs.error = "";
|
|
|
@@ -75,123 +144,373 @@ async function onIngested({ text, source, file } = {}) {
|
|
|
onMounted(async () => {
|
|
|
await loadDocs();
|
|
|
});
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ const v = window.localStorage.getItem("layout.sidebarRecolhida");
|
|
|
+ if (v != null) sidebarRecolhida.value = v === "1" || v === "true";
|
|
|
+});
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => sidebarRecolhida.value,
|
|
|
+ (v) => {
|
|
|
+ window.localStorage.setItem("layout.sidebarRecolhida", v ? "1" : "0");
|
|
|
+ },
|
|
|
+);
|
|
|
+
|
|
|
+function onKeyDown(e) {
|
|
|
+ if (!painelAberto.value) return;
|
|
|
+ if (e?.key === "Escape") fecharModal();
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ window.addEventListener("keydown", onKeyDown);
|
|
|
+});
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener("keydown", onKeyDown);
|
|
|
+});
|
|
|
+
|
|
|
+let overflowAntes = "";
|
|
|
+watch(
|
|
|
+ () => painelAberto.value,
|
|
|
+ (v) => {
|
|
|
+ const aberto = Boolean(v);
|
|
|
+ if (aberto) {
|
|
|
+ overflowAntes = document.body.style.overflow || "";
|
|
|
+ document.body.style.overflow = "hidden";
|
|
|
+ } else {
|
|
|
+ document.body.style.overflow = overflowAntes;
|
|
|
+ }
|
|
|
+ },
|
|
|
+);
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <div class="app">
|
|
|
- <div class="appShell">
|
|
|
- <aside class="sidebar">
|
|
|
- <div class="sidebarHeader">
|
|
|
- <div class="brandTitle">ORÁCULO</div>
|
|
|
- <div class="brandSub">Assistente interno com base de conhecimento</div>
|
|
|
+ <div class="h-screen overflow-hidden bg-background text-foreground">
|
|
|
+ <div
|
|
|
+ class="[--layout-header-height:5rem] relative grid h-full min-h-0 grid-cols-[var(--layout-sidebar-width)_1fr] grid-rows-[var(--layout-header-height)_1fr] overflow-hidden transition-[grid-template-columns] duration-200 ease-in-out max-[980px]:grid-cols-1 max-[980px]:grid-rows-[var(--layout-header-height)_1fr_auto]"
|
|
|
+ :style="layoutStyle"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ aria-hidden="true"
|
|
|
+ class="pointer-events-none absolute inset-0 z-0 bg-[radial-gradient(900px_520px_at_20%_10%,rgba(37,99,235,0.12),transparent_60%),radial-gradient(700px_520px_at_85%_35%,rgba(162,102,255,0.10),transparent_60%)] dark:hidden"
|
|
|
+ />
|
|
|
+ <div
|
|
|
+ aria-hidden="true"
|
|
|
+ class="pointer-events-none absolute inset-0 z-0 hidden bg-[radial-gradient(900px_520px_at_20%_10%,rgba(91,140,255,0.18),transparent_60%),radial-gradient(700px_520px_at_85%_35%,rgba(162,102,255,0.14),transparent_60%)] dark:block"
|
|
|
+ />
|
|
|
+
|
|
|
+ <header
|
|
|
+ class="col-span-full row-start-1 relative z-10 grid grid-cols-[var(--layout-sidebar-width)_1fr] items-center gap-[18px] border-b border-gray-200 bg-primary px-[18px] py-[14px] text-primary-foreground dark:border-gray-700 dark:bg-primary max-[980px]:flex"
|
|
|
+ >
|
|
|
+ <div class="min-w-0 overflow-hidden">
|
|
|
+ <div
|
|
|
+ class="truncate whitespace-nowrap text-[16px] font-extrabold tracking-[0.14em]"
|
|
|
+ >
|
|
|
+ ORÁCULO
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="!sidebarRecolhida"
|
|
|
+ class="mt-1.5 text-xs text-primary-foreground/80"
|
|
|
+ >
|
|
|
+ Assistente interno com base de conhecimento
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="min-w-0 flex items-center justify-between gap-4 max-[980px]:flex-1"
|
|
|
+ >
|
|
|
+ <div class="min-w-0">
|
|
|
+ <div class="truncate text-base font-medium">{{ tituloPagina }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
+ <div class="hidden text-right md:block">
|
|
|
+ <div class="text-sm font-semibold leading-tight">
|
|
|
+ {{ usuarioNome }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ <BotaoAlterarTema />
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="rounded-lg !border-primary !bg-none !bg-primary px-3 py-2 text-sm font-medium !text-primary-foreground !transform-none transition-colors hover:!transform-none hover:!border-[var(--primary-600)] hover:!bg-[var(--primary-600)]"
|
|
|
+ :disabled="logoutLoading"
|
|
|
+ @click="encerrarSessao"
|
|
|
+ >
|
|
|
+ {{ logoutLoading ? "Saindo..." : "Sair" }}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ </header>
|
|
|
|
|
|
- <div class="sidebarScroll">
|
|
|
- <div class="panel sidebarNav">
|
|
|
+ <aside
|
|
|
+ class="col-start-1 row-start-2 relative z-10 flex min-h-0 min-w-0 flex-col gap-3 overflow-hidden border-r border-gray-200 bg-transparent p-0 dark:border-gray-700 max-[980px]:row-start-3 max-[980px]:border-r-0 max-[980px]:border-t"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="flex min-h-0 flex-1 flex-col overflow-hidden rounded-[28px] border border-gray-200 bg-white/70 p-3 shadow-sm backdrop-blur-md dark:border-white/10 dark:bg-gradient-to-b dark:from-[#101827] dark:to-[#070b16] dark:shadow-[0_18px_50px_rgba(0,0,0,0.35)]"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ :class="
|
|
|
+ sidebarRecolhida
|
|
|
+ ? 'flex items-center justify-center px-1 pb-1'
|
|
|
+ : 'flex items-center justify-end px-1 pb-1'
|
|
|
+ "
|
|
|
+ >
|
|
|
<button
|
|
|
type="button"
|
|
|
- class="sidebarNavButton"
|
|
|
- :class="{ active: rotaAtiva === 'home' }"
|
|
|
+ class="inline-flex h-9 w-9 items-center justify-center rounded-xl border border-gray-200 bg-white/60 text-gray-600 transition-colors hover:bg-white/80 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background dark:border-white/10 dark:bg-white/5 dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
|
|
|
+ :aria-label="
|
|
|
+ sidebarRecolhida ? 'Expandir sidebar' : 'Recolher sidebar'
|
|
|
+ "
|
|
|
+ :title="sidebarRecolhida ? 'Expandir sidebar' : 'Recolher sidebar'"
|
|
|
+ @click="sidebarRecolhida = !sidebarRecolhida"
|
|
|
+ >
|
|
|
+ <svg
|
|
|
+ v-if="sidebarRecolhida"
|
|
|
+ class="h-5 w-5"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.8"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="m9 6 6 6-6 6"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ <svg
|
|
|
+ v-else
|
|
|
+ class="h-5 w-5"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.8"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="m15 6-6 6 6 6"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <nav class="grid gap-1.5">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ :class="classeItemSidebar(rotaAtiva === 'home')"
|
|
|
+ :title="sidebarRecolhida ? 'Nova conversa' : ''"
|
|
|
@click="novaConversa"
|
|
|
>
|
|
|
- Nova conversa
|
|
|
+ <svg
|
|
|
+ :class="classeIcon(rotaAtiva === 'home')"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.8"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M3 10.5 12 3l9 7.5V21a.75.75 0 0 1-.75.75H3.75A.75.75 0 0 1 3 21v-10.5Z"
|
|
|
+ />
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M9 21v-7.5a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 .75.75V21"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ <span :class="classeRotulo">Nova conversa</span>
|
|
|
</button>
|
|
|
+
|
|
|
<button
|
|
|
type="button"
|
|
|
- class="sidebarNavButton"
|
|
|
- :class="{ active: rotaAtiva === 'conversas-pesquisar' }"
|
|
|
+ :class="classeItemSidebar(rotaAtiva === 'conversas-pesquisar')"
|
|
|
+ :title="sidebarRecolhida ? 'Pesquisar conversas' : ''"
|
|
|
@click="irPara('conversas-pesquisar')"
|
|
|
>
|
|
|
- Pesquisar conversas
|
|
|
+ <svg
|
|
|
+ :class="classeIcon(rotaAtiva === 'conversas-pesquisar')"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.8"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z"
|
|
|
+ />
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M16.5 16.5 21 21"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ <span :class="classeRotulo">Pesquisar conversas</span>
|
|
|
</button>
|
|
|
+
|
|
|
<button
|
|
|
type="button"
|
|
|
- class="sidebarNavButton"
|
|
|
- :class="{ active: rotaAtiva === 'conversas-historico' }"
|
|
|
+ :class="classeItemSidebar(rotaAtiva === 'conversas-historico')"
|
|
|
+ :title="sidebarRecolhida ? 'Histórico' : ''"
|
|
|
@click="irPara('conversas-historico')"
|
|
|
>
|
|
|
- Histórico
|
|
|
+ <svg
|
|
|
+ :class="classeIcon(rotaAtiva === 'conversas-historico')"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.8"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M12 6v6l4 2"
|
|
|
+ />
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M21 12a9 9 0 1 1-3.2-6.9"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ <span :class="classeRotulo">Histórico</span>
|
|
|
</button>
|
|
|
+ </nav>
|
|
|
|
|
|
- <div class="sidebarNavDivider" />
|
|
|
+ <div class="mt-auto grid gap-1.5 pt-3">
|
|
|
+ <div class="my-1 h-px bg-black/10 dark:bg-white/10" />
|
|
|
|
|
|
<button
|
|
|
type="button"
|
|
|
- class="sidebarNavButton"
|
|
|
- :class="{ active: painelAberto === 'ingestao' }"
|
|
|
- @click="alternarPainel('ingestao')"
|
|
|
+ :class="classeItemSidebar(painelAberto === 'update')"
|
|
|
+ :title="sidebarRecolhida ? 'Update' : ''"
|
|
|
+ @click="alternarPainel('update')"
|
|
|
>
|
|
|
- Ingestão
|
|
|
+ <svg
|
|
|
+ :class="classeIcon(painelAberto === 'update')"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.8"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M12 3v12"
|
|
|
+ />
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M7.5 7.5 12 3l4.5 4.5"
|
|
|
+ />
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M4 21h16"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ <span :class="classeRotulo">Update</span>
|
|
|
</button>
|
|
|
+
|
|
|
<button
|
|
|
type="button"
|
|
|
- class="sidebarNavButton"
|
|
|
- :class="{ active: painelAberto === 'documentos' }"
|
|
|
+ :class="classeItemSidebar(painelAberto === 'documentos')"
|
|
|
+ :title="sidebarRecolhida ? 'Documentos' : ''"
|
|
|
@click="alternarPainel('documentos')"
|
|
|
>
|
|
|
- Documentos
|
|
|
+ <svg
|
|
|
+ :class="classeIcon(painelAberto === 'documentos')"
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ fill="none"
|
|
|
+ stroke="currentColor"
|
|
|
+ stroke-width="1.8"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M7 3h7l3 3v15a.75.75 0 0 1-.75.75H7.75A.75.75 0 0 1 7 21V3Z"
|
|
|
+ />
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M14 3v4h4"
|
|
|
+ />
|
|
|
+ <path
|
|
|
+ stroke-linecap="round"
|
|
|
+ stroke-linejoin="round"
|
|
|
+ d="M9 12h6M9 15.5h6"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ <span :class="classeRotulo">Documentos</span>
|
|
|
</button>
|
|
|
</div>
|
|
|
-
|
|
|
- <DocumentUpload v-if="painelAberto === 'ingestao'" @ingested="onIngested" />
|
|
|
- <template v-if="painelAberto === 'documentos'">
|
|
|
- <SearchBox :results="searchResults" :loading="searchLoading" :error="searchError" @search="runSearch" />
|
|
|
- <DocumentList :items="docs.items" :loading="docs.loading" :error="docs.error" @refresh="loadDocs" />
|
|
|
- </template>
|
|
|
</div>
|
|
|
|
|
|
- <div class="sidebarFooter muted">
|
|
|
- Backend: http://localhost:3001 · Qdrant: http://localhost:6333 · Ollama: http://localhost:11434
|
|
|
- </div>
|
|
|
+
|
|
|
</aside>
|
|
|
|
|
|
- <main class="main !flex !flex-col !p-0 !overflow-hidden">
|
|
|
- <header
|
|
|
- class="sticky top-0 z-30 h-16 flex items-center justify-between gap-4 px-4 md:px-6 bg-primary text-primary-foreground border-b border-border backdrop-blur-sm"
|
|
|
- >
|
|
|
- <div class="min-w-0">
|
|
|
- <div class="text-base font-medium truncate">{{ tituloPagina }}</div>
|
|
|
+ <Teleport to="body">
|
|
|
+ <div v-if="painelAberto" class="fixed inset-0 z-[9999]">
|
|
|
+ <div
|
|
|
+ class="absolute inset-0 bg-black/40 dark:bg-black/60"
|
|
|
+ @click="fecharModal"
|
|
|
+ />
|
|
|
+ <div class="absolute inset-0 flex items-center justify-center p-4">
|
|
|
+ <div
|
|
|
+ class="w-full max-w-[980px] overflow-hidden rounded-2xl border border-gray-200 bg-background text-foreground shadow-lg dark:border-gray-700"
|
|
|
+ @click.stop
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="flex items-center justify-between gap-3 border-b border-gray-300 px-4 py-3 dark:border-gray-700"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="text-sm font-semibold text-gray-900 dark:text-gray-100"
|
|
|
+ >
|
|
|
+ {{ tituloModal }}
|
|
|
+ </div>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="rounded-lg border border-primary bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-colors hover:border-[var(--primary-600)] hover:bg-[var(--primary-600)]"
|
|
|
+ @click="fecharModal"
|
|
|
+ >
|
|
|
+ Fechar
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="max-h-[calc(100vh-8rem)] overflow-auto p-4">
|
|
|
+ <DocumentUpload
|
|
|
+ v-if="painelAberto === 'update'"
|
|
|
+ @ingested="onIngested"
|
|
|
+ />
|
|
|
+ <template v-else-if="painelAberto === 'documentos'">
|
|
|
+ <SearchBox
|
|
|
+ :results="searchResults"
|
|
|
+ :loading="searchLoading"
|
|
|
+ :error="searchError"
|
|
|
+ @search="runSearch"
|
|
|
+ />
|
|
|
+ <div class="h-3" />
|
|
|
+ <DocumentList
|
|
|
+ :items="docs.items"
|
|
|
+ :loading="docs.loading"
|
|
|
+ :error="docs.error"
|
|
|
+ @refresh="loadDocs"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <BotaoAlterarTema />
|
|
|
- </header>
|
|
|
+ </div>
|
|
|
+ </Teleport>
|
|
|
|
|
|
- <div class="flex-1 overflow-auto p-4 md:p-6">
|
|
|
+ <main
|
|
|
+ class="col-start-2 row-start-2 relative z-10 flex min-h-0 min-w-0 flex-col overflow-hidden p-0 max-[980px]:col-start-1 max-[980px]:row-start-2"
|
|
|
+ >
|
|
|
+ <div class="flex-1 min-h-0 overflow-auto bg-black/10 p-4 md:p-6">
|
|
|
<slot />
|
|
|
</div>
|
|
|
</main>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.sidebarNav {
|
|
|
- display: grid;
|
|
|
- align-content: end;
|
|
|
- gap: 6px;
|
|
|
- padding: 6px;
|
|
|
-}
|
|
|
-
|
|
|
-.sidebarNavButton {
|
|
|
- width: 100%;
|
|
|
- height: 95%;
|
|
|
- text-align: center;
|
|
|
- background: var(--primary);
|
|
|
- color: var(--primary-foreground);
|
|
|
- border-color: var(--primary);
|
|
|
-}
|
|
|
-
|
|
|
-.sidebarNavButton:hover:not(:disabled) {
|
|
|
- background: var(--primary-600);
|
|
|
- border-color: var(--primary-600);
|
|
|
-}
|
|
|
-
|
|
|
-.sidebarNavButton.active {
|
|
|
- border-color: var(--ring);
|
|
|
-}
|
|
|
-
|
|
|
-.sidebarNavDivider {
|
|
|
- height: 1px;
|
|
|
- background: var(--border);
|
|
|
- margin: 4px 0;
|
|
|
-}
|
|
|
-</style>
|