Files
gameno-front/src/views/account/MyClassesView.vue
T
kavehhn a9135bfa09 Initial commit: public frontend for GameNo.
Vue 3 + Vite student-facing app with routing, Pinia state, and API integration.
2026-08-09 04:18:09 +02:00

159 lines
3.7 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="classes-view">
<header class="page-head">
<h1>کلاسهای من</h1>
<p>کلاسهایی که در آنها ثبتنام شدهاید.</p>
</header>
<div v-if="loading" class="loading-block">
<div class="spinner" aria-label="در حال بارگذاری"></div>
</div>
<div v-else-if="error" class="empty-state">
<h3>بارگذاری ناموفق بود</h3>
<p>{{ error }}</p>
<button type="button" class="btn btn-ink" @click="load">تلاش دوباره</button>
</div>
<div v-else-if="!classes.length" class="empty-state">
<h3>هنوز کلاسی ندارید</h3>
<p>پس از ثبتنام در کلاس توسط آموزشگاه، اینجا نمایش داده میشود.</p>
<RouterLink to="/#courses" class="btn btn-outline">مشاهده دورهها</RouterLink>
</div>
<div v-else class="class-list">
<article v-for="item in classes" :key="item._id" class="class-card">
<div class="class-top">
<h2>{{ item.name }}</h2>
<span class="badge" :class="item.isActive ? '' : 'badge-muted'">
{{ item.isActive ? 'فعال' : 'غیرفعال' }}
</span>
</div>
<p class="course-title">{{ item.course?.title || 'بدون دوره' }}</p>
<dl class="meta-grid">
<div>
<dt>استاد</dt>
<dd>
{{
item.professor
? `${item.professor.name || ''} ${item.professor.surname || ''}`.trim()
: '—'
}}
</dd>
</div>
<div>
<dt>شروع</dt>
<dd>{{ formatJalali(item.startDate) }}</dd>
</div>
<div>
<dt>پایان</dt>
<dd>{{ formatJalali(item.endDate) }}</dd>
</div>
<div>
<dt>شهریه</dt>
<dd>{{ formatPrice(item.tuitionFee) }}</dd>
</div>
</dl>
</article>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { RouterLink } from 'vue-router'
import { classesApi } from '@/api/classesApi'
import { formatJalali, formatPrice, getApiErrorMessage } from '@/utils/format'
const classes = ref([])
const loading = ref(true)
const error = ref('')
async function load() {
loading.value = true
error.value = ''
try {
const res = await classesApi.getMyClasses({ limit: 50 })
classes.value = res.data || []
} catch (err) {
error.value = getApiErrorMessage(err)
classes.value = []
} finally {
loading.value = false
}
}
onMounted(load)
</script>
<style scoped>
.page-head {
margin-bottom: 1.5rem;
}
.page-head h1 {
font-family: var(--font-display);
font-size: clamp(1.9rem, 3.5vw, 2.4rem);
margin-bottom: 0.3rem;
}
.page-head p {
color: var(--ink-soft);
}
.class-list {
display: grid;
gap: 1rem;
}
.class-card {
padding: 1.35rem 1.4rem;
border-radius: 1.2rem;
background: var(--chalk);
border: 1px solid rgba(20, 53, 43, 0.08);
box-shadow: 0 10px 28px rgba(20, 53, 43, 0.05);
}
.class-top {
display: flex;
justify-content: space-between;
gap: 0.75rem;
align-items: flex-start;
margin-bottom: 0.35rem;
}
.class-top h2 {
font-size: 1.2rem;
}
.course-title {
color: var(--leaf);
font-weight: 700;
margin-bottom: 1rem;
}
.meta-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 0.85rem;
margin: 0;
}
.meta-grid dt {
font-size: 0.75rem;
color: var(--ink-soft);
font-weight: 700;
}
.meta-grid dd {
margin: 0.15rem 0 0;
font-weight: 600;
}
@media (max-width: 760px) {
.meta-grid {
grid-template-columns: 1fr 1fr;
}
}
</style>