mesa-react / frontend /src /deepLinks.js
Guilherme Silberfarb Costa
Enhance avaliacao map and KNN interactions
6240ef0
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)
}