Capa BoardSports com surf, snowboard e skate

Apresentação técnica / PAP 2026

BoardSports explicado página a página.

Código essencial, prints do site e lógica por trás de cada página.

index.html

Página inicial.

Print da página inicial BoardSports
<section class="hero-reel">
  <div data-cinematic-reel>
    <img class="hero-reel__frame is-active" src="assets/images/do-mar-a-tua-rua.jpg">
  </div>
  <a href="mapa.html">Encontrar a próxima sessão</a>
  <a href="register.html">Criar perfil de rider</a>
</section>
<script type="module" src="js/landing_page.js"></script>

login.html

Página de login.

loginForm.onsubmit = async (event) => {
  event.preventDefault()
  const email = emailInput.value.trim()
  const password = passwordInput.value
  const resultado = await fazerLogin(email, password)
  if (resultado.sucesso) window.location.href = '/mapa.html'
}

register.html

Página de registo.

const resultado = await fazerRegistro(email, password, tipoUser, {
  nome: nomeInput.value,
  telefone: telefoneInput.value,
  localidade: localidadeInput.value,
  bio: bioInput.value
})

if (resultado.sucesso) window.location.href = '/verify-email.html'

reset-password.html / verify-email.html

Recuperação e verificação de conta.

await pedirRecuperacaoPassword(email)

const result = await processarRedirecionamentoAuth()
if (result.sucesso) {
  await atualizarStatusEmailVerificado()
  window.location.href = '/mapa.html'
}

mapa.html

Página do mapa.

Print da página do mapa de spots
async function initMapPage() {
  initMap()
  bindEvents()
  user = await obterUsuarioAtual()
  await loadModalidadeOptions()
  await loadSpots()
}

document.addEventListener('DOMContentLoaded', initMapPage)

spot.html

Página de detalhe do spot.

async function init() {
  user = await obterUsuarioAtual()
  spot = await obterSpotById(spotId)
  videos = await obterGaleriaVídeosSpots({ spot_id: spotId })
  comentarios = await obterComentarios('spot', spotId)
  recomendacoes = await obterRecomendacoesSpots(spot)
  renderSpot()
}

videos.html

Página das sessões em vídeo.

Print da galeria de vídeos
async function initVídeosPage() {
  modalidades = await obterModalidades()
  videos = await obterGaleriaVídeosSpots()
  renderModalidadeFilter()
  renderVideos(videos)
}

ui.search?.addEventListener('input', applyFilters)

leaderboard.html

Página do ranking XP.

Print da página de ranking XP
async function carregarLeaderboard() {
  const filtro = ui.filter.value
  const modalidadeId = ui.modalidade.value
  const ranking = await obterLeaderboardXp(filtro, modalidadeId)
  renderLeaderboard(ranking)
}

pesquisa.html

Página de pesquisa global.

Print da página de pesquisa
async function runSearch() {
  const termo = ui.input.value.trim()
  if (termo.length < 2) return
  const results = await pesquisarGlobal(termo)
  renderResults(results)
}

perfil.html

Página de perfil.

async function inicializarPerfil() {
  currentUser = await obterUsuarioAtual()
  perfil = await obterPerfil(profileId || currentUser.id)
  publicacoes = await obterPublicacoesPerfil(perfil.id)
  mensagens = await obterMensagensUsuario(currentUser.id)
  renderPerfil()
}

configuracao.html

Página de configuração.

ui.form.addEventListener('submit', onSubmit)
ui.photoInput.addEventListener('change', onPhotoSelected)
ui.passwordForm.addEventListener('submit', onPasswordSubmit)

await atualizarPerfil(currentUser.id, updates)
await uploadFotoPerfil(currentUser.id, file, currentUrl)

notificacoes.html

Página de notificações.

async function loadNotifications() {
  const items = await obterNotificacoes({ apenasNaoLidas: false })
  renderNotifications(items)
}

ui.refresh.addEventListener('click', loadNotifications)

admin-login.html / moderacao.html

Páginas de administração.

state.user = await obterUsuarioAtual()
if (state.user?.perfil?.is_admin !== true) return renderAccessDenied()

await Promise.all([
  carregarSolicitacoes(),
  carregarSubmissoesXp(),
  carregarDenuncias()
])

sobre.html / contacto.html / termos.html / privacidade.html / 404.html

Páginas institucionais.

<main class="institutional-shell">
  <section class="institutional-hero">
    <h1>Sobre / Contacto / Termos / Privacidade</h1>
  </section>
</main>

<script type="module" src="js/site_header.js"></script>