@@ -0,0 +1,7 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.625 10.625H9.375"/>
|
||||
<path d="M5.625 8.125H9.375"/>
|
||||
<path d="M15.3125 13.125C17.211 13.125 18.75 11.586 18.75 9.6875C18.75 7.78902 17.211 6.25 15.3125 6.25C13.414 6.25 11.875 7.78902 11.875 9.6875C11.875 11.586 13.414 13.125 15.3125 13.125Z"/>
|
||||
<path d="M13.125 15H3.125C2.95924 15 2.80027 14.9342 2.68306 14.8169C2.56585 14.6997 2.5 14.5408 2.5 14.375V4.375C2.5 4.20924 2.56585 4.05027 2.68306 3.93306C2.80027 3.81585 2.95924 3.75 3.125 3.75H16.875C17.0408 3.75 17.1997 3.81585 17.3169 3.93306C17.4342 4.05027 17.5 4.20924 17.5 4.375V7.03594"/>
|
||||
<path d="M13.125 12.3389V17.4998L15.3125 16.2498L17.5 17.4998V12.3389"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 788 B |
@@ -0,0 +1,5 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 8.75H12.8125"/>
|
||||
<path d="M7.5 11.25H12.8125"/>
|
||||
<path d="M10.3125 16.875H3.75C3.58424 16.875 3.42527 16.8092 3.30806 16.6919C3.19085 16.5747 3.125 16.4158 3.125 16.25V9.6875C3.125 7.78126 3.88225 5.95309 5.23017 4.60517C6.57809 3.25725 8.40626 2.5 10.3125 2.5C11.2564 2.5 12.191 2.68591 13.063 3.04712C13.9351 3.40832 14.7274 3.93775 15.3948 4.60517C16.0623 5.27259 16.5917 6.06493 16.9529 6.93696C17.3141 7.80899 17.5 8.74362 17.5 9.6875C17.5 10.6314 17.3141 11.566 16.9529 12.438C16.5917 13.3101 16.0623 14.1024 15.3948 14.7698C14.7274 15.4373 13.9351 15.9667 13.063 16.3279C12.191 16.6891 11.2564 16.875 10.3125 16.875Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 794 B |
@@ -0,0 +1,11 @@
|
||||
<svg viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_24766)">
|
||||
<path d="M4.64062 7.17188L5.90625 8.4375L8.85938 5.48438" stroke="#B8B8B8" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.75 11.8125C9.54594 11.8125 11.8125 9.54594 11.8125 6.75C11.8125 3.95406 9.54594 1.6875 6.75 1.6875C3.95406 1.6875 1.6875 3.95406 1.6875 6.75C1.6875 9.54594 3.95406 11.8125 6.75 11.8125Z" stroke="#B8B8B8" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_24766">
|
||||
<rect width="13.5" height="13.5" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 655 B |
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.5 7.5V15.625C17.5 15.7908 17.4342 15.9497 17.3169 16.0669C17.1997 16.1842 17.0408 16.25 16.875 16.25H3.125C2.95924 16.25 2.80027 16.1842 2.68306 16.0669C2.56585 15.9497 2.5 15.7908 2.5 15.625V7.5L10 2.5L17.5 7.5Z"/>
|
||||
<path d="M8.63711 11.875L2.69336 16.0734"/>
|
||||
<path d="M17.307 16.0734L11.3633 11.875"/>
|
||||
<path d="M17.5 7.5L11.3641 11.875H8.63672L2.5 7.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 526 B |
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.375 16.25H3.125C2.95924 16.25 2.80027 16.1842 2.68306 16.0669C2.56585 15.9497 2.5 15.7908 2.5 15.625V5C2.5 4.83424 2.56585 4.67527 2.68306 4.55806C2.80027 4.44085 2.95924 4.375 3.125 4.375H7.29141C7.42664 4.375 7.55822 4.41886 7.66641 4.5L10 6.25H16.875C17.0408 6.25 17.1997 6.31585 17.3169 6.43306C17.4342 6.55027 17.5 6.70924 17.5 6.875V9.375"/>
|
||||
<path d="M14.375 15.5109L16.693 16.875L16.0625 14.3289L18.125 12.6258L15.418 12.4172L14.375 10L13.332 12.4172L10.625 12.6258L12.6875 14.3289L12.057 16.875L14.375 15.5109Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 687 B |
@@ -0,0 +1,5 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.625 7.5L10 2.5L19.375 7.5L10 12.5L0.625 7.5Z"/>
|
||||
<path d="M10 7.5L14.375 9.83359V18.75"/>
|
||||
<path d="M16.875 8.83398V12.9918C16.8754 13.1445 16.8198 13.2921 16.7188 13.4066C15.8359 14.3902 13.6797 16.2504 10 16.2504C6.32031 16.2504 4.16563 14.3902 3.28125 13.4066C3.18023 13.2921 3.12465 13.1445 3.125 12.9918V8.83398"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 485 B |
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 171 171" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M85.125 148.969C120.385 148.969 148.969 120.385 148.969 85.125C148.969 49.8651 120.385 21.2812 85.125 21.2812C49.8651 21.2812 21.2812 49.8651 21.2812 85.125C21.2812 120.385 49.8651 148.969 85.125 148.969Z" stroke="#CACACA" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M61.1816 65.2207C64.8287 65.2207 67.7852 68.1772 67.7852 71.8242C67.7852 75.4712 64.8287 78.4277 61.1816 78.4277C57.5346 78.4277 54.5781 75.4712 54.5781 71.8242C54.5781 68.1772 57.5346 65.2207 61.1816 65.2207Z" fill="#F55D6D" stroke="#F55D6D" stroke-width="0.09375"/>
|
||||
<path d="M109.064 65.2207C112.711 65.2207 115.668 68.1772 115.668 71.8242C115.668 75.4712 112.711 78.4277 109.064 78.4277C105.417 78.4277 102.461 75.4712 102.461 71.8242C102.461 68.1772 105.417 65.2207 109.064 65.2207Z" fill="#F55D6D" stroke="#F55D6D" stroke-width="0.09375"/>
|
||||
<path d="M111.727 117.047C106.207 107.504 96.9427 101.086 85.125 101.086C73.3073 101.086 64.0433 107.504 58.5234 117.047" stroke="#F55D6D" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12.5C11.7259 12.5 13.125 11.1009 13.125 9.375C13.125 7.64911 11.7259 6.25 10 6.25C8.27411 6.25 6.875 7.64911 6.875 9.375C6.875 11.1009 8.27411 12.5 10 12.5Z"/>
|
||||
<path d="M4.98438 15.5757C5.45462 14.6494 6.17216 13.8715 7.05745 13.3281C7.94275 12.7847 8.96123 12.4971 10 12.4971C11.0388 12.4971 12.0572 12.7847 12.9425 13.3281C13.8278 13.8715 14.5454 14.6494 15.0156 15.5757"/>
|
||||
<path d="M17.3963 8.75003C17.6599 10.3167 17.4189 11.9266 16.7079 13.3474C15.9969 14.7681 14.8528 15.9261 13.4408 16.6542C12.0287 17.3823 10.4218 17.6428 8.85205 17.3981C7.28231 17.1533 5.83096 16.416 4.70757 15.2926C3.58419 14.1693 2.84689 12.7179 2.60215 11.1482C2.35742 9.57842 2.6179 7.97151 3.34598 6.55946C4.07407 5.14741 5.23209 4.00329 6.65283 3.2923C8.07357 2.58132 9.6835 2.34026 11.2502 2.60394"/>
|
||||
<path d="M14.375 4.375L15.625 5.625L18.125 3.125"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1007 B |
@@ -1,13 +1,6 @@
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_21209)">
|
||||
<path d="M5.48438 8.53125C7.16712 8.53125 8.53125 7.16712 8.53125 5.48438C8.53125 3.80163 7.16712 2.4375 5.48438 2.4375C3.80163 2.4375 2.4375 3.80163 2.4375 5.48438C2.4375 7.16712 3.80163 8.53125 5.48438 8.53125Z" stroke="#B8B8B8" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M1.21875 10.5625C2.2623 9.3209 3.73547 8.53125 5.48438 8.53125C7.23328 8.53125 8.70645 9.3209 9.75 10.5625" stroke="#B8B8B8" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.1562 3.49096C10.4243 4.12144 10.5625 4.79952 10.5625 5.48463C10.5625 6.16975 10.4243 6.84782 10.1562 7.47831" stroke="#B8B8B8" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.6468 2.84375C12.0033 3.67846 12.1871 4.57672 12.1871 5.48438C12.1871 6.39203 12.0033 7.29029 11.6468 8.125" stroke="#B8B8B8" stroke-width="0.75" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_21209">
|
||||
<rect width="12.75" height="12.75" fill="white" transform="scale(1.01961)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<svg viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.48438 8.53125C7.16712 8.53125 8.53125 7.16712 8.53125 5.48438C8.53125 3.80163 7.16712 2.4375 5.48438 2.4375C3.80163 2.4375 2.4375 3.80163 2.4375 5.48438C2.4375 7.16712 3.80163 8.53125 5.48438 8.53125Z" stroke-width="0.75"/>
|
||||
<path d="M1.21875 10.5625C2.2623 9.3209 3.73547 8.53125 5.48438 8.53125C7.23328 8.53125 8.70645 9.3209 9.75 10.5625" stroke-width="0.75"/>
|
||||
<path d="M10.1562 3.49096C10.4243 4.12144 10.5625 4.79952 10.5625 5.48463C10.5625 6.16975 10.4243 6.84782 10.1562 7.47831" stroke-width="0.75"/>
|
||||
<path d="M11.6468 2.84375C12.0033 3.67846 12.1871 4.57672 12.1871 5.48438C12.1871 6.39203 12.0033 7.29029 11.6468 8.125" stroke-width="0.75"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 820 B |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 400 B |
@@ -39,8 +39,9 @@ const social = [
|
||||
.copyright {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 3rem;
|
||||
|
||||
&__text {
|
||||
padding: 0 0.25rem;
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<Transition name="dropdown">
|
||||
<div v-if="modelValue" ref="floatingEl" class="dropdown-menu" :style="floatingStyle">
|
||||
<div
|
||||
v-if="modelValue"
|
||||
ref="floatingEl"
|
||||
class="dropdown-menu"
|
||||
:class="{ 'dropdown-menu--center': align === 'center' }"
|
||||
:style="floatingStyle"
|
||||
>
|
||||
<ul class="dropdown-menu__list">
|
||||
<li
|
||||
v-for="(item, i) in items"
|
||||
@@ -11,11 +17,21 @@
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-menu__item"
|
||||
:class="{ 'dropdown-menu__item--danger': item.danger }"
|
||||
:class="{
|
||||
'dropdown-menu__item--danger': item.danger,
|
||||
'dropdown-menu__item--colored': item.color,
|
||||
}"
|
||||
:style="{ color: item.color }"
|
||||
:disabled="item.disabled"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<SvgIcon v-if="item.icon" :name="item.icon" :size="16" class="dropdown-menu__icon" />
|
||||
<SvgIcon
|
||||
v-if="item.icon"
|
||||
:name="item.icon"
|
||||
:size="16"
|
||||
:color="item.iconColor ?? item.color"
|
||||
class="dropdown-menu__icon"
|
||||
/>
|
||||
<span class="dropdown-menu__label">{{ item.label }}</span>
|
||||
</button>
|
||||
</li>
|
||||
@@ -43,6 +59,7 @@ const props = defineProps({
|
||||
placement: { type: String, default: 'bottom-end' },
|
||||
offset: { type: Number, default: 8 },
|
||||
minWidth: { type: String, default: '12rem' },
|
||||
align: { type: String, default: 'start' },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'select'])
|
||||
@@ -180,6 +197,21 @@ onBeforeUnmount(unmountFloating)
|
||||
&__icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__item--colored &__icon {
|
||||
:deep([stroke]) {
|
||||
stroke: currentcolor;
|
||||
}
|
||||
}
|
||||
|
||||
&--center &__item {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&--center &__label {
|
||||
flex: 0 1 auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-enter-active,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<ul>
|
||||
<li v-for="item in iconList" :key="item.icon">
|
||||
<button type="button" class="header-block__icon-btn" @click="item.action">
|
||||
<SvgIcon :name="item.icon" :size="24" color="#9a9a9a" />
|
||||
<SvgIcon :name="item.icon" :size="24" color="var(--header-icon-color)" />
|
||||
<span v-if="item.count" class="header-block__icon-badge">{{ item.count }}</span>
|
||||
</button>
|
||||
</li>
|
||||
@@ -30,14 +30,27 @@
|
||||
<div v-if="!hasSlotContent && pageTitle">{{ pageTitle }}</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<DropdownMenu
|
||||
v-model="userMenuOpen"
|
||||
:reference="userMenuTrigger"
|
||||
:items="userMenuItems"
|
||||
placement="bottom-end"
|
||||
align="center"
|
||||
min-width="9rem"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useUIStore } from '@/store/ui'
|
||||
import { computed, useSlots } from 'vue'
|
||||
import { gallery } from '@/utils/gallery'
|
||||
import useAuth from '@/composables/useAuth'
|
||||
import { computed, ref, useSlots } from 'vue'
|
||||
import SvgIcon from '@/components/icons/SvgIcon.vue'
|
||||
import DropdownMenu from '@/components/DropdownMenu.vue'
|
||||
import { useLogoutMutation } from '@/services/query/auth'
|
||||
|
||||
defineProps({
|
||||
pageTitle: { type: String, default: '' },
|
||||
@@ -47,13 +60,55 @@ const slots = useSlots()
|
||||
const hasSlotContent = computed(() => !!slots.default)
|
||||
|
||||
const ui = useUIStore()
|
||||
const router = useRouter()
|
||||
const { clearSession } = useAuth()
|
||||
const logoutMutation = useLogoutMutation()
|
||||
|
||||
const logo = gallery.authLogo
|
||||
|
||||
const userMenuOpen = ref(false)
|
||||
const userMenuTrigger = ref(null)
|
||||
|
||||
const openUserMenu = (event) => {
|
||||
const trigger = event.currentTarget
|
||||
if (userMenuOpen.value && userMenuTrigger.value === trigger) {
|
||||
userMenuOpen.value = false
|
||||
return
|
||||
}
|
||||
userMenuTrigger.value = trigger
|
||||
userMenuOpen.value = true
|
||||
}
|
||||
|
||||
const goEditProfile = () => {
|
||||
router.push({ name: 'student-profile' }).catch(() => {})
|
||||
}
|
||||
|
||||
const onLogout = async () => {
|
||||
try {
|
||||
await logoutMutation.mutateAsync()
|
||||
} catch {
|
||||
/* ignore */
|
||||
} finally {
|
||||
clearSession()
|
||||
router.push({ name: 'login' })
|
||||
}
|
||||
}
|
||||
|
||||
const userMenuItems = [
|
||||
{ key: 'edit-profile', label: 'ویرایش پروفایل', icon: 'pencil', onClick: goEditProfile },
|
||||
{
|
||||
key: 'logout',
|
||||
label: 'خروج',
|
||||
icon: 'heart',
|
||||
color: 'var(--color-primary)',
|
||||
onClick: onLogout,
|
||||
},
|
||||
]
|
||||
|
||||
const iconList = [
|
||||
{ icon: 'chat', action: () => {} },
|
||||
{ icon: 'bell', action: () => {}, count: 5 },
|
||||
{ icon: 'user', action: () => {} },
|
||||
// { icon: 'chat', action: () => {} },
|
||||
// { icon: 'bell', action: () => {}, count: 5 },
|
||||
{ icon: 'user', action: openUserMenu },
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -136,12 +191,18 @@ const iconList = [
|
||||
}
|
||||
|
||||
&__icon-btn {
|
||||
--header-icon-color: #9a9a9a;
|
||||
|
||||
position: relative;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 9999px;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
--header-icon-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&__icon-badge {
|
||||
|
||||
@@ -25,8 +25,8 @@ defineProps({
|
||||
|
||||
&__bar {
|
||||
background-color: var(--color-primary);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 5%);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 3px 8px rgba(243, 102, 117, 45%);
|
||||
height: 1.9rem;
|
||||
width: 0.375rem;
|
||||
flex-shrink: 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="no-items">
|
||||
<div class="no-items__inner">
|
||||
<SvgIcon name="warning" :size="120" color="#cbd5e1" />
|
||||
<SvgIcon name="smiley-sad" :size="171" />
|
||||
<p class="no-items__title">{{ title }}</p>
|
||||
<p class="no-items__desc">{{ desc }}</p>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@click="onChange(tab.name)"
|
||||
>
|
||||
<span class="tabs-block__tab-card">
|
||||
<SvgIcon v-if="tab.icon" :name="tab.icon" :size="16" color="#bcbcbc" />
|
||||
<SvgIcon v-if="tab.icon" :name="tab.icon" :size="18" color="#bcbcbc" />
|
||||
<span class="tabs-block__tab-label">{{ tab.label }}</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
@dragleave="isDragging = false"
|
||||
@drop="handleDrop"
|
||||
>
|
||||
<SvgIcon name="upload" :size="32" class="file-uploader__upload-icon" />
|
||||
<span class="file-uploader__upload-text">{{ uploadText }}</span>
|
||||
<img :src="uploadSimple" class="file-uploader__upload-icon" />
|
||||
<span class="file-uploader__upload-text">
|
||||
<span class="file-uploader__upload-text--light">بارگذاری</span>
|
||||
<span class="file-uploader__upload-text--bold">فایل ضمیمــــه</span>
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
class="file-uploader__input"
|
||||
@@ -56,14 +59,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="file-uploader__empty">هیچ فایلی آپلود نشده است</div>
|
||||
<!-- <div v-else class="file-uploader__empty">هیچ فایلی آپلود نشده است</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import SvgIcon from '@/components/icons/SvgIcon.vue'
|
||||
import CircleButton from '@/components/CircleButton.vue'
|
||||
import uploadSimple from '@/assets/images/upload-simple.png'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Array, default: () => [] },
|
||||
@@ -77,10 +81,6 @@ const emit = defineEmits(['update:modelValue', 'select', 'remove', 'error'])
|
||||
|
||||
const isDragging = ref(false)
|
||||
|
||||
const uploadText = computed(() =>
|
||||
props.modelValue.length === 0 ? 'فایل جدید اضافه کنید' : 'فایل دیگری اضافه کنید'
|
||||
)
|
||||
|
||||
const handleFileSelect = (event) => {
|
||||
const files = [...event.target.files]
|
||||
emitFiles(files)
|
||||
@@ -140,14 +140,15 @@ const formatFileSize = (bytes) => {
|
||||
}
|
||||
|
||||
&__dropzone {
|
||||
border: 2px dashed #d1d5db;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #f4f4f4;
|
||||
border-radius: 1rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
transition: border-color 0.2s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
&:hover,
|
||||
@@ -161,10 +162,20 @@ const formatFileSize = (bytes) => {
|
||||
}
|
||||
|
||||
&__upload-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: var(--font-family-fa);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #4b5563;
|
||||
|
||||
&--light {
|
||||
text-align: right;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&--bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
</div>
|
||||
|
||||
<div class="image-cropper__hint">
|
||||
<SvgIcon name="warning" class="image-cropper__hint-icon" />
|
||||
<SvgIcon :name="helperIcon" color="#B8B8B8" :size="20" />
|
||||
<p>{{ helperText }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -99,6 +99,7 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: 'سایز تصویر رسمی 3x4 و حجم تصویر بیشتر از 250 کیلو بایت نباشد.',
|
||||
},
|
||||
helperIcon: { type: String, default: 'warning' },
|
||||
error: { type: String, default: '' },
|
||||
})
|
||||
|
||||
@@ -314,7 +315,7 @@ onBeforeUnmount(cleanup)
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
gap: 0.25rem;
|
||||
|
||||
p {
|
||||
font-family: var(--font-family-fa);
|
||||
@@ -325,7 +326,7 @@ onBeforeUnmount(cleanup)
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.75rem;
|
||||
line-height: 1.85rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<SvgIcon
|
||||
name="caret-down"
|
||||
:size="20"
|
||||
color="#DDDDDD"
|
||||
class="select-field__caret"
|
||||
:class="{ 'select-field__caret--open': isOpen }"
|
||||
/>
|
||||
@@ -242,10 +243,6 @@ onBeforeUnmount(() => unmountFloating())
|
||||
transition: all 0.2s;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px var(--color-validate);
|
||||
}
|
||||
|
||||
&--error {
|
||||
border-color: var(--color-error);
|
||||
|
||||
@@ -308,10 +305,6 @@ onBeforeUnmount(() => unmountFloating())
|
||||
border: 1px solid var(--color-thd-gray);
|
||||
border-radius: 0.75rem;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px var(--color-validate);
|
||||
}
|
||||
}
|
||||
|
||||
&__empty {
|
||||
|
||||
@@ -116,7 +116,6 @@ defineExpose({ focus: () => inputEl.value?.focus() })
|
||||
|
||||
&:focus {
|
||||
border-color: var(--color-thd-gray);
|
||||
box-shadow: 0 0 0 1px var(--color-validate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +134,7 @@ defineExpose({ focus: () => inputEl.value?.focus() })
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
inset-inline-start: 0.75rem;
|
||||
inset-inline-end: 0.75rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
|
||||
@@ -143,7 +143,6 @@ onMounted(() => {
|
||||
|
||||
&:focus {
|
||||
border-color: var(--color-thd-gray);
|
||||
box-shadow: 0 0 0 1px var(--color-validate);
|
||||
}
|
||||
|
||||
&--no-resize {
|
||||
|
||||
@@ -26,6 +26,7 @@ const props = defineProps({
|
||||
const component = computed(() => {
|
||||
const c = iconRegistry[props.name]
|
||||
if (!c && import.meta.env.DEV) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[SvgIcon] unknown icon "${props.name}"`)
|
||||
}
|
||||
|
||||
@@ -46,6 +47,5 @@ const iconStyle = computed(() => ({
|
||||
.svg-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
fill: currentcolor;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,17 +12,23 @@ export type IconName =
|
||||
| 'caret-down'
|
||||
| 'caret-left'
|
||||
| 'caret-right'
|
||||
| 'certificate'
|
||||
| 'chat'
|
||||
| 'chat-centered-dots'
|
||||
| 'chat-teardrop-text'
|
||||
| 'check'
|
||||
| 'check-circle'
|
||||
| 'check-square'
|
||||
| 'clock'
|
||||
| 'close'
|
||||
| 'copy'
|
||||
| 'envelope-open'
|
||||
| 'eye'
|
||||
| 'eye-slash'
|
||||
| 'file'
|
||||
| 'folder-simple-star'
|
||||
| 'funnel'
|
||||
| 'graduation-cap'
|
||||
| 'heart'
|
||||
| 'instagram'
|
||||
| 'link'
|
||||
@@ -39,12 +45,14 @@ export type IconName =
|
||||
| 'plus'
|
||||
| 'scroll'
|
||||
| 'shopping-bag'
|
||||
| 'smiley-sad'
|
||||
| 'spinner'
|
||||
| 'square'
|
||||
| 'telegram'
|
||||
| 'trash'
|
||||
| 'upload'
|
||||
| 'user'
|
||||
| 'user-circle-check'
|
||||
| 'user-sound'
|
||||
| 'users'
|
||||
| 'users-three'
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
v-model="form.description"
|
||||
name="description"
|
||||
label="توضیحات خود را وارد کنید"
|
||||
placeholder="لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است."
|
||||
:row="4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
<div class="auth-heading">
|
||||
<img :src="logo" alt="banu" class="auth-heading__logo" />
|
||||
<h2 class="auth-heading__title">{{ title }}</h2>
|
||||
<p v-if="subtitle" class="auth-heading__subtitle">
|
||||
<p v-if="step === 'otp'" class="auth-heading__subtitle">
|
||||
<span class="auth-heading__phone-label">ارسال کد فعالسازی به شماره</span>
|
||||
<span class="auth-heading__phone">{{ phoneNumber || '*******' }}</span>
|
||||
</p>
|
||||
<p v-else-if="subtitle" class="auth-heading__subtitle">
|
||||
<slot name="subtitle">{{ subtitle }}</slot>
|
||||
</p>
|
||||
<slot name="below" />
|
||||
@@ -15,6 +19,8 @@ import { gallery } from '@/utils/gallery'
|
||||
defineProps({
|
||||
title: { type: String, required: true },
|
||||
subtitle: { type: String, default: '' },
|
||||
step: { type: String, default: '' },
|
||||
phoneNumber: { type: String, default: '' },
|
||||
})
|
||||
|
||||
const logo = gallery.authLogo
|
||||
@@ -49,5 +55,18 @@ const logo = gallery.authLogo
|
||||
color: #a7a7a7;
|
||||
margin: 0.25rem 0 0;
|
||||
}
|
||||
|
||||
&__phone-label {
|
||||
padding-inline-end: 0.75rem;
|
||||
color: #a7a7a7;
|
||||
font-family: var(--font-family-fa);
|
||||
border-left: 1px solid #a7a7a7;
|
||||
}
|
||||
|
||||
&__phone {
|
||||
padding-inline-start: 0.75rem;
|
||||
color: var(--color-prim-gray);
|
||||
font-family: var(--font-family-number-fa);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
</TextField>
|
||||
|
||||
<div class="login-form__password-wrap">
|
||||
<button type="button" class="login-form__forgot" @click="goForgotPassword">
|
||||
رمز عبور خودم را فراموش کرده ام
|
||||
</button>
|
||||
<PasswordField
|
||||
v-model="form.password"
|
||||
name="password"
|
||||
@@ -30,6 +27,10 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="button" class="login-form__forgot" @click="goForgotPassword">
|
||||
رمز عبور خودم را فراموش کرده ام
|
||||
</button>
|
||||
|
||||
<BaseButton
|
||||
type="submit"
|
||||
text="تایید اطلاعات"
|
||||
@@ -138,9 +139,7 @@ const goForgotPassword = () => emit('next', { mode: 'forgotPassword' })
|
||||
}
|
||||
|
||||
.login-form__forgot {
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
top: -0.25rem;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
background: transparent;
|
||||
border: none;
|
||||
@@ -149,6 +148,7 @@ const goForgotPassword = () => emit('next', { mode: 'forgotPassword' })
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-prim-gray);
|
||||
text-align: right;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
@@ -168,15 +168,19 @@ const goForgotPassword = () => emit('next', { mode: 'forgotPassword' })
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form__link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
font-family: var(--font-family-fa);
|
||||
font-size: 0.75rem;
|
||||
color: #a7a7a7;
|
||||
align-items: start;
|
||||
|
||||
a {
|
||||
color: var(--color-sec-gray);
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
</template>
|
||||
</BaseButton>
|
||||
|
||||
<button type="button" class="login-otp__back" @click="emit('back')">
|
||||
بازگشت به ورود با رمز عبور
|
||||
</button>
|
||||
<div class="login-otp__links">
|
||||
<button type="button" class="login-otp__back" @click="emit('back')">
|
||||
بازگشت به ورود با رمز عبور
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-form__footer">
|
||||
@@ -124,8 +126,18 @@ const onSubmit = async () => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-otp__links {
|
||||
margin-top: 1.5rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.login-otp__back {
|
||||
margin-top: 1rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -26,7 +26,7 @@ defineProps({
|
||||
height: 100dvh;
|
||||
background-size: contain;
|
||||
background-position-x: right;
|
||||
background-repeat: no-repeat;
|
||||
background-repeat: repeat;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
flex-basis: 66.6667%;
|
||||
|
||||
@@ -160,7 +160,7 @@ const onSubmit = async () => {
|
||||
}
|
||||
|
||||
.signup-form__login-link {
|
||||
margin-top: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
@@ -168,6 +168,10 @@ const onSubmit = async () => {
|
||||
font-size: 0.75rem;
|
||||
color: #a7a7a7;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-sec-gray);
|
||||
text-decoration: underline;
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<form class="auth-form" @submit.prevent="onSubmit">
|
||||
<div class="auth-form__body">
|
||||
<AuthHeading title="کد تایید">
|
||||
<template #subtitle>
|
||||
<span class="verify__phone-label">ارسال کد فعالسازی به شماره</span>
|
||||
<span class="verify__phone">{{ phoneNumber || '*******' }}</span>
|
||||
</template>
|
||||
<AuthHeading title="کد تایید" step="otp" :phone-number="phoneNumber">
|
||||
<template #below>
|
||||
<button type="button" class="verify__resend" @click="onResend" :disabled="resending">
|
||||
{{ resending ? 'در حال ارسال...' : 'ارسال مجدد کد تایید' }}
|
||||
@@ -33,9 +29,20 @@
|
||||
</template>
|
||||
</BaseButton>
|
||||
|
||||
<button type="button" class="verify__back" @click="emit('back')">
|
||||
تغییر شماره تلفن همراه
|
||||
</button>
|
||||
<div class="verify__links">
|
||||
<div class="verify__link">
|
||||
<SvgIcon name="user" :size="12" color="#A7A7A7" />
|
||||
<button type="button" class="verify__link-btn" @click="emit('password-login')">
|
||||
ورود با رمز عبور
|
||||
</button>
|
||||
</div>
|
||||
<div class="verify__link">
|
||||
<SvgIcon name="phone" :size="12" color="#A7A7A7" />
|
||||
<button type="button" class="verify__link-btn" @click="emit('back')">
|
||||
تغییر شماره تلفن همراه
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-form__footer">
|
||||
@@ -71,7 +78,7 @@ const props = defineProps({
|
||||
mode: { type: String, default: 'loginWithCode' },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['back', 'verified'])
|
||||
const emit = defineEmits(['back', 'verified', 'password-login'])
|
||||
|
||||
const schema = verifyCodeSchema
|
||||
const form = ref({ code: '' })
|
||||
@@ -170,18 +177,6 @@ const onResend = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
.verify__phone-label {
|
||||
padding-inline-end: 0.75rem;
|
||||
color: #a7a7a7;
|
||||
font-family: var(--font-family-fa);
|
||||
}
|
||||
|
||||
.verify__phone {
|
||||
padding-inline-start: 0.75rem;
|
||||
color: var(--color-prim-gray);
|
||||
font-family: var(--font-family-number-fa);
|
||||
}
|
||||
|
||||
.verify__resend {
|
||||
margin-top: 0.5rem;
|
||||
background: transparent;
|
||||
@@ -205,14 +200,36 @@ const onResend = async () => {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.verify__back {
|
||||
margin-top: 1rem;
|
||||
.verify__links {
|
||||
margin-top: 1.5rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.verify__link {
|
||||
display: inline-flex;
|
||||
align-items: start;
|
||||
gap: 0.25rem;
|
||||
font-family: var(--font-family-fa);
|
||||
font-size: 0.75rem;
|
||||
color: #a7a7a7;
|
||||
}
|
||||
|
||||
.verify__link-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-family-fa);
|
||||
font-size: 0.75rem;
|
||||
color: #a7a7a7;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
:phone-number="phoneNumber"
|
||||
:mode="mode"
|
||||
@back="step = LOGIN_STEPS.LOGIN_WITH_VERIFY_CODE"
|
||||
@password-login="step = LOGIN_STEPS.LOGIN"
|
||||
/>
|
||||
</AuthShell>
|
||||
</template>
|
||||
@@ -33,7 +34,7 @@ const handleNext = ({ mode: newMode }) => {
|
||||
step.value = LOGIN_STEPS.LOGIN_WITH_VERIFY_CODE
|
||||
}
|
||||
|
||||
const handleCodeSent = ({ phoneNumber: phone, mode: newMode }) => {
|
||||
const handleCodeSent = ({ phone, mode: newMode }) => {
|
||||
phoneNumber.value = phone
|
||||
mode.value = newMode
|
||||
step.value = LOGIN_STEPS.VERIFY_CODE
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
mode="signup"
|
||||
@back="step = SIGNUP_STEPS.SIGNUP"
|
||||
@verified="handleVerified"
|
||||
@password-login="router.push({ name: 'login' })"
|
||||
/>
|
||||
</AuthShell>
|
||||
</template>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
desc="در این قسمت شما میتوانید گواهینامه هر دوره ای را که گذرانده اید را مشاهده کنید."
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon name="scroll" :size="24" color="var(--color-primary)" />
|
||||
<SvgIcon name="certificate" :size="24" color="var(--color-primary)" />
|
||||
</template>
|
||||
</BoxedIconTitleBlock>
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
v-model="form.message"
|
||||
name="message"
|
||||
label="توضیحات خود را وارد نمایید"
|
||||
placeholder="لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است."
|
||||
:row="5"
|
||||
:error="errors.message"
|
||||
/>
|
||||
@@ -118,11 +117,7 @@ const onSubmit = async () => {
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__submit {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
desc="درخواست مشاوره جدید ثبت کنید یا تاریخچه مشاورههای خود را مشاهده نمایید."
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon name="chat" :size="24" color="var(--color-primary)" />
|
||||
<img :src="chatsImage" alt="مشاوره" class="student-consultants__heading-img" />
|
||||
</template>
|
||||
</BoxedIconTitleBlock>
|
||||
|
||||
@@ -37,10 +37,10 @@ import { computed, ref } from 'vue'
|
||||
import { toast } from 'vue3-toastify'
|
||||
import { TICKET_STATUS } from '@/enums'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import SvgIcon from '@/components/icons/SvgIcon.vue'
|
||||
import NoItems from '@/components/blocks/NoItems.vue'
|
||||
import { formatJalaaliDate } from '@/utils/date-utils'
|
||||
import TabsBlock from '@/components/blocks/TabsBlock.vue'
|
||||
import chatsImage from '@/assets/images/tickets/chats.png'
|
||||
import { usePagination } from '@/composables/usePagination'
|
||||
import PaginationBlock from '@/components/blocks/PaginationBlock.vue'
|
||||
import BoxedIconTitleBlock from '@/components/blocks/BoxedIconTitleBlock.vue'
|
||||
@@ -130,5 +130,11 @@ const onShowDetails = () => {}
|
||||
&__heading {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
&__heading-img {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -58,8 +58,8 @@ import StudentCourseItem from '@/features/student/courses/components/StudentCour
|
||||
const router = useRouter()
|
||||
|
||||
const tabs = [
|
||||
{ name: 'current', label: 'در حال گذراندن', icon: 'list-bullets' },
|
||||
{ name: 'ended', label: 'تکمیل شده', icon: 'list-bullets' },
|
||||
{ name: 'current', label: 'در حال گذراندن', icon: 'play' },
|
||||
{ name: 'ended', label: 'تکمیل شده', icon: 'check-circle' },
|
||||
]
|
||||
const activeTab = ref('current')
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
desc="در این قسمت میتوانید پروفایل خود را بهروز کنید."
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon name="user" :size="24" color="var(--color-primary)" />
|
||||
<SvgIcon name="user-circle-check" :size="24" color="var(--color-primary)" />
|
||||
</template>
|
||||
</BoxedIconTitleBlock>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
v-model="avatar"
|
||||
name="avatar"
|
||||
bg-color="#eeeeee"
|
||||
helper-text="برای آپلود تصویر جدید لطفا روی دکمه بالا کلیک کنید."
|
||||
@crop="onAvatarCropped"
|
||||
@error="onAvatarError"
|
||||
/>
|
||||
@@ -405,34 +406,36 @@ const onCancel = () => router.push({ name: 'student-dashboard' })
|
||||
}
|
||||
|
||||
&__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
grid-template-columns: 4fr 8fr;
|
||||
display: grid;
|
||||
grid-template-columns: 8fr 4fr;
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
grid-template-columns: 3fr 9fr;
|
||||
display: grid;
|
||||
grid-template-columns: 9fr 3fr;
|
||||
}
|
||||
}
|
||||
|
||||
&__avatar-col {
|
||||
order: 2;
|
||||
order: 1;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
order: 1;
|
||||
order: 2;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__main-col {
|
||||
order: 1;
|
||||
order: 2;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
order: 2;
|
||||
padding-inline-start: 1.25rem;
|
||||
order: 1;
|
||||
padding-inline-end: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
v-model="form.message"
|
||||
name="message"
|
||||
label="توضیحات خود را وارد نمایید"
|
||||
placeholder="لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است."
|
||||
:row="5"
|
||||
:error="errors.message"
|
||||
/>
|
||||
@@ -210,7 +209,7 @@ const onSubmit = async () => {
|
||||
gap: 1rem;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +217,10 @@ const onSubmit = async () => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
&--full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
@show-details="onShowDetails"
|
||||
/>
|
||||
</div>
|
||||
<NoItems v-else title="ترمای نیست" desc="در حال حاضر ترمای در حال گذراندن ندارید." />
|
||||
<NoItems v-else title="ترمی نیست" desc="در حال حاضر ترمی در حال گذراندن ندارید." />
|
||||
<PaginationBlock :pagination="currentMeta" @update:page="setCurrentPage" />
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="ticket-item">
|
||||
<div class="ticket-item" :class="{ 'ticket-item--clickable': isMobile }" @click="onItemClick">
|
||||
<div class="ticket-item__user">
|
||||
<div v-if="ticket.student?.avatarUrl" class="ticket-item__avatar">
|
||||
<img :src="ticket.student.avatarUrl" :alt="userName" />
|
||||
@@ -43,12 +43,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import Badge from '@/components/Badge.vue'
|
||||
import BaseButton from '@/components/BaseButton.vue'
|
||||
import SvgIcon from '@/components/icons/SvgIcon.vue'
|
||||
import { formatJalaaliDate } from '@/utils/date-utils'
|
||||
import { TICKET_PRIORITY, TICKET_STATUS } from '@/enums'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
const STATUS_VARIANT = {
|
||||
open: 'warning',
|
||||
@@ -69,6 +69,17 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['show-details'])
|
||||
|
||||
const isMobile = ref(window.innerWidth < 1024)
|
||||
const onResize = () => {
|
||||
isMobile.value = window.innerWidth < 1024
|
||||
}
|
||||
onMounted(() => window.addEventListener('resize', onResize))
|
||||
onBeforeUnmount(() => window.removeEventListener('resize', onResize))
|
||||
|
||||
const onItemClick = () => {
|
||||
if (isMobile.value) emit('show-details', props.ticket)
|
||||
}
|
||||
|
||||
const userName = computed(() => props.ticket.student?.name || props.ticket.subject || '—')
|
||||
|
||||
const statusLabel = computed(() => TICKET_STATUS[props.ticket.status] || '—')
|
||||
@@ -96,6 +107,10 @@ const createdAt = computed(() => formatJalaaliDate(props.ticket.createdAt) || '
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -105,6 +120,7 @@ const createdAt = computed(() => formatJalaaliDate(props.ticket.createdAt) || '
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
display: none;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
min-width: 3rem;
|
||||
@@ -112,10 +128,13 @@ const createdAt = computed(() => formatJalaaliDate(props.ticket.createdAt) || '
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -166,13 +185,14 @@ const createdAt = computed(() => formatJalaaliDate(props.ticket.createdAt) || '
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
display: none;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
flex: 1 1 100%;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
v-model="form.message"
|
||||
name="message"
|
||||
label="توضیحات خودتان را وارد کنید"
|
||||
placeholder="لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است."
|
||||
:row="4"
|
||||
:error="errors.message"
|
||||
/>
|
||||
@@ -55,8 +54,8 @@
|
||||
|
||||
<footer class="ctm__footer">
|
||||
<button type="button" class="ctm__cancel" @click="close">
|
||||
<SvgIcon name="caret-right" :size="16" color="currentColor" />
|
||||
<span>منصرف شدم</span>
|
||||
<SvgIcon name="caret-left" :size="16" color="currentColor" />
|
||||
</button>
|
||||
<BaseButton
|
||||
type="submit"
|
||||
@@ -211,13 +210,17 @@ const onSubmit = async (close) => {
|
||||
&__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@media (min-width: 640px) {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
&__cancel {
|
||||
display: inline-flex;
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
background: transparent;
|
||||
@@ -229,6 +232,10 @@ const onSubmit = async (close) => {
|
||||
font-weight: 500;
|
||||
color: #969696;
|
||||
|
||||
@media (min-width: 640px) {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #6b6b6b;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
desc="در این قسمت میتوانید تیکتهای خود را مدیریت کنید"
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon name="chat" :size="24" color="var(--color-primary)" />
|
||||
<SvgIcon name="envelope-open" :size="24" color="var(--color-primary)" />
|
||||
</template>
|
||||
</BoxedIconTitleBlock>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
@click="onNewTicket"
|
||||
>
|
||||
<template #appendIcon>
|
||||
<SvgIcon name="plus" :size="16" color="#fff" />
|
||||
<SvgIcon name="arrow-left" :size="16" color="#fff" />
|
||||
</template>
|
||||
</BaseButton>
|
||||
</div>
|
||||
@@ -127,7 +127,7 @@ const statusTiles = computed(() => [
|
||||
{
|
||||
key: 'all',
|
||||
image: chatTextPng,
|
||||
label: 'همه تیکتها',
|
||||
label: 'تمام تیکتها',
|
||||
count: stats.value?.all ?? 0,
|
||||
countColor: '#B6842D',
|
||||
},
|
||||
|
||||
@@ -198,6 +198,9 @@ const menuItems = computed(() => [
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
@@ -214,8 +217,8 @@ const menuItems = computed(() => [
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin-top: auto;
|
||||
padding-top: 0.5rem;
|
||||
margin-top: 2.5rem;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
|
||||
@@ -108,6 +108,9 @@ const menuItems = computed(() => [
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
@@ -124,8 +127,8 @@ const menuItems = computed(() => [
|
||||
}
|
||||
|
||||
&__footer {
|
||||
margin-top: auto;
|
||||
padding-top: 0.5rem;
|
||||
margin-top: 2.5rem;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
|
||||
@@ -54,20 +54,8 @@ const pageTitle = computed(() => route.meta?.title || 'پیشخوان')
|
||||
|
||||
const menuItems = computed(() => [
|
||||
{
|
||||
title: 'پیشـخوان',
|
||||
icon: 'menu',
|
||||
to: { name: 'student-dashboard' },
|
||||
active: route.name === 'student-dashboard',
|
||||
},
|
||||
{
|
||||
title: 'دورههای من',
|
||||
icon: 'book',
|
||||
to: { name: 'student-courses' },
|
||||
active: ['student-courses', 'student-course-details'].includes(route.name),
|
||||
},
|
||||
{
|
||||
title: 'دورههای آموزشی',
|
||||
icon: 'book',
|
||||
title: 'آموزش',
|
||||
icon: 'graduation-cap',
|
||||
to: { name: 'student-terms' },
|
||||
active: [
|
||||
'student-terms',
|
||||
@@ -75,44 +63,47 @@ const menuItems = computed(() => [
|
||||
'student-lesson-details',
|
||||
'student-session-details',
|
||||
'student-exam',
|
||||
'student-courses',
|
||||
'student-course-details',
|
||||
'student-dashboard',
|
||||
].includes(route.name),
|
||||
},
|
||||
{
|
||||
title: 'سامانه اعزام',
|
||||
icon: 'paper-plane-right',
|
||||
icon: 'user-sound',
|
||||
to: { name: 'student-missionary' },
|
||||
active: route.name === 'student-missionary',
|
||||
},
|
||||
{
|
||||
title: 'خدمات',
|
||||
icon: 'paper-plane',
|
||||
icon: 'folder-simple-star',
|
||||
to: { name: 'student-services' },
|
||||
active: route.name === 'student-services',
|
||||
},
|
||||
{
|
||||
title: 'مشاوره',
|
||||
icon: 'chat',
|
||||
icon: 'chat-teardrop-text',
|
||||
to: { name: 'student-consultants' },
|
||||
active: route.name === 'student-consultants',
|
||||
},
|
||||
{
|
||||
title: 'تیکتهای من',
|
||||
icon: 'chat-centered-dots',
|
||||
to: { name: 'student-tickets' },
|
||||
active: route.name === 'student-tickets',
|
||||
},
|
||||
{
|
||||
title: 'گواهینامه ها',
|
||||
icon: 'scroll',
|
||||
title: 'گواهینامه',
|
||||
icon: 'certificate',
|
||||
to: { name: 'student-certificates' },
|
||||
active: route.name === 'student-certificates',
|
||||
},
|
||||
{
|
||||
title: 'ویرایش پروفایل',
|
||||
icon: 'user',
|
||||
icon: 'user-circle-check',
|
||||
to: { name: 'student-profile' },
|
||||
active: route.name === 'student-profile',
|
||||
},
|
||||
{
|
||||
title: 'تیکت',
|
||||
icon: 'envelope-open',
|
||||
to: { name: 'student-tickets' },
|
||||
active: route.name === 'student-tickets',
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
@@ -177,6 +168,9 @@ const menuItems = computed(() => [
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100dvh;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
@@ -189,16 +183,8 @@ const menuItems = computed(() => [
|
||||
}
|
||||
|
||||
&__footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 1.25rem;
|
||||
width: 100%;
|
||||
margin-top: auto;
|
||||
padding: 0.5rem 0 1.25rem;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -60,7 +60,7 @@ const logo = gallery.logoPinkishRed
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 3.5rem;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ const logo = gallery.logoPinkishRed
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 3.5rem;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<aside class="student-sidebar">
|
||||
<aside class="student-sidebar" :style="{ backgroundImage: `url(${bgPattern})` }">
|
||||
<div class="student-sidebar__logo">
|
||||
<img :src="logo" alt="banu" />
|
||||
</div>
|
||||
@@ -12,7 +12,7 @@
|
||||
class="student-sidebar__link"
|
||||
:class="{ 'student-sidebar__link--active': item.active }"
|
||||
>
|
||||
<SvgIcon v-if="item.icon" :name="item.icon" :size="22" color="#fff" />
|
||||
<SvgIcon v-if="item.icon" :name="item.icon" :size="22" color="currentColor" />
|
||||
<span>{{ item.title }}</span>
|
||||
</component>
|
||||
</li>
|
||||
@@ -30,17 +30,20 @@ defineProps({
|
||||
})
|
||||
|
||||
const logo = gallery.logoWhite
|
||||
const bgPattern = gallery.studentSidebarPattern
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.student-sidebar {
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
padding: 1.5rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
border-radius: 1rem;
|
||||
border-radius: 0;
|
||||
border-top-left-radius: 2rem;
|
||||
border-bottom-left-radius: 2rem;
|
||||
background-color: var(--color-primary);
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
@@ -51,7 +54,7 @@ const logo = gallery.logoWhite
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
height: 3.5rem;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import loginPattern from '@/assets/images/auth/login-pattern.jpg'
|
||||
import logoPinkishRed from '@/assets/images/logo-pinkish-red.png'
|
||||
import registrationCompleteFlower from '@/assets/images/auth/flower.png'
|
||||
import loginPatternMobile from '@/assets/images/auth/login-pattern-mobile.png'
|
||||
import loginPatternWithLogo from '@/assets/images/auth/login-pattern-logo.jpg'
|
||||
import loginPatternWithLogo from '@/assets/images/auth/login-pattern-logo.png'
|
||||
import studentSidebarPattern from '@/assets/images/backgrounds/student-layout-bg.jpg'
|
||||
|
||||
export const gallery = {
|
||||
loginPattern,
|
||||
@@ -16,4 +17,5 @@ export const gallery = {
|
||||
logoPinkishRed,
|
||||
VideoCover,
|
||||
registrationCompleteFlower,
|
||||
studentSidebarPattern,
|
||||
}
|
||||
|
||||