feat(batch-1): multi-role sidebar, video display, certificate layout, upload progress, counselor access

- roleMenuItems.js: merged menu builder that unions items from all user roles
  (student + missionary, student + counselor, etc.) additively
- auth store: expose `roles` array in addition to singular `role`; wire in
  WordPress auth-status cookie sync so the public site can show/hide the
  account button
- router guards: use roles.includes() instead of role === for multi-role access
- layouts (Student/Counselor/Missionary): delegate menuItems to
  buildMergedMenuItems so dual-role users see sections from both roles
- StudentSessionDetailsPage: contentKind now returns 'video' (not 'link')
  when session.videoUrl is set; adds embed iframe branch for online sessions
- CertificateItem.vue: fix CSS order/alignment for desktop + mobile layout
- StudentCertificatesPage.vue: map backend payload fields to component props
  with Jalaali date formatting
- SessionFormPage: upload progress bar (%) during video uploads; embed_code
  textarea for online sessions (frontend-only; dormant until batch-2 backend
  migration is deployed)
- wp-sync/auth-status-cookie.js: cookie helper for WordPress login-state sync

[batch-1-low-risk]
This commit is contained in:
Saghar Mohammadi
2026-09-02 11:17:20 +04:30
parent 81332abdb0
commit 9f86acacb0
12 changed files with 349 additions and 105 deletions
@@ -37,12 +37,23 @@
<SvgIcon name="warning" :size="22" color="#b8b8b8" class="ssd__text-icon" />
<p class="ssd__text-body">{{ session.text }}</p>
</div>
<div v-else-if="contentKind === 'embed'" class="ssd__embed">
<iframe
class="ssd__embed-frame"
:srcdoc="session.embedCode"
sandbox="allow-scripts allow-popups allow-forms allow-presentation"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen
loading="lazy"
referrerpolicy="no-referrer"
/>
</div>
<div v-else-if="contentKind === 'link'" class="ssd__link">
<div class="ssd__link__box">
<span>https://link</span>
<span>{{ session?.link || 'لینکی ثبت نشده است' }}</span>
<SvgIcon name="link" :size="22" class="ssd__link-icon" />
</div>
<BaseButton text="ورود به جلسه " custom-class="ssd__link-btn">
<BaseButton text="ورود به جلسه " custom-class="ssd__link-btn" @click="openSessionLink">
<template #appendIcon>
<SvgIcon name="arrow-left" :size="18" color="#fff" />
</template>
@@ -129,13 +140,17 @@ const homework = computed(() => (homeworksData.value?.data ?? [])[0] ?? null)
const contentKind = computed(() => {
const s = session.value
if (!s) return ''
if (s.type === 'online') return 'link'
if (s.videoUrl) return 'link'
if (s.type === 'online') return s.embedCode?.trim() ? 'embed' : 'link'
if (s.videoUrl) return 'video'
if (s.audioUrl) return 'audio'
if (s.text) return 'text'
return ''
})
const openSessionLink = () => {
if (session.value?.link) window.open(session.value.link, '_blank', 'noopener,noreferrer')
}
const homeworkAudio = ref(null)
const homeworkImage = ref(null)
const errors = reactive({ audio: '', image: '' })
@@ -260,6 +275,18 @@ const onSubmit = async () => {
}
}
&__embed {
width: 100%;
}
&__embed-frame {
width: 100%;
aspect-ratio: 16 / 9;
border: 0;
border-radius: 1rem;
background: #000;
}
&__link {
display: flex;
align-items: center;