Skip to main content

Components

Overview

Smart Academic Hub uses a component-based architecture with reusable UI components.

Component Structure

components/
├── common/ # Generic reusable components
│ ├── BaseButton.vue
│ ├── BaseInput.vue
│ ├── BaseModal.vue
│ └── ...
├── layout/ # Layout components
│ ├── Header.vue
│ ├── Sidebar.vue
│ └── Footer.vue
├── students/ # Student-specific components
├── courses/ # Course-specific components
└── ...

Base Components

BaseButton

<template>
<button
:type="type"
:class="buttonClasses"
:disabled="disabled || loading"
>
<slot />
</button>
</template>

<script setup lang="ts">
interface Props {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
type?: 'button' | 'submit';
disabled?: boolean;
loading?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md',
type: 'button',
});
</script>

BaseInput

<template>
<div class="form-group">
<label v-if="label" :for="id">{{ label }}</label>
<input
:id="id"
:type="type"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder"
:disabled="disabled"
/>
<span v-if="error" class="error">{{ error }}</span>
</div>
</template>

<script setup lang="ts">
interface Props {
modelValue: string | number;
type?: string;
label?: string;
placeholder?: string;
disabled?: boolean;
error?: string;
id?: string;
}

defineProps<Props>();
defineEmits<{
'update:modelValue': [value: string];
}>();
</script>

Composition API

Use Composition API for component logic:

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { useStudentStore } from '@/stores/students';

const studentStore = useStudentStore();
const searchQuery = ref('');

const filteredStudents = computed(() => {
return studentStore.students.filter(student =>
student.name.toLowerCase().includes(searchQuery.value.toLowerCase())
);
});

onMounted(() => {
studentStore.fetchStudents();
});
</script>

Props and Emits

Define props and emits with TypeScript:

<script setup lang="ts">
interface Props {
student: Student;
editable?: boolean;
}

interface Emits {
(e: 'update', student: Student): void;
(e: 'delete', id: number): void;
}

const props = defineProps<Props>();
const emit = defineEmits<Emits>();
</script>

Slots

Use slots for flexible content:

<template>
<div class="card">
<div class="card-header">
<slot name="header" />
</div>
<div class="card-body">
<slot />
</div>
<div class="card-footer">
<slot name="footer" />
</div>
</div>
</template>

Best Practices

  1. Keep components small and focused
  2. Use TypeScript for props and emits
  3. Extract reusable logic to composables
  4. Use computed properties for derived state
  5. Emit events for parent communication
  6. Use slots for flexible content
  7. Follow Vue 3 Composition API patterns