Merge branch 'main' of https://github.com/Byeto-Company/hossein_por_shop
This commit is contained in:
@@ -86,6 +86,7 @@ const closeModal = () => {
|
||||
for_me: "بله",
|
||||
};
|
||||
}
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const addNew = () => {
|
||||
|
||||
+2
-2
@@ -19,11 +19,11 @@ withDefaults(defineProps<Props>(), {
|
||||
<div class="w-full flex flex-col gap-2">
|
||||
<div class="flex items-center gap-1 ps-2">
|
||||
<label :for="id" class="typo-label-sm">{{ label }}</label>
|
||||
<span v-if="required" class="text-danger-600">*</span>
|
||||
<span v-if="!!required && required" class="text-danger-600">*</span>
|
||||
</div>
|
||||
<slot />
|
||||
<div
|
||||
v-if="error.$error"
|
||||
v-if="!!error && error.$error"
|
||||
class="w-full typo-label-xs flex items-center py-1 px-3 rounded-md text-danger-600"
|
||||
>
|
||||
{{ error.$errors[0].$message }}
|
||||
@@ -0,0 +1,144 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import { useToast } from "~/composables/global/useToast";
|
||||
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
modelValue: File[];
|
||||
};
|
||||
|
||||
type Emits = {
|
||||
"update:modelValue": [value: any];
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { modelValue } = toRefs(props);
|
||||
|
||||
// emits
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
// state
|
||||
|
||||
const { addToast } = useToast();
|
||||
|
||||
const dropZoneRef = ref<HTMLDivElement>();
|
||||
const fileLimit = 1024 * 1024 * 2;
|
||||
|
||||
// methods
|
||||
|
||||
const onDrop = (files: File[] | null) => {
|
||||
if (modelValue.value.length < 3) {
|
||||
files?.forEach((file, index) => {
|
||||
if (file.size > fileLimit) {
|
||||
files.splice(index, 1);
|
||||
addToast({
|
||||
message: "حداکثر حجم فایل 2 مگابایت می باشد",
|
||||
options: {
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
if (files.length > 3) {
|
||||
emit("update:modelValue", [...files.slice(0, 3)]);
|
||||
} else {
|
||||
if (modelValue.value.length + files.length <= 3) {
|
||||
files?.forEach((item) => {
|
||||
emit("update:modelValue", [...modelValue.value, item]);
|
||||
});
|
||||
} else {
|
||||
addToast({
|
||||
message: `مجاز به آپلود ${
|
||||
3 - modelValue.value.length
|
||||
} فایل دیگر هستید`,
|
||||
options: {
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addToast({
|
||||
message: "محدودیت تعداد را رعایت کنید",
|
||||
options: {
|
||||
status: "error",
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const { isOverDropZone } = useDropZone(dropZoneRef, {
|
||||
onDrop,
|
||||
dataTypes: ["image/jpeg", "image/png", "image/jpg"],
|
||||
});
|
||||
|
||||
const { open: openDialog, onChange } = useFileDialog({
|
||||
accept: "image/*",
|
||||
directory: false,
|
||||
});
|
||||
|
||||
onChange((files: any) => {
|
||||
let arr: File[] = [];
|
||||
Object.keys(files).forEach((item) => {
|
||||
arr.push(files[item]);
|
||||
});
|
||||
onDrop(arr);
|
||||
});
|
||||
|
||||
const deleteFile = (index: number) => {
|
||||
const clone = [...modelValue.value];
|
||||
clone.splice(index, 1);
|
||||
emit("update:modelValue", clone);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col w-full h-max gap-5 pt-8">
|
||||
<div
|
||||
ref="dropZoneRef"
|
||||
@click="openDialog"
|
||||
class="bg-slate-50 flex-col-center w-full transition-all text-black/50 gap-3 h-[20rem] border border-dashed rounded-xl cursor-pointer select-none"
|
||||
:class="isOverDropZone ? 'border-black' : ' border-slate-300'"
|
||||
>
|
||||
<Icon name="bi:file-earmark-arrow-down" size="32" />
|
||||
<p class="font-bold text-dynamic-primary text-sm lg:text-[1rem]">
|
||||
برای آپلود کلیک کنید یا فایل خود را اینجا بیاندازید
|
||||
</p>
|
||||
<p class="text-xs font-semibold">فرمت مجاز : jpg, png, jpeg</p>
|
||||
<p class="text-xs font-semibold">تعداد فایل مجاز : 3 عدد</p>
|
||||
<p class="text-xs font-semibold">حداکثر حجم فایل : 2 مگابایت</p>
|
||||
</div>
|
||||
|
||||
<ul
|
||||
v-for="(item, index) in modelValue"
|
||||
:key="index"
|
||||
v-auto-animate
|
||||
class="flex flex-row-reverse items-center justify-between w-full px-2 animate__animated animate__fadeIn"
|
||||
>
|
||||
<li class="w-full flex items-center">
|
||||
<div class="flex justify-end w-9/12">
|
||||
<p class="text-sm text-black">{{ item.name }}</p>
|
||||
</div>
|
||||
<div class="w-2/12">
|
||||
<p class="text-sm text-black">
|
||||
{{ (item.size / 1024).toFixed(2) }}KB
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-1/12">
|
||||
<Icon
|
||||
name="ci:close"
|
||||
class="**:stroke-red-500 cursor-pointer pb-1"
|
||||
@click="deleteFile(index)"
|
||||
size="28"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@@ -6,7 +6,7 @@ type Props = {
|
||||
disabled?: boolean;
|
||||
modelValue: string;
|
||||
error?: boolean;
|
||||
options: string[];
|
||||
options?: string[];
|
||||
placeholder?: string;
|
||||
triggerRootClass?: string;
|
||||
};
|
||||
@@ -71,7 +71,9 @@ const classes = computed(() => {
|
||||
:side-offset="5"
|
||||
>
|
||||
<SelectViewport class="p-[5px]">
|
||||
<SelectGroup>
|
||||
<slot v-if="!!$slots.content" name="content" />
|
||||
|
||||
<SelectGroup v-else>
|
||||
<SelectItem
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
|
||||
@@ -184,7 +184,7 @@ watch(
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<span class="text-black">فقط کالاهای موجود</span>
|
||||
|
||||
<Switch v-model="has_discount" />
|
||||
<Switch v-model="in_stock" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,24 +3,36 @@
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
borderLess?: boolean;
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
defineProps<Props>();
|
||||
withDefaults(defineProps<Props>(), {
|
||||
borderLess: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex flex-col items-start">
|
||||
<div
|
||||
class="w-full flex items-center justify-between h-[3rem] pb-5 px-5"
|
||||
>
|
||||
<div class="flex flex-col w-full gap-5">
|
||||
<div
|
||||
class="flex flex-col items-start"
|
||||
:class="{
|
||||
'border-b border-slate-200 pb-5': borderLess,
|
||||
}"
|
||||
>
|
||||
<div class="w-full flex items-center justify-between h-[3rem] px-5">
|
||||
<span class="typo-sub-h-lg">{{ title }}</span>
|
||||
<slot name="button" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full flex flex-col border border-slate-200 rounded-xl">
|
||||
<div
|
||||
class="w-full flex flex-col border rounded-xl"
|
||||
:class="{
|
||||
'border-none': borderLess,
|
||||
'border-slate-200': !borderLess,
|
||||
}"
|
||||
>
|
||||
<div class="w-full p-5">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -27,9 +27,18 @@ const profileLinks = ref([
|
||||
icon: "bi:ticket",
|
||||
title: "تیکت ها",
|
||||
path: { name: "profile-tickets" },
|
||||
matchPattern: /^profile-ticket/,
|
||||
},
|
||||
]);
|
||||
|
||||
const isLinkActive = (link: any) => {
|
||||
if (link.matchPattern) {
|
||||
return link.matchPattern.test(route.name);
|
||||
} else {
|
||||
return route.name === link.path.name;
|
||||
}
|
||||
};
|
||||
|
||||
// queries
|
||||
|
||||
const { data: account, suspense } = useGetAccount();
|
||||
@@ -66,9 +75,9 @@ await suspense();
|
||||
:key="index"
|
||||
:to="{ ...link.path }"
|
||||
:class="
|
||||
route.name == link.path.name
|
||||
? 'bg-black text-slate-100 **:fill-slate-100 '
|
||||
: '**:fill-black '
|
||||
isLinkActive(link)
|
||||
? 'bg-black text-slate-100 **:fill-slate-100'
|
||||
: '**:fill-black'
|
||||
"
|
||||
class="flex items-center justify-between transition-all rounded-lg py-4 px-3"
|
||||
>
|
||||
|
||||
+5
-5
@@ -6,14 +6,14 @@
|
||||
>
|
||||
<td
|
||||
scope="row"
|
||||
class="w-3/12 px-6 py-7 font-medium whitespace-nowrap text-black"
|
||||
class="w-3/12 px-6 py-6 font-medium whitespace-nowrap text-black"
|
||||
>
|
||||
Apple MacBook Pro 17
|
||||
</td>
|
||||
<td class="w-3/12 px-6 py-7">Silver</td>
|
||||
<td class="w-3/12 px-6 py-7">Laptop</td>
|
||||
<td class="w-2/12 px-6 py-7">$2999</td>
|
||||
<td class="w-1/12 px-6 py-7">
|
||||
<td class="w-3/12 px-6 py-6">Silver</td>
|
||||
<td class="w-3/12 px-6 py-6">Laptop</td>
|
||||
<td class="w-2/12 px-6 py-6">$2999</td>
|
||||
<td class="w-1/12 px-6 py-6">
|
||||
<NuxtLink :to="{ name: 'profile-tickets-id', params: { id: 1 } }">
|
||||
<button
|
||||
class="size-10 flex-center border border-slate-200 rounded-md"
|
||||
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
is_user: boolean;
|
||||
date: string;
|
||||
username: string;
|
||||
message: string;
|
||||
profile: string;
|
||||
files: string[];
|
||||
};
|
||||
|
||||
// props
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-full flex items-center"
|
||||
:class="is_user ? 'justify-start' : 'justify-end'"
|
||||
>
|
||||
<div
|
||||
class="w-full lg:w-1/2 bg-slate-50 border border-slate-200 rounded-xl p-5 flex items-start"
|
||||
:class="is_user ? 'rounded-br-none' : 'rounded-bl-none'"
|
||||
>
|
||||
<div class="w-2/12 flex items-start justify-start">
|
||||
<img :src="profile" class="size-16 rounded-full" />
|
||||
</div>
|
||||
|
||||
<div class="w-10/12 flex flex-col items-start pt-8">
|
||||
<div class="flex flex-col gap-0.5 w-full">
|
||||
<p
|
||||
class="text-[10px] font-semibold text-[#1677FF] line-clamp-1 text-right"
|
||||
style="direction: ltr"
|
||||
>
|
||||
{{ date }}
|
||||
</p>
|
||||
<p
|
||||
class="text-xs font-semibold text-dynamic-secondary line-clamp-1"
|
||||
>
|
||||
{{ username }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="py-2 text-start">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -28,7 +28,7 @@ const handleSelectAddress = (address: Address) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col gap-10">
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<ProfilePageTitle title="آدرس های شما" icon="bi:map" />
|
||||
|
||||
<ProfileSection title="همه">
|
||||
|
||||
@@ -156,7 +156,7 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col gap-10">
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<ProfilePageTitle title="پروفایل" icon="bi:person-vcard" />
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
@@ -243,7 +243,7 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
<div
|
||||
class="w-full grid grid-cols-1 lg:grid-cols-2 gap-x-3 gap-y-5"
|
||||
>
|
||||
<PersonalDataField
|
||||
<DataField
|
||||
id="personal-data-name"
|
||||
label="نام"
|
||||
:error="formValidator$.first_name"
|
||||
@@ -253,8 +253,8 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
variant="outlined"
|
||||
:error="formValidator$.first_name.$error"
|
||||
/>
|
||||
</PersonalDataField>
|
||||
<PersonalDataField
|
||||
</DataField>
|
||||
<DataField
|
||||
id="personal-data-last-name"
|
||||
label="نام خانوادگی"
|
||||
:error="formValidator$.last_name"
|
||||
@@ -264,8 +264,8 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
variant="outlined"
|
||||
:error="formValidator$.last_name.$error"
|
||||
/>
|
||||
</PersonalDataField>
|
||||
<PersonalDataField
|
||||
</DataField>
|
||||
<DataField
|
||||
id="personal-data-gender"
|
||||
label="جنسیت"
|
||||
:error="formValidator$.gender"
|
||||
@@ -276,8 +276,8 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
variant="outlined"
|
||||
:error="formValidator$.gender.$error"
|
||||
/>
|
||||
</PersonalDataField>
|
||||
<PersonalDataField
|
||||
</DataField>
|
||||
<DataField
|
||||
id="personal-data-birth-date"
|
||||
label="تاریخ تولد"
|
||||
:error="formValidator$.birth_date"
|
||||
@@ -286,8 +286,8 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
v-model="personalData.birth_date!"
|
||||
:error="formValidator$.birth_date.$error"
|
||||
/>
|
||||
</PersonalDataField>
|
||||
<PersonalDataField
|
||||
</DataField>
|
||||
<DataField
|
||||
id="personal-data-phone"
|
||||
label="تلفن همراه"
|
||||
:error="formValidator$.phone"
|
||||
@@ -297,8 +297,8 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
variant="outlined"
|
||||
:error="formValidator$.phone.$error"
|
||||
/>
|
||||
</PersonalDataField>
|
||||
<PersonalDataField
|
||||
</DataField>
|
||||
<DataField
|
||||
id="personal-email"
|
||||
label="حساب الکترونیکی"
|
||||
:error="formValidator$.email"
|
||||
@@ -308,7 +308,7 @@ const handleSubmit = (withValidation: boolean) => {
|
||||
variant="outlined"
|
||||
:error="formValidator$.email.$error"
|
||||
/>
|
||||
</PersonalDataField>
|
||||
</DataField>
|
||||
</div>
|
||||
</ProfileSection>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ definePageMeta({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col gap-10">
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<ProfilePageTitle title="خرید ها و سفارش های شما" icon="bi:cart" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,198 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
middleware: "check-is-logged-in",
|
||||
layout: "profile",
|
||||
});
|
||||
|
||||
// state
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const ticketData = ref({
|
||||
message: "",
|
||||
files: [],
|
||||
});
|
||||
|
||||
const messages = [
|
||||
{
|
||||
is_user: true,
|
||||
date: "2023-10-15T14:30:00Z",
|
||||
username: "JohnDoe",
|
||||
message: "Hey, did you get a chance to review the design files I sent?",
|
||||
profile: "https://example.com/profiles/johndoe.jpg",
|
||||
files: ["design-draft-1.pdf", "design-draft-2.pdf"],
|
||||
},
|
||||
{
|
||||
is_user: false,
|
||||
date: "2023-10-15T14:35:00Z",
|
||||
username: "JaneSmith",
|
||||
message:
|
||||
"Yes, I just finished reviewing them. The second draft looks great!",
|
||||
profile: "https://example.com/profiles/janesmith.jpg",
|
||||
files: [],
|
||||
},
|
||||
{
|
||||
is_user: true,
|
||||
date: "2023-10-15T14:40:00Z",
|
||||
username: "JohnDoe",
|
||||
message:
|
||||
"Awesome! Let me know if you need any changes before we finalize it.",
|
||||
profile: "https://example.com/profiles/johndoe.jpg",
|
||||
files: ["final-design-notes.txt"],
|
||||
},
|
||||
{
|
||||
is_user: false,
|
||||
date: "2023-10-15T14:35:00Z",
|
||||
username: "JaneSmith",
|
||||
message:
|
||||
"Yes, I just finished reviewing them. The second draft looks great!",
|
||||
profile: "https://example.com/profiles/janesmith.jpg",
|
||||
files: [],
|
||||
},
|
||||
{
|
||||
is_user: true,
|
||||
date: "2023-10-15T14:40:00Z",
|
||||
username: "JohnDoe",
|
||||
message:
|
||||
"Awesome! Let me know if you need any changes before we finalize it.",
|
||||
profile: "https://example.com/profiles/johndoe.jpg",
|
||||
files: ["final-design-notes.txt"],
|
||||
},
|
||||
{
|
||||
is_user: false,
|
||||
date: "2023-10-15T14:35:00Z",
|
||||
username: "JaneSmith",
|
||||
message:
|
||||
"Yes, I just finished reviewing them. The second draft looks great!",
|
||||
profile: "https://example.com/profiles/janesmith.jpg",
|
||||
files: [],
|
||||
},
|
||||
{
|
||||
is_user: true,
|
||||
date: "2023-10-15T14:40:00Z",
|
||||
username: "JohnDoe",
|
||||
message:
|
||||
"Awesome! Let me know if you need any changes before we finalize it.",
|
||||
profile: "https://example.com/profiles/johndoe.jpg",
|
||||
files: ["final-design-notes.txt"],
|
||||
},
|
||||
];
|
||||
|
||||
// computed
|
||||
|
||||
const ticketId = computed(() => route.params.id);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<ProfilePageTitle :title="`تیکت شماره ${ticketId}`" icon="bi:ticket" />
|
||||
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between w-full py-4 border-b lg:px-5 border-slate-200"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col items-start w-1/2 gap-4 lg:gap-5 lg:w-full lg:items-center lg:flex-row"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col w-full gap-2 lg:py-2 lg:pe-5 lg:w-1/3 lg:border-e border-slate-200"
|
||||
>
|
||||
<p class="text-xs lg:text-sm text-dynamic-secondary">
|
||||
شماره تیکت :
|
||||
</p>
|
||||
<p class="text-xs font-semibold lg:text-sm text-black">
|
||||
634932
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col w-full gap-2 lg:py-2 lg:pe-5 lg:w-1/3 lg:border-e border-slate-200"
|
||||
>
|
||||
<p class="text-xs lg:text-sm text-dynamic-secondary">
|
||||
دسته‌بندی :
|
||||
</p>
|
||||
<p class="text-xs font-semibold lg:text-sm text-black">
|
||||
گیفت‌کارت
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col w-full gap-2 lg:py-2 lg:pe-5 lg:w-1/3"
|
||||
>
|
||||
<p class="text-xs lg:text-sm text-dynamic-secondary">
|
||||
وضعیت :
|
||||
</p>
|
||||
<p
|
||||
class="text-xs font-semibold lg:text-sm text-black text-dynamic-secondary"
|
||||
>
|
||||
بسته شده
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-2 lg:hidden border-black">
|
||||
<p class="text-xs lg:text-sm text-dynamic-secondary">
|
||||
موضوع :
|
||||
</p>
|
||||
<p class="text-xs font-semibold lg:text-sm text-black">
|
||||
نمی‌دانم کد ارسال گیفت کارت خریداری شده را از کجا
|
||||
باید ببینم
|
||||
</p>
|
||||
</div>
|
||||
<div class="items-center justify-end hidden w-1/4 lg:flex">
|
||||
<NuxtLink :to="{ name: 'profile-tickets' }">
|
||||
<Button
|
||||
class="rounded-full"
|
||||
size="md"
|
||||
end-icon="bi:arrow-left"
|
||||
>
|
||||
بازگشت به تیکت ها
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="hidden w-full text-sm px-5 pb-8 mt-3 border-b lg:block text-md border-slate-200"
|
||||
>
|
||||
<span class="text-black/50"> موضوع : </span> نمی‌دانم کد ارسال
|
||||
گیفت کارت خریداری شده را از کجا باید ببینم
|
||||
</div>
|
||||
|
||||
<div class="w-full flex flex-col gap-5 h-[32rem] overflow-y-auto">
|
||||
<TicketBubble
|
||||
v-for="(message, index) in messages"
|
||||
:key="index"
|
||||
:is_user="message.is_user"
|
||||
:date="message.date"
|
||||
:message="message.message"
|
||||
:profile="message.profile"
|
||||
:username="message.username"
|
||||
:files="message.files"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid grid-cols-1 lg:grid-cols-2 gap-5 mt-5 pt-5 border-t border-slate-200"
|
||||
>
|
||||
<DataField id="message" :required="true" label="متن پیام">
|
||||
<textarea
|
||||
v-model="ticketData.message"
|
||||
class="w-full bg-white border-[1.5px] border-slate-200 rounded-xl text-xs lg:text-sm p-5 text-black h-[10rem] lg:h-[20rem] transition resize-none outline-none focus:!border-black"
|
||||
placeholder="متن تیکت را اینجا بنویسید ..."
|
||||
/>
|
||||
</DataField>
|
||||
<FileInput v-model="ticketData.files" />
|
||||
</div>
|
||||
|
||||
<div class="w-full flex-center py-5">
|
||||
<Button class="rounded-full px-20" end-icon="bi:send" size="md">
|
||||
<!-- <Icon
|
||||
v-if="createAddressIsPending"
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
/> -->
|
||||
<span>ارسال پیام</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import TicketsTableRow from "~/components/profile/tickets/TicketsTableRow.vue";
|
||||
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
@@ -26,10 +24,10 @@ const tableHeads = ref([
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full flex flex-col gap-10">
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<ProfilePageTitle title="تیکت های شما" icon="bi:ticket" />
|
||||
|
||||
<div class="w-full flex flex-col gap-8">
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<div class="w-full flex items-center justify-between px-5">
|
||||
<div class="flex items-center justify-start gap-8">
|
||||
<div class="flex items-center justify-start gap-3">
|
||||
|
||||
@@ -1,7 +1,158 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
middleware: "check-is-logged-in",
|
||||
layout: "profile",
|
||||
});
|
||||
|
||||
// types
|
||||
|
||||
type TicketCategory = {
|
||||
title: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
// state
|
||||
|
||||
const ticketCategories: TicketCategory[] = [
|
||||
{
|
||||
title: "مالی و حسابداری",
|
||||
value: "finance_and_accounting",
|
||||
},
|
||||
{
|
||||
title: "پروفایل کاربری",
|
||||
value: "user_profile",
|
||||
},
|
||||
{
|
||||
title: "پیگیری سفارش",
|
||||
value: "order_tracking",
|
||||
},
|
||||
{
|
||||
title: "احراز هویت",
|
||||
value: "authentication",
|
||||
},
|
||||
{
|
||||
title: "محصول",
|
||||
value: "product",
|
||||
},
|
||||
{
|
||||
title: "اعلام باگ و خطا در وبسایت",
|
||||
value: "bug_and_error_reporting",
|
||||
},
|
||||
{
|
||||
title: "سایر",
|
||||
value: "other",
|
||||
},
|
||||
];
|
||||
|
||||
const ticketData = ref({
|
||||
category: undefined,
|
||||
order: undefined,
|
||||
message: "",
|
||||
files: [],
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<div class="w-full flex flex-col gap-5">
|
||||
<ProfilePageTitle title="تیکت جدید" icon="bi:ticket" />
|
||||
|
||||
<ProfileSection title="ارتباط با پشتیبانی">
|
||||
<template #button>
|
||||
<NuxtLink :to="{ name: 'profile-tickets' }">
|
||||
<Button
|
||||
class="rounded-full"
|
||||
size="md"
|
||||
end-icon="bi:arrow-left"
|
||||
>
|
||||
بازگشت به تیکت ها
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||
<DataField id="category" :required="true" label="دسته بندی">
|
||||
<Select
|
||||
placeholder="انتخاب کنید"
|
||||
variant="outlined"
|
||||
v-model="ticketData.category"
|
||||
>
|
||||
<template #content>
|
||||
<SelectGroup>
|
||||
<SelectItem
|
||||
v-for="(
|
||||
category, index
|
||||
) in ticketCategories"
|
||||
:key="index"
|
||||
class="text-xs leading-none w-full rounded-sm py-5 flex items-center justify-between h-[25px] pr-[12px] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-slate-300 data-[highlighted]:text-black"
|
||||
:value="category.value"
|
||||
>
|
||||
<SelectItemIndicator
|
||||
class="absolute left-0 w-[25px] inline-flex items-center justify-center"
|
||||
>
|
||||
<Icon name="bi:check" size="20" />
|
||||
</SelectItemIndicator>
|
||||
<SelectItemText
|
||||
class="text-end font-iran-yekan-x text-sm"
|
||||
>
|
||||
{{ category.title }}
|
||||
</SelectItemText>
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</template>
|
||||
</Select>
|
||||
</DataField>
|
||||
<DataField id="orders" :required="true" label="خرید یا سفارش">
|
||||
<Select
|
||||
placeholder="انتخاب کنید"
|
||||
variant="outlined"
|
||||
v-model="ticketData.order"
|
||||
>
|
||||
<template #content>
|
||||
<SelectGroup>
|
||||
<SelectItem
|
||||
v-for="(
|
||||
category, index
|
||||
) in ticketCategories"
|
||||
:key="index"
|
||||
class="text-xs leading-none w-full rounded-sm py-5 flex items-center justify-between h-[25px] pr-[12px] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-slate-300 data-[highlighted]:text-black"
|
||||
:value="category.value"
|
||||
>
|
||||
<SelectItemIndicator
|
||||
class="absolute left-0 w-[25px] inline-flex items-center justify-center"
|
||||
>
|
||||
<Icon name="bi:check" size="20" />
|
||||
</SelectItemIndicator>
|
||||
<SelectItemText
|
||||
class="text-end font-iran-yekan-x text-sm"
|
||||
>
|
||||
{{ category.title }}
|
||||
</SelectItemText>
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</template>
|
||||
</Select>
|
||||
</DataField>
|
||||
<DataField id="message" :required="true" label="متن تیکت">
|
||||
<textarea
|
||||
v-model="ticketData.message"
|
||||
class="w-full bg-white border-[1.5px] border-slate-200 rounded-xl text-xs lg:text-sm p-5 text-black h-[10rem] lg:h-[20rem] transition resize-none outline-none focus:!border-black"
|
||||
placeholder="متن تیکت را اینجا بنویسید ..."
|
||||
/>
|
||||
</DataField>
|
||||
<FileInput v-model="ticketData.files" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
<div class="w-full flex-center py-5">
|
||||
<Button class="rounded-full px-20" end-icon="bi:send" size="md">
|
||||
<!-- <Icon
|
||||
v-if="createAddressIsPending"
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
/> -->
|
||||
<span>ارسال تیکت</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user