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.
<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>
- Linha 1: cria a zona principal da página inicial.
- Linhas 2-3: mostram a imagem principal da marca.
- Linhas 5-6: levam o utilizador para o mapa ou para o registo.
- Linha 8: carrega o JavaScript da landing page.
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'
}
- Linha 1: executa código quando o formulário é submetido.
- Linha 2: impede o recarregamento normal da página.
- Linhas 3-4: lêem email e password dos inputs.
- Linha 5: chama a função de login.
- Linha 6: redireciona para o mapa se o login for válido.
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'
- Linha 1: chama a função que cria a conta.
- Linhas 2-5: enviam os dados iniciais do perfil.
- Linha 8: abre a página de verificação de email.
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'
}
- Linha 1: envia pedido de recuperação de password.
- Linha 3: processa o link enviado pelo Supabase.
- Linhas 4-6: confirmam o email e redirecionam para o mapa.
mapa.html
Página do mapa.
async function initMapPage() {
initMap()
bindEvents()
user = await obterUsuarioAtual()
await loadModalidadeOptions()
await loadSpots()
}
document.addEventListener('DOMContentLoaded', initMapPage)
- Linha 2: cria o mapa Leaflet.
- Linha 3: liga filtros, botões e formulários.
- Linha 4: obtém o utilizador atual.
- Linhas 5-6: carregam modalidades e spots.
- Linha 9: inicia a página quando o HTML acaba de carregar.
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()
}
- Linha 2: identifica o utilizador atual.
- Linha 3: carrega o spot aberto pelo ID.
- Linha 4: carrega vídeos do spot.
- Linha 5: carrega comentários.
- Linha 6: calcula recomendações.
- Linha 7: desenha os dados na página.
videos.html
Página das sessões em vídeo.
async function initVídeosPage() {
modalidades = await obterModalidades()
videos = await obterGaleriaVídeosSpots()
renderModalidadeFilter()
renderVideos(videos)
}
ui.search?.addEventListener('input', applyFilters)
- Linha 2: carrega modalidades para o filtro.
- Linha 3: carrega vídeos publicados.
- Linha 4: desenha o filtro.
- Linha 5: desenha a galeria.
- Linha 8: aplica pesquisa/filtros.
leaderboard.html
Página do ranking XP.
async function carregarLeaderboard() {
const filtro = ui.filter.value
const modalidadeId = ui.modalidade.value
const ranking = await obterLeaderboardXp(filtro, modalidadeId)
renderLeaderboard(ranking)
}
- Linha 2: lê o filtro escolhido.
- Linha 3: lê a modalidade escolhida.
- Linha 4: busca o ranking na base de dados.
- Linha 5: desenha o ranking no ecrã.
pesquisa.html
Página de pesquisa global.
async function runSearch() {
const termo = ui.input.value.trim()
if (termo.length < 2) return
const results = await pesquisarGlobal(termo)
renderResults(results)
}
- Linha 2: lê o texto pesquisado.
- Linha 3: evita pesquisas demasiado curtas.
- Linha 4: procura spots, vídeos e utilizadores.
- Linha 5: mostra os resultados.
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()
}
- Linha 2: obtém a conta autenticada.
- Linha 3: carrega o perfil.
- Linha 4: carrega publicações.
- Linha 5: carrega mensagens.
- Linha 6: desenha a página.
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)
- Linha 1: atualiza dados do perfil.
- Linha 2: reage à escolha de nova foto.
- Linha 3: atualiza password.
- Linha 5: grava alterações em
profiles. - Linha 6: envia avatar para o Storage.
notificacoes.html
Página de notificações.
async function loadNotifications() {
const items = await obterNotificacoes({ apenasNaoLidas: false })
renderNotifications(items)
}
ui.refresh.addEventListener('click', loadNotifications)
- Linha 2: carrega notificações do utilizador.
- Linha 3: desenha a lista.
- Linha 6: recarrega quando o utilizador clica em atualizar.
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()
])
- Linha 1: obtém o utilizador atual.
- Linha 2: bloqueia quem não é admin.
- Linhas 4-8: carrega pedidos, XP e denúncias.
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>
- Linha 1: usa o layout comum destas páginas.
- Linha 2: cria a área principal.
- Linha 3: representa o conteúdo textual.
- Linha 7: carrega o menu global.