Introdução
O componente Calendário é responsável pela visualização
            centralizada das avaliações de todas as disciplinas de um semestre. Este
            componente foi desenhado para oferecer uma visão global, filtrável e
            interativa das avaliações, integrando dados dinâmicos provenientes de
            ficheiros ucs.json e mantendo consistência visual com o
            resto da interface do utilizador.
- Visualização mensal de avaliações com navegação entre meses.
- Vista alternativa em lista cronológica de todas as avaliações.
- Vista heatmap semestral, representando intensidade de carga académica.
- Filtros por disciplina com seleção múltipla e legenda de cores.
- Integração direta com dados JSON de unidades curriculares.
- Comportamento responsivo e acessível, com foco em desempenho.
Instalação
Instalar dependências
npm install date-fns tailwindcss lucide-vue-nextImportar e registar o componente
import CalendarComponent from "@/components/Calendar.vue";
O componente foi desenvolvido para Vue 3 com suporte a
                Composition API. Não é compatível com Vue 2 sem alterações.
                Certifica-te de que o teu projeto utiliza vue@^3.3+
e
vite ou
nuxt 3.
Utilização Base
O exemplo seguinte demonstra a utilização mínima do componente de
            calendário. Por omissão, este apresenta-se em modo de visualização
calendar (mensal), carregando internamente os dados das
            disciplinas e avaliações.
<template>
  <div class="p-6">
    <Calendar />
  </div>
</template>
<script setup lang="ts">
import Calendar from '@/components/ui/calendar'
</script>
O componente não requer propriedades obrigatórias. Caso não
                sejam fornecidos dados externos, ele inicializa-se com o estado
                interno e os valores padrão definidos na sua configuração. É, no
                entanto, possível passar explicitamente listas de disciplinas e
                avaliações através das
props descritas na secção Propriedades e Eventos.
Propriedades e Eventos
A API do componente foi desenhada para ser flexível, podes utilizar o calendário com dados internos (padrão) ou passar dados e controladores externos para integração avançada. Abaixo está a lista exaustiva de propriedades (props), eventos (emits) e métodos expostos.
-  Props: viewMode,disciplinasData,initialDate,locale,confige mais.
-  Emits / Callbacks: day-click,month-change,filter-change.
-  Métodos: possibilidade de aceder a funções
                    reativas via ref(ex.:goToDate,refreshData).
Assinatura TypeScript
// Tipos principais usados pelo componente
type Avaliacao = {
  data: string; // ISO date 'YYYY-MM-DD'
  descricao: string;
  disciplina?: string;
  slug?: string;
};
type Disciplina = {
  slug: string;
  nome: string;
  avaliacoes: Avaliacao[];
};
interface CalendarConfig {
  firstWeekDay?: number; // 0 = Domingo, 1 = Segunda (padrão usado: 1)
  semesterRange?: { startMonth: number; endMonth: number }; // ex: { startMonth: 8, endMonth: 1 } para set-fev
  heatmapBucketCount?: number; // número de níveis de cor
  maxHeatmapWeeks?: number;
  dateAdapter?: 'native' | 'date-fns' | 'dayjs';
}
// Props expostas
interface CalendarProps {
  viewMode?: 'calendar' | 'list' | 'heatmap';
  disciplinasData?: Disciplina[];
  initialDate?: string | Date;
  locale?: string; // ex: 'pt-PT'
  config?: CalendarConfig;
  showLegend?: boolean;
  compact?: boolean;
  // Callbacks
  onDayClick?: (date: string, avaliacoes: Avaliacao[]) => void;
  onViewChange?: (view: 'calendar' | 'list' | 'heatmap') => void;
  onFilterChange?: (selectedSlugs: string[]) => void;
}
// Métodos expostos via ref (opcional)
interface CalendarHandle {
  goToDate: (date: string | Date) => void;
  nextMonth: () => void;
  previousMonth: () => void;
  refreshData: () => Promise<void>;
}Props detalhadas
| Prop | Tipo | Default | Descrição | 
|---|---|---|---|
| viewMode | 'calendar' | 'list' | 'heatmap' | 'calendar' | Modo de visualização inicial. Pode ser controlado externamente para implementar layouts específicos. | 
| disciplinasData | Array<Disciplina> | undefined | Fonte dos dados, se não fornecida, o componente
                            carrega ucs.jsonpor defeito. Estrutura:{ slug, nome, avaliacoes }. | 
| initialDate | string | Date | new Date() | Data inicial exibida no calendário. Aceita ISO string ('YYYY-MM-DD') ou objeto Date. | 
| locale | string | 'pt-PT' | Localização para formatação de datas. Usado em toLocaleDateStringe títulos. | 
| config | CalendarConfig | Parâmetros avançados: definição do primeiro dia da semana, intervalo do semestre, comportamento do heatmap, e adaptador de datas. | |
| showLegend | boolean | true | Controla a visibilidade da legenda de cores das disciplinas. | 
| compact | boolean | false | Modo compacto para inserção em sidebars ou painéis menores (reduz padding e textos). | 
Eventos / Callbacks
-  day-click — disparado quando o utilizador clica
                num dia com avaliações. Callback: (date: string, avaliacoes: Avaliacao[]) => void.
-  month-change — disparado quando o mês alterou (navegação
                por anterior/próximo). Callback: (year: number, month: number) => void.
-  filter-change — disparado sempre que as disciplinas
                selecionadas são alteradas. Callback: (selectedSlugs: string[]) => void.
-  view-change — disparado quando o utilizador muda
                o modo de visualização (calendar/list/heatmap). Callback: (view: 'calendar' | 'list' | 'heatmap') => void.
Exemplo de utilização controlada (props + emits)
<template>
  <Calendar
    :viewMode="view"
    :disciplinasData="disciplinas"
    :initialDate="initialDate"
    @day-click="onDayClick"
    @filter-change="onFilterChange"
    @view-change="onViewChange"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import CalendarComponent from "@/components/Calendar.vue";
import disciplinas from '@/data/ucs.json'
const view = ref<'calendar'|'list'|'heatmap'>('calendar')
const initialDate = ref(new Date())
function onDayClick(date, avaliacoes) {
  // abre um modal personalizado com informações complementares
  console.log('Dia clicado', date, avaliacoes)
}
function onFilterChange(selected) {
  console.log('Disciplinas selecionadas', selected)
}
function onViewChange(v) {
  view.value = v
}
</script>
Se passares disciplinasData como prop, considera fornecer
                uma cópia imutável ou utilizar um mecanismo de refresh no componente
                para evitar que alterações externas desalinhem estados internos (por
                exemplo: seleção de disciplinas). O componente aceita actualizações
                reativas, mas algumas optimizações (memoização de mapas por slug)
                podem exigir uma chamada a refreshData().
Componentes Internos
O componente principal é composto por vários subcomponentes e unidades lógicas que suportam a experiência: Painel de filtros, Vista de Calendário (grid de dias), Vista de Lista, Heatmap Semestral, Diálogo de detalhe e utilitários auxiliares. Abaixo descrevemos cada um deles, com responsabilidades, API interna e pontos de atenção.
Filtros de Disciplinas
O painel de filtros permite selecionar quais as disciplinas que aparecem nas vistas. É composto por um componente de selecção (Checkbox), um botão para seleccionar/desseleccionar todas e uma área scrollable com a lista de disciplinas.
Responsabilidades
- Manter o estado de seleção por slug.
- Expor um estado "todasSelecionadas" (boolean) e "algumasSelecionadas" (indeterminado).
- 
Emitir filter-changecom a lista de slugs seleccionados.
- Mostrar um indicador numérico de avaliações por disciplina e por mês (estatísticas).
Props internas
-  disciplinas: Disciplina[]— lista completa.
-  selectedMap: Record<string, boolean>— mapa de selecção por slug.
-  onToggle(slug: string)— callback para alternar seleção.
Exemplo: implementação minimal (pseudo)
<template>
  <div class="filters">
    <Checkbox v-model="allSelected" :indeterminate="someSelected" />
    <div class="list">
      <div v-for="d in disciplinas" :key="d.slug" class="item">
        <Checkbox :id="d.slug" v-model="selectedMap[d.slug]" />
        <label :for="d.slug">{{ d.nome }} ({{ d.avaliacoes.length }})</label>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
defineProps({
  disciplinas: Array,
  selectedMap: Object,
})
// lógica: allSelected, someSelected, watchers para emitir filter-change
</script>
As checkboxes devem usar atributos aria-checked
e labels associados com for. Em dispositivos
                    móveis, o painel de filtros abre como Sheet para
                    ocupar ecrã completo.
Vista: Calendário (Grid Mensal)
A vista de calendário é responsável por:
- Gerar a grelha de dias do mês com placeholders para os dias anteriores ao primeiro dia do mês.
- Exibir micro-blocos que representam avaliações por dia, com limite de itens visíveis e indicador "+N".
- Destacar o dia actual e aplicar estilos de foco/hover para interacção.
- Expor clique no dia para abrir o diálogo de detalhes.
Algoritmos importantes
- getFirstDayOfMonth(date): calcula deslocamento para alinhar segunda-feira como primeiro dia da semana (consistente com PT).
- calendarDays: constrói o array de (Date|null) com placeholders.
- getAvaliacoesForDate(date): lookup por string ISO, optimizado com um Map<dateStr, Avaliacao[]> para O(1) por dia.
Estrutura do DOM e classes utilitárias
Cada célula do calendário é um botão semânticamente interativo (ou div com role="button") com:
-  aria-labelcom data formatada e número de avaliações.
- Indicadores visuais por disciplina (bolinhas ou badges).
- Classes responsivas para versão mobile/desktop.
// Optimização sugerida (pseudo)
const avaliacoesMap = computed(() => {
  const map = new Map()
  avaliacoes.forEach(a => {
    map.set(a.data, (map.get(a.data) || []).concat(a))
  })
  return map
})
function getAvaliacoesForDate(date) {
  const key = date.toISOString().split('T')[0]
  return avaliacoesMap.value.get(key) || []
}
Para meses com elevada densidade de avaliações (ex.:
                    centenas por mês), evita renderizações desnecessárias: usa v-for com :key estável, memoiza lookups e limita o número
                    de items renderizados por célula.
Vista: Lista Cronológica
A vista em lista é destinada a leitura sequencial, útil para estudantes que querem ver as próximas avaliações ordenadas por data e agrupadas por mês.
Comportamento
- Ordena por data ascendente (mais próximo primeiro).
- Agrupa por mês (ex.: "Setembro 2025").
- Cada item apresenta: dia, mês abreviado, nome da disciplina, descrição e badge opcional.
- Suporta infinite-scroll ou altura fixa com ScrollArea para usabilidade em listas longas.
// Agrupar por mês (já implementado no componente)
const agruparPorMes = (lista) => {
  const grupos = {}
  lista.forEach(a => {
    const data = new Date(a.data + 'T00:00:00')
    const mesAno = data.toLocaleDateString('pt-PT', { month: 'long', year: 'numeric' })
    if (!grupos[mesAno]) grupos[mesAno] = []
    grupos[mesAno].push(a)
  })
  return grupos
}Vista: Heatmap Semestral
O heatmap apresenta semanas como linhas (ou colunas, dependendo da implementação) e dias como células, colore cada célula segundo o número de avaliações desse dia. É orientado para identificar picos de carga.
Construção do semestre
O heatmap é gerado com base no período de um semestre académico
                definido por startMonth e endMonth. A
                implementação calcula todas as semanas entre as datas de início
                e fim e preenche dias nulos com placeholders.
Intensidade e buckets
A cor de cada célula é determinada por um valor normalizado: intensity = count / maxCount. O componente fornece utilitários para mapear essa intensidade
                para classes de CSS (ex: bg-chart-1/30).
function getHeatmapIntensity(count: number, max: number) {
  if (count === 0) return 'bg-muted'
  const intensity = count / max
  if (intensity <= 0.25) return 'bg-chart-1/30'
  if (intensity <= 0.5) return 'bg-chart-1/50'
  if (intensity <= 0.75) return 'bg-chart-1/70'
  return 'bg-chart-1'
}Para manter a legibilidade do heatmap em ecrãs pequenos, mostra apenas marcadores numéricos ao hover/tap e fornece a legenda de intensidades mais em baixo.
Dialog / Details
O pop-up de detalhe é invocado ao clicar num dia com avaliações.
                É implementado com o componente Dialog
e apresenta a lista de avaliações com os campos: disciplina, descrição
                e data formatada.
Requisitos de acessibilidade
- Foco inicial no título do pop-up.
- 
Fecho com Escapee botão explícito de fechar.
- 
Leitura linear por leitores de ecrã; elementos com role="dialog"earia-modal="true".
<Dialog v-model:open="dialogOpen">
  <DialogContent>
    <DialogHeader>
      <DialogTitle>{{ selectedDateFormatted }}</DialogTitle>
    </DialogHeader>
    <div v-if="avaliacoes.length">
      <div v-for="a in avaliacoes" :key="a.descricao" class="p-3 border rounded">
        <h4 class="font-semibold">{{ a.disciplina }}</h4>
        <p class="text-sm">{{ a.descricao }}</p>
        <p class="text-xs text-muted-foreground">{{ formatarData(a.data) }}</p>
      </div>
    </div>
  </DialogContent>
</Dialog>Utilitários e Helpers
O componente inclui vários helpers pequenas funções que encapsulam lógica de datas, formatação e mapear disciplinas para cores.
Principais funções
-  formatarData(dateStr)— formata para 'dd de mês de aaaa' em pt-PT.
-  formatarDataRelativa(dateStr)— devolve 'hoje', 'amanhã', 'há X dias', 'daqui a X semanas', etc.
-  getCorDisciplina(slug)— devolve uma classe CSS colorida com mapeamento estável por índice.
-  agruparPorMes(lista)— agrupa avaliações por label 'Mês Ano'.
// Exemplo: formatarData
function formatarData(dataStr: string) {
  const data = new Date(dataStr + 'T00:00:00')
  return data.toLocaleDateString('pt-PT', {
    day: '2-digit',
    month: 'long',
    year: 'numeric',
  })
}Estados e Lógica Interna
O componente Calendário é altamente reativo. Toda a
            sua arquitetura interna é baseada em
ref, computed e watch da Composition
            API. O estado principal é mínimo e derivável, permitindo uma renderização
            previsível e consistente, mesmo sob carga intensa (ex.: dezenas de disciplinas
            e centenas de avaliações).
- currentDate: data atual exibida (mês/ano corrente).
- viewMode: modo de visualização ativo — 'calendar', 'list' ou 'heatmap'.
- selectedSlugs: disciplinas atualmente visíveis no filtro.
- avaliacoesFiltradas: avaliações já filtradas, usadas nas vistas.
- avaliacoesMap: cache (Map) de avaliações por data.
- dialogState: estado reativo de abertura/fecho e data selecionada.
- config: configuração normalizada, combinando defaults com props.
Estrutura geral do script setup
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { parseISO, format, addMonths, subMonths } from 'date-fns'
import { pt } from 'date-fns/locale'
import type { Disciplina, Avaliacao, CalendarConfig } from './types'
const props = defineProps<CalendarProps>()
const emit = defineEmits(['day-click', 'month-change', 'filter-change', 'view-change'])
// Estado principal
const currentDate = ref(props.initialDate ? new Date(props.initialDate) : new Date())
const viewMode = ref(props.viewMode || 'calendar')
const selectedSlugs = ref<string[]>([])
const dialogOpen = ref(false)
const selectedDay = ref<string | null>(null)
// Computed: configuração normalizada
const config = computed<CalendarConfig>(() => ({
  firstWeekDay: props.config?.firstWeekDay ?? 1,
  semesterRange: props.config?.semesterRange ?? { startMonth: 8, endMonth: 1 },
  dateAdapter: props.config?.dateAdapter ?? 'date-fns',
  heatmapBucketCount: props.config?.heatmapBucketCount ?? 5,
  maxHeatmapWeeks: props.config?.maxHeatmapWeeks ?? 24,
}))
// Computed: disciplinas ativas
const disciplinasAtivas = computed(() => {
  if (!props.disciplinasData) return []
  if (selectedSlugs.value.length === 0) return props.disciplinasData
  return props.disciplinasData.filter((d) => selectedSlugs.value.includes(d.slug))
})
// Computed: avaliações filtradas
const avaliacoesFiltradas = computed<Avaliacao[]>(() => {
  return disciplinasAtivas.value.flatMap((d) => d.avaliacoes)
})
// Cache: mapa de avaliações por data (ISO)
const avaliacoesMap = computed(() => {
  const map = new Map<string, Avaliacao[]>()
  avaliacoesFiltradas.value.forEach((a) => {
    if (!map.has(a.data)) map.set(a.data, [])
    map.get(a.data)!.push(a)
  })
  return map
})
// Watchers
watch(viewMode, (v) => emit('view-change', v))
watch(selectedSlugs, (s) => emit('filter-change', s))
function goToNextMonth() {
  currentDate.value = addMonths(currentDate.value, 1)
  emit('month-change', currentDate.value.getFullYear(), currentDate.value.getMonth() + 1)
}
function goToPreviousMonth() {
  currentDate.value = subMonths(currentDate.value, 1)
  emit('month-change', currentDate.value.getFullYear(), currentDate.value.getMonth() + 1)
}
function onDayClick(dateStr: string) {
  const avaliacoes = avaliacoesMap.value.get(dateStr) || []
  emit('day-click', dateStr, avaliacoes)
  selectedDay.value = dateStr
  dialogOpen.value = true
}
</script>Gestão de estado derivado
Nenhum dado é duplicado. O estado derivado é obtido dinamicamente, minimizando inconsistências:
-  avaliacoesFiltradasdepende dedisciplinasAtivas.
-  avaliacoesMapdepende deavaliacoesFiltradas.
-  heatmapDatadepende deavaliacoesMapeconfig.
Esta hierarquia reativa assegura que o calendário nunca precisa
                de deep watch em listas complexas, todas as dependências
                são computadas a partir de fontes únicas, o que evita loops ou actualizações
                desnecessárias.
Decisões de Design
O desenho do componente foi orientado por princípios de consistência visual, acessibilidade, desempenho e manutenção. As decisões abaixo documentam os principais trade-offs técnicos e UX considerados.
1. Consistência visual
Foi seguida a linha de design dos componentes shadcn/ui, priorizando legibilidade e hierarquia visual: cores neutras, foco
            claro e interacções suaves. O calendário herda as variáveis do tema
            definido.
2. Acessibilidade (A11Y)
- 
Todos os elementos interativos possuem roleearia-label.
- Navegação por teclado: setas movem o foco entre dias, Enter ativa o diálogo.
- Dialog implementado com foco cíclico e retorno automático após fecho.
3. Desempenho
- 
Pré-cálculo de mapas de avaliações (Map<string, Avaliacao[]>) para lookup O(1).
- 
Uso intensivo de computedem vez de watchers manuais.
- Lazy rendering em heatmap e lista (v-if condicional fora da viewport).
4. UX e microinterações
A navegação mensal é suave: o calendário realiza uma transição animada entre meses, evitando o salto abrupto. As células de dia são clicáveis, mas também focáveis, garantindo que o utilizador pode navegar só com teclado.
5. Modularidade
Todas as vistas (CalendarView, ListView, HeatmapView) são componentes independentes. Isto permite manter responsabilidades isoladas e facilita futuras extensões (ex.: "WeekView" ou "AgendaView").
Evita inserir lógica de dados (fetch, transformação) diretamente
                na UI. O calendário deve receber dados prontos ou delegar o
                carregamento ao módulo de integração (ucs.json).
Integração com o Projeto
O componente está desenhado para integrar-se diretamente com os
            dados definidos em
ucs.json, um ficheiro que contém a estrutura de todas
            as unidades curriculares, incluindo avaliações, datas e metadados
            adicionais.
Estrutura do ficheiro ucs.json
[
  {
    "slug": "matematica-i",
    "nome": "Matemática I",
    "avaliacoes": [
      { "data": "2025-09-24", "descricao": "Teste 1" },
      { "data": "2025-11-02", "descricao": "Exame final" }
    ]
  },
  {
    "slug": "fisica-i",
    "nome": "Física I",
    "avaliacoes": [
      { "data": "2025-10-13", "descricao": "Mini-teste" }
    ]
  }
]
O formato é simples, mas consistente. Cada disciplina é
                identificada por um
slug único, usado internamente no calendário para gerar
                cores e filtros estáveis.
Importação e validação
O ficheiro é importado diretamente via módulo estático, o que permite bundling e tipagem. Contudo, pode também ser carregado dinamicamente (fetch). O calendário valida a estrutura básica dos dados antes de processá-los.
import ucsData from '@/data/ucs.json'
function validarUCS(data: any): data is Disciplina[] {
  if (!Array.isArray(data)) return false
  return data.every((uc) => uc.slug && uc.nome && Array.isArray(uc.avaliacoes))
}
if (!validarUCS(ucsData)) {
  console.error('Estrutura de ucs.json inválida')
} else {
  console.log('Dados validados:', ucsData.length, 'disciplinas')
}Não é necessário validar profundamente cada campo, mas garantir que os campos estruturais principais estão presentes evita erros de parsing e datas inválidas.
Exemplo de integração direta
<template>
  <div class="p-6">
    <Calendar :disciplinasData="ucsData" @day-click="abrirDetalhes" />
  </div>
</template>
<script setup lang="ts">
import CalendarComponent from "@/components/Calendar.vue";
import ucsData from '@/data/ucs.json'
function abrirDetalhes(date, avaliacoes) {
  console.log('Avaliações nesse dia:', date, avaliacoes)
}
</script>
Este exemplo mostra a integração típica no projeto, o componente
                é reativo e funciona imediatamente ao receber a estrutura ucs.json.
Boas Práticas e Recomendações
O componente foi projetado para ser reutilizável, extensível e fácil de manter. As boas práticas abaixo ajudam a garantir consistência e longevidade no projeto.
1. Tipagem e Segurança
Define sempre os tipos Disciplina, Avaliacao e CalendarConfig explicitamente. Isto evita erros silenciosos
            e melhora a autocompletação no editor.
2. Comunicação de Eventos
Usa defineEmits com eventos semanticamente claros. Evita
            enviar objetos excessivamente aninhados ou dependentes de estado interno.
3. Acessibilidade
- Evita dependências de mouse-only, testa com teclado.
- Usa labels descritivos e feedback auditivo (screen readers).
- Verifica contraste de cores e tamanhos de fonte.
4. Desempenho e Lazy Loading
Carrega apenas a vista necessária, v-if sobre o modo ativo.
            O heatmap e lista não devem ser montados até o utilizador os ativar.
5. Persistência e Preferências
Podes armazenar as preferências de filtro e modo de visualização no localStorage (embora para já não seja implementado nativamente):
watch(viewMode, (v) => localStorage.setItem('calendar-view', v))
onMounted(() => {
  const saved = localStorage.getItem('calendar-view')
  if (saved) viewMode.value = saved
})
À medida que o número de disciplinas cresce, o calendário
                mantém-se estável porque usa computed e Map para operações de O(1). O heatmap, em particular, escala para mais
                de 500 avaliações sem perda de fluidez perceptível.
7. Deploy e Internacionalização
Usa date-fns com locale configurável. Para novos idiomas,
            basta importar e ajustar o locale:
import { es } from 'date-fns/locale'
const config = { ...defaultConfig, locale: es }
Todos os subcomponentes do calendário aceitam class
e style props para override. Assim, é possível adaptá-los
                ao tema da instituição sem alterar a lógica base.
Extensões Futuras / Roadmap
O Calendário foi concebido como base sólida de visualização e gestão de avaliações. No entanto, existem diversas áreas de expansão já mapeadas para versões futuras, alinhadas com a evolução do ecossistema Vue e das necessidades.
1. Vistas adicionais
- WeekView: visão semanal compacta, ideal para planeamento detalhado.
- AgendaView: listagem contínua de avaliações futuras, agrupadas por disciplina.
- TimelineView: linha temporal horizontal com marcações por tipo de avaliação.
2. Sincronização externa
- 
Exportação para iCalendar (.ics),Google Calendare sincronização bidirecional.
- Integração com APIs institucionais (ex.: SIGARRA, Moodle, Google Classroom).
- Webhook para notificações automáticas de alterações de datas.
3. Colaboração
Permitir que professores e estudantes adicionem anotações ou lembretes pessoais diretamente no calendário, sem afetar os dados oficiais.
4. Inteligência Adaptativa
Planeia-se a introdução de um módulo preditivo, capaz de sugerir datas de estudo ideais com base na densidade de avaliações e desempenho histórico do aluno.
5. Extensão para dispositivos móveis
- Gestos de swipe para navegar entre meses.
- Modo compacto adaptativo para ecrãs pequenos.
- Cache local e modo offline (PWA-ready).
6. Observabilidade e Telemetria
Implementação futura de métricas de uso anónimas, recolhendo eventos como mudança de vista, número médio de avaliações carregadas e frequência de acessos. Estas métricas ajudarão a priorizar otimizações reais, baseadas em uso.
Cada extensão relacionada deve ser lançada sob um sistema de
                versionamento semântico:
major.minor.patch. As alterações de API pública
                futuramente serão documentadas em changelogs no diretório docs/changelogs/.
7. Interoperabilidade futura
Uma das metas de médio prazo é converter o núcleo do calendário num
componente web independente (<academic-calendar>), permitindo a integração com projetos não-Vue, como Astro puro,
            React ou Svelte.
Versão do documento: 1.0.0 • Última atualização: Outubro 2025 — Infoloom