Files
CRM_Montzar/apps/web/src/components/dashboard/HomeHero.vue
T

244 lines
7.1 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { computed } from 'vue';
import { SearchOutlined, SendOutlined } from '@ant-design/icons-vue';
import {
Button as AButton,
Input as AInput,
Typography,
} from 'ant-design-vue';
import { brand } from '@mento/shared';
2026-09-15 14:10:52 +03:30
import { useAuthStore } from '@/stores/auth';
const props = defineProps<{
greeting: string;
firstName: string;
userInitial: string;
askQuery: string;
asking?: boolean;
askAnswer?: string;
askError?: string;
statusText?: string;
}>();
const emit = defineEmits<{
'update:askQuery': [value: string];
ask: [];
}>();
2026-09-15 14:10:52 +03:30
const auth = useAuthStore();
const { Title: ATypographyTitle, Text: ATypographyText, Paragraph: ATypographyParagraph } =
Typography;
2026-09-15 14:10:52 +03:30
/** فقط عکس واقعی کاربر؛ پیش‌فرض عمومی را نشان نده */
const hasCustomPhoto = computed(() => Boolean(auth.user?.hasAvatar));
const shortcutLabel = computed(() =>
typeof navigator !== 'undefined' && /Mac|iPhone|iPad/i.test(navigator.platform)
? '⌘K'
: 'Ctrl+K',
);
const statusMessage = computed(() => {
if (props.asking) return props.statusText || 'در حال پاسخ…';
if (props.askError) return props.askError;
if (props.askAnswer) return props.askAnswer;
return '';
});
</script>
<template>
<div class="flex flex-col gap-3">
<div class="home-hero relative overflow-hidden rounded-2xl border border-emerald-300/50 shadow-sm">
<!-- base gradient -->
<div class="home-hero__gradient pointer-events-none absolute inset-0" aria-hidden="true" />
<!-- geometric pattern -->
<div class="home-hero__pattern pointer-events-none absolute inset-0" aria-hidden="true" />
<!-- soft orbs -->
<div
class="pointer-events-none absolute -left-20 -top-24 h-52 w-52 rounded-full bg-emerald-400/20 blur-3xl"
aria-hidden="true"
/>
<div
class="pointer-events-none absolute -right-16 -bottom-20 h-48 w-48 rounded-full bg-teal-500/15 blur-3xl"
aria-hidden="true"
/>
<div
class="relative flex flex-col lg:flex-row items-stretch lg:items-center justify-between gap-4 p-4 sm:p-5"
>
<div class="flex items-center gap-3.5 min-w-0">
<div class="home-hero__identity relative shrink-0" :aria-label="firstName">
<div class="home-hero__frame">
<img
v-if="hasCustomPhoto"
:src="auth.avatarSrc"
:alt="firstName"
class="home-hero__photo"
/>
<span v-else class="home-hero__monogram" aria-hidden="true">
{{ userInitial }}
</span>
</div>
<span class="home-hero__online" title="آنلاین" aria-hidden="true" />
</div>
<div class="min-w-0">
<p class="m-0 text-[11px] font-semibold tracking-wide text-emerald-700/80">
{{ brand.nameFa }}
</p>
<a-typography-title
:level="4"
class="!m-0 !mt-0.5 !text-slate-800 !text-lg sm:!text-xl !font-extrabold !leading-7 truncate"
>
{{ greeting }}، {{ firstName }}
</a-typography-title>
<a-typography-text class="!text-slate-500 !text-sm !mt-0.5 block">
از داده‌ها بپرسید یا کار امروز را از همین‌جا شروع کنید
</a-typography-text>
</div>
</div>
<div
class="flex items-center gap-2 w-full lg:max-w-xl bg-white border border-white/80 rounded-2xl px-3 py-2 shadow-md shadow-emerald-900/5 focus-within:border-emerald-300 focus-within:ring-2 focus-within:ring-emerald-200/70 transition"
>
<span
class="w-8 h-8 rounded-xl bg-emerald-50 text-emerald-600 flex items-center justify-center shrink-0"
aria-hidden="true"
>
<SearchOutlined />
</span>
<a-input
id="home-hero-ask"
:value="askQuery"
:placeholder="brand.askPlaceholderFa"
:disabled="asking"
:bordered="false"
class="flex-1 !bg-transparent"
@update:value="emit('update:askQuery', $event)"
@press-enter="emit('ask')"
/>
<kbd
class="hidden sm:inline-flex items-center text-[11px] font-sans text-slate-400 bg-slate-50 px-2 py-1 rounded-lg border border-slate-200 shrink-0"
>
{{ shortcutLabel }}
</kbd>
<a-button
type="primary"
shape="round"
:disabled="asking"
:loading="asking"
aria-label="ارسال"
@click="emit('ask')"
>
<template #icon><SendOutlined /></template>
<span class="hidden sm:inline">بپرس</span>
</a-button>
</div>
</div>
</div>
<div
v-if="statusMessage"
class="rounded-xl border px-3.5 py-2.5"
:class="
askError
? 'border-rose-200 bg-rose-50/70'
: asking
? 'border-emerald-200/70 bg-emerald-50/50'
: 'border-slate-200 bg-slate-50/80'
"
>
<a-typography-paragraph
:type="askError ? 'danger' : 'secondary'"
:ellipsis="{ rows: 3 }"
class="!mb-0 !text-sm"
>
{{ statusMessage }}
</a-typography-paragraph>
</div>
</div>
</template>
<style scoped>
.home-hero__gradient {
background:
linear-gradient(
115deg,
#ecfdf5 0%,
#d1fae5 28%,
#ccfbf1 55%,
#f0fdfa 78%,
#ffffff 100%
);
}
.home-hero__pattern {
opacity: 0.45;
background-image:
radial-gradient(circle at 1px 1px, rgba(16, 185, 129, 0.28) 1px, transparent 0),
linear-gradient(
135deg,
rgba(20, 184, 166, 0.08) 25%,
transparent 25%,
transparent 50%,
rgba(20, 184, 166, 0.08) 50%,
rgba(20, 184, 166, 0.08) 75%,
transparent 75%,
transparent
);
background-size:
18px 18px,
28px 28px;
mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.25));
}
.home-hero__identity {
width: 3.25rem;
height: 3.25rem;
}
.home-hero__frame {
width: 100%;
height: 100%;
border-radius: 1rem;
padding: 2px;
background: linear-gradient(145deg, #34d399 0%, #0d9488 48%, #059669 100%);
box-shadow:
0 0 0 3px rgba(255, 255, 255, 0.85),
0 8px 18px -10px rgba(4, 120, 87, 0.55);
}
.home-hero__photo,
.home-hero__monogram {
display: flex;
width: 100%;
height: 100%;
border-radius: calc(1rem - 2px);
object-fit: cover;
}
.home-hero__monogram {
align-items: center;
justify-content: center;
font-size: 1.15rem;
font-weight: 800;
color: #ecfdf5;
background:
radial-gradient(circle at 30% 25%, rgba(255, 255, 255, 0.22), transparent 45%),
linear-gradient(160deg, #10b981 0%, #0f766e 100%);
letter-spacing: -0.02em;
user-select: none;
}
.home-hero__online {
position: absolute;
left: -1px;
bottom: -1px;
width: 0.85rem;
height: 0.85rem;
border-radius: 999px;
background: #10b981;
border: 2px solid #fff;
box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.25);
}
</style>