fix: bugs
Deploy banu-front / deploy (push) Successful in 1m22s

This commit is contained in:
sajjadtalkhabi
2026-07-02 19:09:13 +03:30
parent e700609236
commit b9ef684b51
51 changed files with 406 additions and 170 deletions
+2 -1
View File
@@ -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;
+35 -3
View File
@@ -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,
+66 -5
View File
@@ -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 {
+1 -1
View File
@@ -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 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+23 -12
View File
@@ -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 {
+4 -3
View File
@@ -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;
}
}
}
+1 -8
View File
@@ -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 {
+1 -2
View File
@@ -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;
-1
View File
@@ -143,7 +143,6 @@ onMounted(() => {
&:focus {
border-color: var(--color-thd-gray);
box-shadow: 0 0 0 1px var(--color-validate);
}
&--no-resize {
+1 -1
View File
@@ -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>
+8
View File
@@ -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'