Replace active courses with upcoming classes and improve class card UI.

Split the landing page into upcoming and active class sections with colored ClassCard components, and refresh navigation and account class cards to match.
This commit is contained in:
2026-08-16 08:24:01 +03:30
parent f1a7ad8bf8
commit 06d091156c
5 changed files with 600 additions and 271 deletions
+140 -49
View File
@@ -18,50 +18,65 @@
<div v-else-if="!classes.length" class="empty-state">
<h3>هنوز کلاسی ندارید</h3>
<p>پس از ثبتنام در کلاس توسط آموزشگاه، اینجا نمایش داده میشود.</p>
<RouterLink to="/#courses" class="btn btn-outline">مشاهده دورهها</RouterLink>
<RouterLink to="/#upcoming-classes" 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>
<article
v-for="(item, index) in classes"
:key="item._id"
class="class-card"
:class="[
item.isActive ? 'class-card--active' : 'class-card--inactive',
`class-card--tone-${index % 3}`
]"
>
<div class="class-card-accent" aria-hidden="true"></div>
<div class="class-card-body">
<div class="class-top">
<div>
<span class="course-tag">{{ item.course?.title || 'بدون دوره' }}</span>
<h2>{{ item.name }}</h2>
</div>
<span class="status-badge" :class="item.isActive ? 'status-active' : 'status-muted'">
{{ item.isActive ? 'فعال' : 'غیرفعال' }}
</span>
</div>
<dl class="meta-grid">
<div class="meta-item">
<dt>استاد</dt>
<dd>
{{
item.professor
? `${item.professor.name || ''} ${item.professor.surname || ''}`.trim()
: '—'
}}
</dd>
</div>
<div class="meta-item">
<dt>شروع</dt>
<dd>{{ formatJalali(item.startDate) }}</dd>
</div>
<div class="meta-item">
<dt>پایان</dt>
<dd>{{ formatJalali(item.endDate) }}</dd>
</div>
<div class="meta-item">
<dt>روزها</dt>
<dd>{{ formatClassDays(item.days) }}</dd>
</div>
<div class="meta-item">
<dt>ساعت</dt>
<dd>{{ formatClassTime(item.startTime, item.endTime) }}</dd>
</div>
<div class="meta-item meta-item--price">
<dt>شهریه</dt>
<dd>{{ formatPrice(item.finalTuitionFee ?? item.tuitionFee) }}</dd>
</div>
</dl>
</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>{{ formatClassDays(item.days) }}</dd>
</div>
<div>
<dt>ساعت</dt>
<dd>{{ formatClassTime(item.startTime, item.endTime) }}</dd>
</div>
<div>
<dt>شهریه</dt>
<dd>{{ formatPrice(item.tuitionFee) }}</dd>
</div>
</dl>
</article>
</div>
</div>
@@ -144,15 +159,53 @@ onMounted(load)
.class-list {
display: grid;
gap: 1rem;
gap: 1.15rem;
}
.class-card {
padding: 1.35rem 1.4rem;
border-radius: 1.2rem;
border-radius: 1.25rem;
overflow: hidden;
background: var(--chalk);
border: 1px solid rgba(20, 53, 43, 0.08);
box-shadow: 0 10px 28px rgba(20, 53, 43, 0.05);
transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.class-card:hover {
transform: translateY(-3px);
box-shadow: var(--shadow);
}
.class-card-accent {
height: 4px;
}
.class-card--active.class-card--tone-0 .class-card-accent {
background: linear-gradient(90deg, var(--leaf), var(--leaf-bright));
}
.class-card--active.class-card--tone-1 .class-card-accent {
background: linear-gradient(90deg, #2a6b5a, #3d8b78);
}
.class-card--active.class-card--tone-2 .class-card-accent {
background: linear-gradient(90deg, #4a7c59, #6b9f7a);
}
.class-card--inactive .class-card-accent {
background: linear-gradient(90deg, #9aa8a0, #b8cfc0);
}
.class-card--active.class-card--tone-0 {
background: linear-gradient(165deg, #eef7f1 0%, var(--chalk) 40%);
}
.class-card--active.class-card--tone-1 {
background: linear-gradient(165deg, #eaf4ef 0%, var(--chalk) 40%);
}
.class-card--active.class-card--tone-2 {
background: linear-gradient(165deg, #e8f2eb 0%, var(--chalk) 40%);
}
.class-card-body {
padding: 1.25rem 1.35rem 1.35rem;
}
.class-top {
@@ -160,6 +213,17 @@ onMounted(load)
justify-content: space-between;
gap: 0.75rem;
align-items: flex-start;
margin-bottom: 1rem;
}
.course-tag {
display: inline-block;
font-size: 0.75rem;
font-weight: 700;
color: var(--ink-soft);
padding: 0.18rem 0.55rem;
border-radius: 999px;
background: rgba(20, 53, 43, 0.06);
margin-bottom: 0.35rem;
}
@@ -167,28 +231,55 @@ onMounted(load)
font-size: 1.2rem;
}
.course-title {
.status-badge {
font-size: 0.72rem;
font-weight: 800;
padding: 0.25rem 0.65rem;
border-radius: 999px;
white-space: nowrap;
}
.status-active {
background: rgba(45, 106, 79, 0.16);
color: var(--leaf);
font-weight: 700;
margin-bottom: 1rem;
}
.status-muted {
background: rgba(20, 53, 43, 0.08);
color: var(--ink-soft);
}
.meta-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0.85rem;
gap: 0.75rem;
margin: 0;
}
.meta-item {
padding: 0.65rem 0.75rem;
border-radius: 0.75rem;
background: rgba(20, 53, 43, 0.04);
}
.meta-item--price {
background: rgba(45, 106, 79, 0.1);
}
.meta-item--price dd {
color: var(--leaf);
}
.meta-grid dt {
font-size: 0.75rem;
font-size: 0.72rem;
color: var(--ink-soft);
font-weight: 700;
}
.meta-grid dd {
margin: 0.15rem 0 0;
margin: 0.2rem 0 0;
font-weight: 600;
font-size: 0.92rem;
}
@media (max-width: 760px) {