const TAB_LABEL_BY_SLUG = { modelos: 'Modelos Estatísticos', elaboracao: 'Elaboração/Edição', avaliacao: 'Avaliação', trabalhos: 'Trabalhos Técnicos', } const TAB_SLUG_BY_LABEL = Object.fromEntries( Object.entries(TAB_LABEL_BY_SLUG).map(([slug, label]) => [label, slug]), ) const MODELOS_SUBTAB_LABEL_BY_SLUG = { pesquisa: 'Pesquisar Modelos', repositorio: 'Repositório de Modelos', 'visao-geral': 'Visão Geral', } const MODELOS_SUBTAB_SLUG_BY_LABEL = Object.fromEntries( Object.entries(MODELOS_SUBTAB_LABEL_BY_SLUG).map(([slug, label]) => [label, slug]), ) const TRABALHOS_SUBTAB_LABEL_BY_SLUG = { repositorio: 'repositorio', mapa: 'mapa', } const TRABALHOS_SUBTAB_SLUG_BY_LABEL = Object.fromEntries( Object.entries(TRABALHOS_SUBTAB_LABEL_BY_SLUG).map(([slug, label]) => [label, slug]), ) const MODEL_TAB_SET = new Set([ 'mapa', 'trabalhos-tecnicos', 'dados-mercado', 'metricas', 'transformacoes', 'resumo', 'coeficientes', 'obs-calc', 'graficos', ]) const PESQUISA_FILTER_KEYS = [ 'nomeModelo', 'tipoModelo', 'negociacaoModelo', 'dataMin', 'dataMax', 'versionamentoModelos', 'avalFinalidade', 'avalZona', 'avalBairro', 'avalArea', 'avalRh', ] const PESQUISA_FILTER_DEFAULTS = { nomeModelo: '', tipoModelo: '', negociacaoModelo: '', dataMin: '', dataMax: '', versionamentoModelos: 'incluir_antigos', avalFinalidade: '', avalZona: '', avalBairro: '', avalArea: '', avalRh: '', } function normalizeSlug(value) { return String(value || '').trim().toLowerCase() } function trimValue(value) { return String(value || '').trim() } function parseFiniteNumber(value) { const text = trimValue(value).replace(',', '.') if (!text) return null const parsed = Number(text) return Number.isFinite(parsed) ? parsed : null } function sanitizePesquisaFilters(rawFilters = {}) { const next = { ...PESQUISA_FILTER_DEFAULTS } PESQUISA_FILTER_KEYS.forEach((key) => { if (!Object.prototype.hasOwnProperty.call(rawFilters, key)) return const value = trimValue(rawFilters[key]) if (key === 'versionamentoModelos') { next[key] = value === 'atuais' ? 'atuais' : 'incluir_antigos' return } next[key] = value }) return next } function hasPesquisaFilters(filters = {}) { return PESQUISA_FILTER_KEYS.some((key) => { const value = key === 'versionamentoModelos' ? trimValue(filters[key] || PESQUISA_FILTER_DEFAULTS[key]) : trimValue(filters[key]) if (key === 'versionamentoModelos') return value === 'atuais' return Boolean(value) }) } export function getAppTabKeyFromSlug(slug) { return TAB_LABEL_BY_SLUG[normalizeSlug(slug)] || '' } export function getAppTabSlugFromKey(label) { return TAB_SLUG_BY_LABEL[String(label || '').trim()] || '' } export function getModelosSubtabKeyFromSlug(slug) { return MODELOS_SUBTAB_LABEL_BY_SLUG[normalizeSlug(slug)] || MODELOS_SUBTAB_LABEL_BY_SLUG.pesquisa } export function getModelosSubtabSlugFromKey(label) { return MODELOS_SUBTAB_SLUG_BY_LABEL[String(label || '').trim()] || 'pesquisa' } export function getTrabalhosSubtabKeyFromSlug(slug) { return TRABALHOS_SUBTAB_LABEL_BY_SLUG[normalizeSlug(slug)] || TRABALHOS_SUBTAB_LABEL_BY_SLUG.repositorio } export function getTrabalhosSubtabSlugFromKey(label) { return TRABALHOS_SUBTAB_SLUG_BY_LABEL[String(label || '').trim()] || 'repositorio' } export function getModelTabSlug(value) { const slug = normalizeSlug(value) return MODEL_TAB_SET.has(slug) ? slug : 'mapa' } export function hasMesaDeepLink(intent) { if (!intent || typeof intent !== 'object') return false return Boolean( trimValue(intent.tab) || trimValue(intent.modeloId) || trimValue(intent.trabalhoId) || trimValue(intent.subtab) || hasPesquisaFilters(intent.filters) || (intent.avaliando && parseFiniteNumber(intent.avaliando.lat) !== null && parseFiniteNumber(intent.avaliando.lon) !== null), ) } export function normalizeMesaDeepLink(intent = {}) { const safeIntent = intent && typeof intent === 'object' ? intent : {} const rawTab = normalizeSlug(safeIntent.tab) const modeloId = trimValue(safeIntent.modeloId) const trabalhoId = trimValue(safeIntent.trabalhoId) let tab = TAB_LABEL_BY_SLUG[rawTab] ? rawTab : '' if (!tab) { if (modeloId) tab = 'modelos' if (!tab && trabalhoId) tab = 'trabalhos' } let subtab = normalizeSlug(safeIntent.subtab) if (tab === 'modelos') { if (modeloId) { subtab = 'repositorio' } else if (!MODELOS_SUBTAB_LABEL_BY_SLUG[subtab]) { subtab = 'pesquisa' } } else if (tab === 'trabalhos') { if (!TRABALHOS_SUBTAB_LABEL_BY_SLUG[subtab]) subtab = 'repositorio' } else { subtab = '' } const filters = sanitizePesquisaFilters(safeIntent.filters) const lat = parseFiniteNumber(safeIntent.avaliando?.lat) const lon = parseFiniteNumber(safeIntent.avaliando?.lon) const avaliando = lat !== null && lon !== null ? { lat, lon } : null const modelTab = tab === 'modelos' && subtab === 'repositorio' && modeloId ? getModelTabSlug(safeIntent.modelTab) : '' return { tab, subtab, modelTab, modeloId, trabalhoId, filters, avaliando, } } export function parseMesaDeepLink(search = '') { const params = new URLSearchParams(String(search || '').replace(/^\?/, '')) const filters = {} PESQUISA_FILTER_KEYS.forEach((key) => { if (!params.has(key)) return filters[key] = params.get(key) }) return normalizeMesaDeepLink({ tab: params.get('tab'), subtab: params.get('subtab'), modelTab: params.get('modelTab'), modeloId: params.get('modeloId'), trabalhoId: params.get('trabalhoId'), filters, avaliando: { lat: params.get('avalLat'), lon: params.get('avalLon'), }, }) } export function serializeMesaDeepLink(intent = {}) { const normalized = normalizeMesaDeepLink(intent) const params = new URLSearchParams() if (normalized.tab) params.set('tab', normalized.tab) if (normalized.tab === 'modelos' && normalized.subtab) { params.set('subtab', normalized.subtab) } if (normalized.tab === 'trabalhos' && normalized.subtab && !normalized.trabalhoId) { params.set('subtab', normalized.subtab) } if (normalized.modeloId) params.set('modeloId', normalized.modeloId) if (normalized.trabalhoId) params.set('trabalhoId', normalized.trabalhoId) if (normalized.modelTab) params.set('modelTab', normalized.modelTab) if (normalized.tab === 'modelos' && normalized.subtab === 'pesquisa') { PESQUISA_FILTER_KEYS.forEach((key) => { const value = key === 'versionamentoModelos' ? trimValue(normalized.filters[key] || PESQUISA_FILTER_DEFAULTS[key]) : trimValue(normalized.filters[key]) if (key === 'versionamentoModelos') { if (value === 'atuais') params.set(key, value) return } if (value) params.set(key, value) }) if (normalized.avaliando) { params.set('avalLat', String(normalized.avaliando.lat)) params.set('avalLon', String(normalized.avaliando.lon)) } } const query = params.toString() return query ? `?${query}` : '' } export function buildMesaUrl(intent = {}) { if (typeof window === 'undefined') return serializeMesaDeepLink(intent) const url = new URL(window.location.href) url.search = serializeMesaDeepLink(intent) url.hash = '' return url.toString() } function syncHuggingFaceParentUrl(queryString = '', hash = '') { if (typeof window === 'undefined') return if (!window.parent || window.parent === window) return try { window.parent.postMessage( { queryString, hash, }, 'https://huggingface.co', ) } catch { // Ignora falhas cross-origin e mantém o sync local no iframe. } } export function replaceMesaDeepLink(intent = {}) { if (typeof window === 'undefined') return const queryString = serializeMesaDeepLink(intent) window.history.replaceState(null, '', buildMesaUrl(intent)) syncHuggingFaceParentUrl(queryString, '') } export function pushMesaDeepLink(intent = {}) { if (typeof window === 'undefined') return const queryString = serializeMesaDeepLink(intent) window.history.pushState(null, '', buildMesaUrl(intent)) syncHuggingFaceParentUrl(queryString, '') } export function buildRepositorioModeloLink(modeloId, modelTab = 'mapa') { return buildMesaUrl({ tab: 'modelos', subtab: 'repositorio', modeloId, modelTab, }) } export function buildAvaliacaoModeloLink(modeloId) { return buildMesaUrl({ tab: 'avaliacao', modeloId, }) } export function buildElaboracaoModeloLink(modeloId) { return buildMesaUrl({ tab: 'elaboracao', modeloId, }) } export function buildTrabalhoTecnicoLink(trabalhoId) { return buildMesaUrl({ tab: 'trabalhos', trabalhoId, }) } export function buildPesquisaLink(filters = {}, avaliando = null) { return buildMesaUrl({ tab: 'modelos', subtab: 'pesquisa', filters, avaliando, }) } export function buildPesquisaRoutePayload(filters = {}, avaliando = null) { return normalizeMesaDeepLink({ tab: 'modelos', subtab: 'pesquisa', filters, avaliando, }) } export function getPesquisaFilterDefaults() { return { ...PESQUISA_FILTER_DEFAULTS } } export function hasPesquisaRoutePayload(filters = {}, avaliando = null) { return hasPesquisaFilters(filters) || Boolean(avaliando) }