complete Complete User Request Submission section RAD-1

This commit is contained in:
2026-01-03 19:14:30 +03:30
commit 7137ec5792
393 changed files with 152602 additions and 0 deletions
@@ -0,0 +1,56 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue'
import UiButton from '../../components/ui/UiButton.vue'
import { Link } from '@inertiajs/inertia-vue3';
const props = defineProps({
phone: String,
availableLevels: Array
})
const getLevelLabel = (level) => {
return level === 'supervisor' ? 'پورتال کاربران' : level === 'doctor' ? 'پورتال پزشکان' : 'پورتال کاربران'
}
const getLevelRoute = (level) => {
return `/auth/login/choose-portal/${level}`
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[450px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.ChoosePortal.title') }}
</h1>
<p class="!text-dominant-800 mt-1 text-[11px] font-normal">
{{ $t('auth.ChoosePortal.subtitle').replace('%phone%', phone) }}
</p>
<Link href="/auth/login/phone" class="!text-dominant-600 text-[11px] font-normal">
(تغییر شماره)
</Link>
</div>
<div class="flex flex-col gap-2 mt-6">
<Link
v-for="level in availableLevels"
:key="level"
:href="getLevelRoute(level)"
class="no-underline"
>
<UiButton
severity="secondary"
:label="getLevelLabel(level)"
size="large"
full-width
outlined
height="40px"
icon-position="start"
icon-class="!text-sm mt-1"
/>
</Link>
</div>
</main>
</AuthLayout>
</template>
@@ -0,0 +1,59 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue';
import UiButton from '../../components/ui/UiButton.vue'
import { Inertia } from '@inertiajs/inertia'
import { inject } from 'vue';
const route = inject('route');
const props = defineProps({
phone: String
})
const handleRegisterSupervisor = () => {
Inertia.visit(route('register.supervisor'))
}
const handleRegisterVet = () => {
Inertia.visit(route('register.veterinarian'))
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[450px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.ChooseRegisterType.title') }}
</h1>
<p class="!text-dominant-800 text-[11px] font-normal">
{{ $t('auth.ChooseRegisterType.subtitle') }}
</p>
</div>
<div class="flex flex-col gap-2 mt-6">
<UiButton
severity="secondary"
label="ثبت‌نام به عنوان سرپرست"
size="large"
full-width
outlined
height="40px"
icon-position="start"
icon-class="!text-sm mt-1"
@click="handleRegisterSupervisor"
/>
<UiButton
severity="secondary"
label="ثبت‌نام به عنوان دامپزشک"
size="large"
full-width
outlined
height="40px"
icon-position="start"
icon-class="!text-sm mt-1"
@click="handleRegisterVet"
/>
</div>
</main>
</AuthLayout>
</template>
@@ -0,0 +1,15 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue'
</script>
<template>
<AuthLayout>
<div class="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<h1 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
{{ $t('auth.forgot_password.title') }}
</h1>
</div>
</AuthLayout>
</template>
@@ -0,0 +1,78 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue'
import UiButton from '../../components/ui/UiButton.vue'
import UiInput from '../../components/ui/UiInput.vue'
import { Link } from '@inertiajs/inertia-vue3';
import { Inertia } from '@inertiajs/inertia'
import { ref } from 'vue';
import { inject } from 'vue';
import { toast } from 'vue3-toastify';
const route = inject('route');
const code = ref('');
const isLoading = ref(false);
const handleVerifyOtp = () => {
isLoading.value = true
Inertia.post(route('verifyOtp'), {
otp_code: code.value
}, {
onError: (errors) => {
toast.error(errors.otp_code || 'خطا در تایید کد تایید', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
},
onFinish: () => {
isLoading.value = false
}
})
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[450px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.OtpVerification.title').replace('%userType%', 'پزشکان') }}
</h1>
<p class="!text-dominant-800 mt-1 text-[11px] font-normal">
{{ $t('auth.OtpVerification.subtitle') }}
</p>
</div>
<div class="flex flex-col gap-2 mt-6">
<UiInput
prefix-end="کد تایید موقت"
type="text"
custom-class="!pl-3"
direction="ltr"
v-model="code"
/>
<UiButton
severity="dominant"
class="!mt-4"
:label="isLoading ? 'در حال تایید...' : 'تایید و بازیابی گذرواژه'"
size="large"
full-width
outlined
height="40px"
icon-position="start"
icon-class="!text-sm mt-1"
:loading="isLoading"
@click="handleVerifyOtp"
/>
</div>
</main>
<template #footer>
<Link class="text-center text-[13px] font-normal text-dominant-700 mt-6" :href="route('login')">
تغییر شماره موبایل
</Link>
</template>
</AuthLayout>
</template>
@@ -0,0 +1,89 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue'
import UiButton from '../../components/ui/UiButton.vue'
import UiInput from '../../components/ui/UiInput.vue'
import { Link } from '@inertiajs/inertia-vue3';
import { Inertia } from '@inertiajs/inertia';
import { ref } from 'vue'
import { toast } from 'vue3-toastify';
const props = defineProps({
phone: String,
level: String
})
const password = ref('');
const isLoading = ref(false)
const handleLogin = () => {
if (!password.value) {
toast.error('لطفاً گذرواژه را وارد کنید', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
return
}
isLoading.value = true
Inertia.post('/auth/login/authenticate', {
password: password.value
}, {
onSuccess: () => {
isLoading.value = false
},
onError: (errors) => {
isLoading.value = false
toast.error(errors.password || 'خطا در ورود')
}
})
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[450px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.Password.title').replace('%userType%', level === 'supervisor' ? 'سرپرست' : level === 'doctor' ? 'پزشک' : 'دانپزشک') }}
</h1>
<p class="!text-dominant-800 mt-1 text-[11px] font-normal">
{{ $t('auth.Password.subtitle') }}
</p>
</div>
<div class="flex flex-col gap-2 mt-6">
<UiInput
v-model="password"
prefix-end="گذرواژه"
type="password"
password-display-type="asterisks"
direction="ltr"
/>
<UiButton
severity="dominant"
class="!mt-4"
:label="isLoading ? 'در حال ورود...' : 'تایید و ورود به حساب'"
size="large"
full-width
outlined
height="40px"
icon-position="start"
icon-class="!text-sm mt-1"
:loading="isLoading"
@click="handleLogin"
/>
</div>
</main>
<template #footer>
<Link method="post" class="text-center text-[13px] font-normal text-dominant-700 mt-6" :href="route('sendOtp')">
رمز خود را فراموش کرده‌اید؟
</Link>
</template>
</AuthLayout>
</template>
+124
View File
@@ -0,0 +1,124 @@
<script setup>
import { ref } from 'vue'
import AuthLayout from '../../layouts/AuthLayout.vue';
import UiInputWithDropdown from '../../components/ui/UiInputWithDropdown.vue'
import UiButton from '../../components/ui/UiButton.vue'
import { countries } from '../../data/countries.js'
import { Inertia } from '@inertiajs/inertia'
import { inject } from 'vue';
import { convertPersianToEnglish } from '../../utils/numberUtils.js'
import { toast } from 'vue3-toastify'
const route = inject('route');
const selectedCountry = ref(countries[0])
const phoneNumber = ref('')
const isLoading = ref(false)
const handlePrefixChange = (country) => {
selectedCountry.value = country
}
const validatePhone = (phone) => {
const phoneRegex = /^[0-9۰-۹]{10,15}$/
return phoneRegex.test(phone.replace(/\s/g, ''))
}
const handleLogin = async () => {
if (!phoneNumber.value) {
toast.error('لطفاً شماره موبایل را وارد کنید', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
return
}
if (!validatePhone(phoneNumber.value)) {
toast.error('لطفاً شماره موبایل معتبر وارد کنید', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
return
}
isLoading.value = true
try {
let processedPhone = convertPersianToEnglish(phoneNumber.value)
if (processedPhone.startsWith('0')) {
processedPhone = processedPhone.substring(1)
}
Inertia.post(route('checkPhone'), {
prefix: selectedCountry.value.prefix,
phone: processedPhone
}, {
onSuccess: (page) => {
},
onError: (errors) => {
},
onFinish: () => {
isLoading.value = false
}
})
} catch (error) {
isLoading.value = false
}
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[450px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.phone.title') }}
</h1>
<p class="!text-dominant-800 text-[11px] font-normal">
{{ $t('auth.phone.subtitle') }}
</p>
</div>
<div class="my-6">
<UiInputWithDropdown
search-placeholder="Search Country Name or Phone Code ..."
dropdown-search-input-class="!text-dominant-200 !bg-dominant-50 !border-0 !pt-3 !font-normal !text-sm"
type="tel"
v-model="phoneNumber"
prefix-end="شماره موبایل"
dropdown-direction="ltr"
:prefix-options="countries"
:default-prefix-value="selectedCountry.value"
direction="ltr"
@prefix-change="handlePrefixChange"
/>
</div>
<UiButton
severity="dominant"
:label="isLoading ? 'در حال بررسی...' : 'ورود و ثبت‌نام'"
size="large"
class="!mt-4"
full-width
outlined
height="40px"
icon-position="start"
icon-class="!text-sm mt-1"
:loading="isLoading"
@click="handleLogin"
/>
</main>
<template #footer>
<p class="text-center text-[13px] font-normal text-dominant-500 mt-6">
Change Language to English
</p>
</template>
</AuthLayout>
</template>
@@ -0,0 +1,208 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue'
import UiButton from '../../components/ui/UiButton.vue'
import UiInput from '../../components/ui/UiInput.vue'
import UiInputWithDropdown from '../../components/ui/UiInputWithDropdown.vue'
import { Link } from '@inertiajs/inertia-vue3';
import { inject } from 'vue';
import { countries } from '../../data/countries.js'
import { ref, onMounted } from 'vue';
import UiSelect from '../../components/ui/UiSelect.vue';
import provinces from '../../data/provinces.json'
import cities from '../../data/cities.json'
import { Inertia } from '@inertiajs/inertia';
import { toast } from 'vue3-toastify';
const props = defineProps({
phone: String
})
const errors = ref([])
const isLoading = ref(false)
const route = inject('route');
const selectedCountry = ref(countries[0])
const handlePrefixChange = (country) => {
selectedCountry.value = country
}
const selectedCity = ref(null)
const selectedProvince = ref(null)
const citiesList = ref([])
const provincesList = ref(provinces);
const formData = ref({
fullName: '',
nationalCode: '',
phone: props.phone || '',
address: '',
province: '',
city: '',
address: '',
level: 'سرپرست',
})
const handleProvinceChange = (province) => {
if (province) {
const provinceId = province.id
const filteredCities = cities.filter(city => city.province_id === provinceId)
citiesList.value = filteredCities
} else {
citiesList.value = []
}
selectedCity.value = null
}
onMounted(() => {
if (props.phone) {
if (props.phone.startsWith('+')) {
const match = countries.find(c => props.phone.startsWith(c.prefix))
if (match) {
selectedCountry.value = match
formData.value.phone = props.phone.replace(match.prefix, '')
} else {
formData.value.phone = props.phone
}
} else {
formData.value.phone = props.phone
}
}
})
const handleSubmit = () => {
if (!selectedProvince.value) {
toast.error('لطفاً استان را انتخاب کنید')
return
}
if (!selectedCity.value) {
toast.error('لطفاً شهر را انتخاب کنید')
return
}
if (!formData.value.nationalCode) {
toast.error('لطفاً کد ملی را وارد کنید')
return
}
isLoading.value = true
const fullPhone = selectedCountry.value.prefix + formData.value.phone
const submitData = {
fullName: formData.value.fullName,
national_code: formData.value.nationalCode,
phone: fullPhone,
province: selectedProvince.value.name,
city: selectedCity.value.name,
address: formData.value.address,
level: formData.value.level,
}
Inertia.post(route('register.store'), submitData, {
onSuccess: () => {
},
onError: (errors) => {
errors.value = errors
isLoading.value = false
},
onFinish: () => {
isLoading.value = false
}
})
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[700px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.RegisterSupervisor.title').replace('%userType%', 'سرپرستان') }}
</h1>
<p class="!text-dominant-800 mt-1 text-[11px] font-normal">
{{ $t('auth.RegisterSupervisor.subtitle') }}
</p>
</div>
<div class="grid grid-cols-2 gap-2 content-start items-start mt-6">
<UiInput
prefix-start="نام و نام خانوادگی"
type="text"
class="!my-0 max-sm:!col-span-2"
v-model="formData.fullName"
/>
<UiInput
prefix-end="کد ملی"
type="text"
convert-persian-numbers
direction="ltr"
class="!my-0 max-sm:!col-span-2"
custom-class="!font-yekanBakh !text-sm"
v-model="formData.nationalCode"
:error="errors.national_code"
/>
<UiInputWithDropdown
search-placeholder="Search Country Name or Phone Code ..."
dropdown-search-input-class="!text-dominant-200 !bg-dominant-50 !border-0 !pt-3 !font-normal !text-sm"
type="tel"
class="!my-0 !col-span-2"
prefix-end="شماره موبایل"
dropdown-direction="ltr"
:prefix-options="countries"
:default-prefix-value="selectedCountry.value"
direction="ltr"
v-model="formData.phone"
@prefix-change="handlePrefixChange"
/>
<UiSelect
v-model="selectedProvince"
:options="provincesList"
option-label="name"
option-value="id"
prefix-start="استان"
:filter="true"
filter-placeholder="جستجو در استانها..."
:show-clear="false"
class="!my-0 max-sm:col-span-2"
@change="handleProvinceChange"
/>
<UiSelect
v-model="selectedCity"
:options="citiesList"
option-label="name"
option-value="id"
prefix-start="شهر"
:filter="true"
class="!my-0 max-sm:col-span-2"
filter-placeholder="جستجو در شهرها..."
:show-clear="false"
/>
<UiInput
prefix-start="آدرس"
type="text"
class="!my-0 col-span-2"
v-model="formData.address"
/>
<UiButton
severity="dominant"
:label="isLoading ? 'در حال ثبت...' : 'تایید اطلاعات و ثبت‌نام'"
size="large"
full-width
outlined
class="col-span-2 mt-4 max-sm:!mt-0"
height="40px"
icon-position="start"
icon-class="!text-sm"
:loading="isLoading"
@click="handleSubmit"
/>
</div>
</main>
<template #footer>
<Link class="text-center text-[13px] font-normal text-dominant-700 mt-6" :href="route('register.chooseType')">
بازگشت
</Link>
</template>
</AuthLayout>
</template>
@@ -0,0 +1,253 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue'
import UiButton from '../../components/ui/UiButton.vue'
import UiInput from '../../components/ui/UiInput.vue'
import { Link } from '@inertiajs/inertia-vue3';
import provinces from '../../data/provinces.json'
import cities from '../../data/cities.json'
import UiInputWithDropdown from '../../components/ui/UiInputWithDropdown.vue';
import UiSelect from '../../components/ui/UiSelect.vue';
import { inject } from 'vue';
import { ref, onMounted } from 'vue';
import { countries } from '../../data/countries.js'
import { Inertia } from '@inertiajs/inertia';
import { toast } from 'vue3-toastify';
const props = defineProps({
phone: String
})
const route = inject('route');
const selectedCountry = ref(countries[0])
const handlePrefixChange = (country) => {
selectedCountry.value = country
}
const selectedSpecialty = ref(null)
const selectedCity = ref(null)
const selectedProvince = ref(null)
const citiesList = ref([])
const provincesList = ref(provinces)
const specialtiesList = ref([
{ id: 1, name: 'جراح عمومی' },
{ id: 2, name: 'جراح حیوانات' },
{ id: 3, name: 'جراح حیوانات و طبیعت' }
])
const formData = ref({
fullName: '',
nationalCode: '',
phone: props.phone || '',
clinicName: '',
clinicPhone: '',
address: '',
specialty: '',
province: '',
city: '',
address: '',
level: 'دانپزشک',
})
const errors = ref({})
const isLoading = ref(false)
const handleProvinceChange = (province) => {
if (province) {
const provinceId = province.id
const filteredCities = cities.filter(city => city.province_id === provinceId)
citiesList.value = filteredCities
} else {
citiesList.value = []
}
selectedCity.value = null
}
onMounted(() => {
if (props.phone) {
if (props.phone.startsWith('+')) {
const match = countries.find(c => props.phone.startsWith(c.prefix))
if (match) {
selectedCountry.value = match
formData.value.phone = props.phone.replace(match.prefix, '')
} else {
formData.value.phone = props.phone
}
} else {
formData.value.phone = props.phone
}
}
})
const handleSubmit = () => {
if (!selectedSpecialty.value) {
toast.error('لطفاً تخصص را انتخاب کنید')
return
}
if (!selectedProvince.value) {
toast.error('لطفاً استان را انتخاب کنید')
return
}
if (!selectedCity.value) {
toast.error('لطفاً شهر را انتخاب کنید')
return
}
if (!formData.value.nationalCode) {
toast.error('لطفاً کد ملی را وارد کنید')
return
}
isLoading.value = true
const fullPhone = selectedCountry.value.prefix + formData.value.phone
const submitData = {
fullName: formData.value.fullName,
national_code: formData.value.nationalCode,
specialty: selectedSpecialty.value.name,
phone: fullPhone,
clinic_name: formData.value.clinicName,
clinic_phone: formData.value.clinicPhone,
province: selectedProvince.value.name,
city: selectedCity.value.name,
address: formData.value.address,
level: 'دانپزشک'
}
Inertia.post(route('register.store'), submitData, {
onSuccess: () => {
},
onError: (errors) => {
errors.value = errors
isLoading.value = false
},
onFinish: () => {
isLoading.value = false
}
})
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[700px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.RegisterVet.title').replace('%userType%', 'پزشکان') }}
</h1>
<p class="!text-dominant-800 mt-1 text-[11px] font-normal">
{{ $t('auth.RegisterVet.subtitle') }}
</p>
</div>
<div class="grid grid-cols-2 gap-2 content-start items-start mt-6">
<UiInput
prefix-start="نام و نام خانوادگی"
type="text"
class="!my-0 max-sm:!col-span-2"
v-model="formData.fullName"
:error="errors.first_name"
/>
<UiInput
prefix-end="کد ملی"
type="text"
convert-persian-numbers
class="!my-0 max-sm:!col-span-2"
direction="ltr"
custom-class="!font-yekanBakh !text-sm"
v-model="formData.nationalCode"
:error="errors.national_code"
/>
<UiSelect
v-model="selectedSpecialty"
:options="specialtiesList"
option-label="name"
option-value="id"
prefix-start="تخصص"
class="!my-0 max-sm:!col-span-2"
:filter="true"
filter-placeholder="جستجو در تخصصها..."
:show-clear="false"
/>
<UiInputWithDropdown
search-placeholder="Search Country Name or Phone Code ..."
dropdown-search-input-class="!text-dominant-200 !bg-dominant-50 !border-0 !pt-3 !font-normal !text-sm"
type="tel"
class="!my-0 max-sm:!col-span-2"
prefix-end="شماره موبایل"
dropdown-direction="ltr"
v-model="formData.phone"
:prefix-options="countries"
:default-prefix-value="selectedCountry.value"
direction="ltr"
@prefix-change="handlePrefixChange"
/>
<UiInput
prefix-start="نام کلینیک"
class="!mb-0 max-sm:!col-span-2"
type="text"
custom-class="!font-yekanBakh !text-sm"
v-model="formData.clinicName"
/>
<UiInput
prefix-end="تلفن کلینیک"
type="text"
direction="ltr"
class="!mb-0 max-sm:!col-span-2"
convert-persian-numbers
v-model="formData.clinicPhone"
/>
<UiSelect
v-model="selectedProvince"
:options="provincesList"
option-label="name"
option-value="id"
prefix-start="استان"
:filter="true"
filter-placeholder="جستجو در استانها..."
:show-clear="false"
class="!my-0"
@change="handleProvinceChange"
/>
<UiSelect
v-model="selectedCity"
:options="citiesList"
option-label="name"
option-value="id"
prefix-start="شهر"
class="!my-0 "
:filter="true"
filter-placeholder="جستجو در شهرها..."
:show-clear="false"
/>
<UiInput
prefix-start="آدرس"
type="text"
class="col-span-2 !mb-0"
v-model="formData.address"
/>
<UiButton
severity="dominant"
:label="isLoading ? 'در حال ثبت...' : 'تایید اطلاعات و ثبت‌نام'"
size="large"
full-width
outlined
class="col-span-2 mt-4 max-sm:!mt-0"
height="40px"
icon-position="start"
icon-class="!text-sm"
:loading="isLoading"
@click="handleSubmit"
/>
</div>
</main>
<template #footer>
<Link class="text-center text-[13px] font-normal text-dominant-700 mt-6" :href="route('register.chooseType')">
بازگشت
</Link>
</template>
</AuthLayout>
</template>
@@ -0,0 +1,110 @@
<script setup>
import AuthLayout from '../../layouts/AuthLayout.vue'
import UiButton from '../../components/ui/UiButton.vue'
import UiInput from '../../components/ui/UiInput.vue'
import { Link } from '@inertiajs/inertia-vue3';
import { ref } from 'vue';
import { Inertia } from '@inertiajs/inertia';
import { inject } from 'vue';
import { toast } from 'vue3-toastify';
const route = inject('route');
const isLoading = ref(false);
const password = ref('');
const confirmPassword = ref('');
const updatePassword = () => {
if (!password.value) {
toast.error('لطفاً گذرواژه را وارد کنید', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
return
}
if (!confirmPassword.value) {
toast.error('لطفاً تکرار گذرواژه را وارد کنید', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
return
}
if (password.value !== confirmPassword.value) {
toast.error('گذرواژه و تکرار گذرواژه مطابقت ندارند', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
return
}
isLoading.value = true
Inertia.post(route('updatePassword'), { password: password.value, password_confirmation: confirmPassword.value }, {
onError: (errors) => {
toast.error(errors.password || 'خطا در تغییر گذرواژه', {
position: 'top-right',
rtl: true,
theme: 'colored',
autoClose: 3000,
})
},
onFinish: () => {
isLoading.value = false
}
})
}
</script>
<template>
<AuthLayout>
<main class="p-9 h-auto w-[450px] max-sm:w-[90%] m-auto bg-white rounded-xl">
<div class="grid grid-cols-a justify-start">
<h1 class="text-[16px] font-semibold !text-dominant-800">
{{ $t('auth.ResetPassword.title').replace('%userType%', 'پزشکان') }}
</h1>
<p class="!text-dominant-800 mt-1 text-[11px] font-normal">
{{ $t('auth.ResetPassword.subtitle') }}
</p>
</div>
<div class="flex content-start flex-col mt-6">
<UiInput
prefix-end="گذرواژه"
type="password"
password-display-type="asterisks"
direction="ltr"
class="!mb-2"
v-model="password"
/>
<UiInput
prefix-end="تکرار گذرواژه"
type="password"
password-display-type="asterisks"
direction="ltr"
v-model="confirmPassword"
/>
<UiButton
severity="dominant"
class="!mt-4"
label="تایید و بازیابی گذرواژه"
size="large"
full-width
outlined
height="40px"
icon-position="start"
icon-class="!text-sm"
:loading="isLoading"
@click="updatePassword"
/>
</div>
</main>
<template #footer>
<Link :href="route('login')" class="text-center text-[13px] font-normal text-dominant-700 mt-6">
بازگشت به صفحه ورود
</Link>
</template>
</AuthLayout>
</template>
@@ -0,0 +1,11 @@
<script setup>
import ClientLayout from '../../../layouts/ClientLayout.vue'
</script>
<template>
<ClientLayout>
<h1 class="text-2xl font-bold">
Cases
</h1>
</ClientLayout>
</template>
@@ -0,0 +1,142 @@
<script setup>
import ClientLayout from '../../layouts/ClientLayout.vue'
import ActivitiesWidget from '../../components/dashboard/ActivitiesWidget.vue'
import ActivitiesChart from '../../components/dashboard/ActivitiesChart.vue'
import TableGeneric from '../../components/ui/TableGeneric.vue'
import UiButton from '../../components/ui/UiButton.vue'
import EyeSvg from '../../components/svg/eye.svg'
import UiCard from '../../components/ui/UiCard.vue'
import RequestCard from '../../components/dashboard/RequestCard.vue'
import { ref } from 'vue'
import { Link } from '@inertiajs/inertia-vue3'
defineProps({
user: {
type: Object,
required: true
}
})
const data = ref([
{
id: 1,
file_number: '1234567890',
status: 'مشاهده نشد',
request_type: 'CT',
requester: 'محراب اسماعیل‌نیا',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'اتمام فرصت رسیدگی'
},
{
id: 2,
file_number: '1234567891',
status: 'مشاهده نشد',
request_type: 'CT',
requester: 'رضا گلیان',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: '۵۳ دقیقه'
},
{
id: 3,
file_number: '1234567892',
status: 'مشاهده نشد',
request_type: 'MRI',
requester: 'دکتر مریم مهدی‌پور',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۳۲ ساعت'
},
{
id: 4,
file_number: '1234567893',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'دکتر مریم مهدی‌پور',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۲۴ ساعت'
},
{
id: 5,
file_number: '1234567894',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'نام شخص و یا پزشک',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۵ ساعت'
}
])
const columns = ref([
{
title: 'شماره پرونده',
key: 'file_number'
},
{
title: 'وضعیت',
key: 'status'
},
{
title: 'نوع درخواست',
key: 'request_type'
},
{
title: 'درخواست‌کننده',
key: 'requester'
},
{
title: 'تاریخ ثبت',
key: 'registration_date'
},
{
title: 'فرصت رسیدگی',
key: 'processing_deadline'
},
{
title: '',
key: 'actions',
width: 80,
style: 'grid grid-cols-1',
slot: true,
}
])
</script>
<template>
<ClientLayout>
<ActivitiesWidget />
<UiCard title="ثبت آنی درخواست جدید" variant="default" class="mt-4">
<p class="w-full flex items-center justify-start font-[400] text-[11px]">فقط چند قدم ساده برای ثبت درخواست کافیه.</p>
<div class="w-full h-[179px] grid grid-cols-5 gap-3 justify-start pt-[10px]">
<RequestCard title="CT Scan" icon="ct-scan" severity="primary"/>
<RequestCard title="MRI" icon="mri" severity="primary"/>
<RequestCard title="سونوگرافی" icon="sonography" severity="primary"/>
<RequestCard title="اکوکاردیوگرافی" icon="echo" severity="primary"/>
<RequestCard title="رادیولوژی" icon="radiology" severity="primary"/>
</div>
</UiCard>
<UiCard title="آخرین کیس‌های در انتظار بررسی" variant="default" class="mt-4">
<TableGeneric
class="bg-white !rounded-2xl"
:data="data"
customStyle="!text-[11px] !font-normal"
:searchable="false"
:columns="columns"
:selectable="false"
:pagination="null"
:model-value="[]"
>
<template #column-actions="{ data }" class="">
<div class="flex items-center justify-end">
<Link to="/dashboard/doctor/cases/1"
class="text-primary-500 bg-complement-100 flex items-center justify-end p-1.5 rounded-md"
>
<EyeSvg class="w-4 h-4 text-primary-500" />
</Link>
</div>
</template>
</TableGeneric>
</UiCard>
<UiCard title="خلاصه عملکرد شما" variant="default" class="mt-4">
<ActivitiesChart />
</UiCard>
</ClientLayout>
</template>
@@ -0,0 +1,185 @@
<script setup>
import ClientLayout from '../../../layouts/ClientLayout.vue'
import UiCard from '../../../components/ui/UiCard.vue'
import TableGeneric from '../../../components/ui/TableGeneric.vue'
import EyeSvg from '../../../components/svg/eye.svg'
import { Link } from '@inertiajs/inertia-vue3'
import { ref } from 'vue'
import { useDocumentStore } from '../../../store/Document'
const documentStore = useDocumentStore();
const selectedItems = ref([])
const data = ref([
{
id: 1,
file_number: '1234567890',
status: 'مشاهده نشد',
request_type: 'CT',
requester: 'محراب اسماعیل‌نیا',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'اتمام فرصت رسیدگی'
},
{
id: 2,
file_number: '1234567891',
status: 'مشاهده نشد',
request_type: 'CT',
requester: 'رضا گلیان',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: '۵۳ دقیقه'
},
{
id: 3,
file_number: '1234567892',
status: 'مشاهده نشد',
request_type: 'MRI',
requester: 'دکتر مریم مهدی‌پور',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۳۲ ساعت'
},
{
id: 4,
file_number: '1234567893',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'دکتر مریم مهدی‌پور',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۲۴ ساعت'
},
{
id: 5,
file_number: '1234567894',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'نام شخص و یا پزشک',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۵ ساعت'
},
{
id: 6,
file_number: '1234567893',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'دکتر مریم مهدی‌پور',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۲۴ ساعت'
},
{
id: 7,
file_number: '1234567894',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'نام شخص و یا پزشک',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۵ ساعت'
},
{
id: 8,
file_number: '1234567893',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'دکتر مریم مهدی‌پور',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۲۴ ساعت'
},
{
id: 9,
file_number: '1234567894',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'نام شخص و یا پزشک',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۵ ساعت'
},
{
id: 7,
file_number: '1234567894',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'نام شخص و یا پزشک',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۵ ساعت'
},
{
id: 8,
file_number: '1234567893',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'دکتر مریم مهدی‌پور',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۲۴ ساعت'
},
{
id: 9,
file_number: '1234567894',
status: 'در انتظار ثبت گزارش',
request_type: 'MRI',
requester: 'نام شخص و یا پزشک',
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
processing_deadline: 'کمتر از ۵ ساعت'
}
])
const columns = ref([
{
title: 'شماره پرونده',
key: 'file_number'
},
{
title: 'وضعیت',
key: 'status'
},
{
title: 'نوع درخواست',
key: 'request_type'
},
{
title: 'درخواست‌کننده',
key: 'requester'
},
{
title: 'تاریخ ثبت',
key: 'registration_date'
},
{
title: 'فرصت رسیدگی',
key: 'processing_deadline'
},
{
title: '',
key: 'actions',
width: 80,
style: 'grid grid-cols-1',
slot: true,
},
])
</script>
<template>
<ClientLayout>
<UiCard title="کیس‌های در انتظار بررسی" variant="default" class="">
<TableGeneric
class="bg-white !rounded-2xl"
:data="data"
customStyle="!text-[11px] !font-normal"
:searchable="false"
:columns="columns"
:selectable="false"
:pagination="null"
:model-value="selectedItems"
>
<template #column-actions="{ data }" class="">
<div class="flex items-center justify-end">
<Link :href="documentStore.endpoints.show(data.id)"
class="text-primary-500 bg-complement-100 flex items-center justify-end p-1.5 rounded-md"
>
<EyeSvg class="w-4 h-4 text-primary-500" />
</Link>
</div>
</template>
</TableGeneric>
</UiCard>
</ClientLayout>
</template>
@@ -0,0 +1,26 @@
<script setup>
import ClientLayout from '../../../layouts/ClientLayout.vue';
const props = defineProps({
document: {
type: Object,
required: true,
}
});
</script>
<template>
<ClientLayout>
<h1>{{ document.title }}</h1>
<p>{{ document.description }}</p>
<p>{{ document.created_at }}</p>
<p>{{ document.updated_at }}</p>
<p>{{ document.status }}</p>
<p>{{ document.priority }}</p>
<p>{{ document.note }}</p>
<p>{{ document.modality }}</p>
<p>{{ document.pet_id }}</p>
<p>{{ document.requester_id }}</p>
</ClientLayout>
</template>
@@ -0,0 +1,11 @@
<script setup>
import ClientLayout from '../../../layouts/ClientLayout.vue'
</script>
<template>
<ClientLayout>
<h1 class="text-2xl font-bold">
Reports
</h1>
</ClientLayout>
</template>
@@ -0,0 +1,11 @@
<script setup>
import ClientLayout from '../../../layouts/ClientLayout.vue'
</script>
<template>
<ClientLayout>
<h1 class="text-2xl font-bold">
Shifts
</h1>
</ClientLayout>
</template>
+39
View File
@@ -0,0 +1,39 @@
<script setup>
import { Link } from '@inertiajs/inertia-vue3'
import { usePage } from '@inertiajs/inertia-vue3'
const {url ,props} = usePage();
const user = props.value.auth.User || null;
</script>
<template>
<div class="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-2xl w-full space-y-8">
<h1 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
پروژه رادیکال
</h1>
<div class="bg-white p-6 rounded-lg space-y-6">
<p>
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد.
</p>
</div>
<div class="flex items-center justify-center gap-4" v-if="!user">
<Link :href="route('login')" class="text-center bg-primary text-white px-4 py-2 rounded-md hover:bg-primary-active transition-all duration-300">
ورود
</Link>
<Link :href="route('login')" class="text-center bg-primary text-white px-4 py-2 rounded-md hover:bg-primary-active transition-all duration-300">
ثبت نام
</Link>
</div>
<div class="flex items-center justify-center gap-4" v-if="user">
<a :href="route('dashboard.index')" class="text-center bg-primary text-white px-4 py-2 rounded-md hover:bg-primary-active transition-all duration-300">
داشبورد
</a>
</div>
</div>
</div>
</template>