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
- Keep components small and focused
- Use TypeScript for props and emits
- Extract reusable logic to composables
- Use computed properties for derived state
- Emit events for parent communication
- Use slots for flexible content
- Follow Vue 3 Composition API patterns