complete Complete User Request Submission section RAD-1
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<script setup>
|
||||
import {Link} from '@inertiajs/inertia-vue3';
|
||||
import {useAuthStore} from "../../store/Auth.js";
|
||||
|
||||
const AuthStore = useAuthStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-white px-6 h-[100vh] flex items-center">
|
||||
<div class="p-6 bg-white rounded-3xl shadow-all w-full xl:!w-1/3 mx-auto">
|
||||
<div class="text-center mb-8">
|
||||
<div class="text-client-Fontcolor text-2xl font-extrabold mb-4">ورود به حساب کاربری</div>
|
||||
<span class="text-client-Fontcolor text-xs font-black leading-normal">قبلا حساب ساختی؟</span>
|
||||
<Link href="/auth/register" class="font-medium no-underline mr-2 text-xs text-client-Fontcolor cursor-pointer">ایجاد حساب جدید</Link>
|
||||
</div>
|
||||
<div class="text-white grid pb-6 grid-cols-2 gap-2">
|
||||
<a class="border-[1px] bg-[#4875e9] flex justify-between px-4 items-center rounded-lg h-11">
|
||||
<span class="text-sm font-normal">ورود با گوگل</span>
|
||||
<i class="fi fi-brands-google flex"></i>
|
||||
</a>
|
||||
<a class="border-[1px] flex items-center px-4 justify-between bg-client-sidebar-background rounded-lg h-11">
|
||||
<span class="text-sm font-normal">ورود با گیتهاب</span>
|
||||
<i class="fi fi-brands-github flex"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-[1fr_auto_1fr] gap-2 h-10 mb-6 items-center">
|
||||
<div class="h-[1px] bg-client-gray/10"></div>
|
||||
<div class="text-client-Fontcolor/90 px-4">یا</div>
|
||||
<div class="h-[1px] bg-client-gray/10"></div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<FloatLabel variant="on"
|
||||
class="!w-full">
|
||||
<InputText id="email1" type="text"
|
||||
appendTo="self"
|
||||
v-model="AuthStore.loginForm.email"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !text-client-Fontcolor w-full"/>
|
||||
<label for="email1" class="!text-client-Fontcolor !text-sm !bg-white font-light">ایمیل</label>
|
||||
</FloatLabel>
|
||||
<Message v-if="AuthStore.errorsLoginForm.email"
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small">{{AuthStore.errorsLoginForm.email[0]}}</Message>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FloatLabel variant="on"
|
||||
class="!w-full">
|
||||
<Password id="password" type="password" :feedback="false"
|
||||
appendTo="self" toggleMask v-model="AuthStore.loginForm.password"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !text-client-Fontcolor "/>
|
||||
<label for="on_label" class="!text-client-Fontcolor !bg-white !text-sm font-light">کلمه عبور</label>
|
||||
</FloatLabel>
|
||||
<Message v-if="AuthStore.errorsLoginForm.password"
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small">{{AuthStore.errorsLoginForm.password[0]}}</Message>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2a items-center justify-start mb-12">
|
||||
<div class="flex items-center gap-2 !text-client-Fontcolor">
|
||||
<Checkbox inputId="remember1"
|
||||
class="custom-checkbox"
|
||||
v-model="AuthStore.loginForm.remember"
|
||||
binary />
|
||||
<label class="!text-sm cursor-pointer !font-normal" for="remember1">مرا به خاطر بسپار</label>
|
||||
</div>
|
||||
</div>
|
||||
<Button @click="AuthStore.login()" :loading="AuthStore.loaderLoginForm" label="ورود به سایت" icon="pi pi-user !text-md flex !leading-none" severity="secondary"
|
||||
class="w-1/2 m-auto flex flex-row-reverse items-center !font-normal" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="css">
|
||||
.p-password{
|
||||
@apply !w-full
|
||||
}
|
||||
.p-password-input{
|
||||
@apply !w-full
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import {Link} from "@inertiajs/inertia-vue3";
|
||||
import {useAuthStore} from "../../store/Auth";
|
||||
|
||||
const AuthStore = useAuthStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-white px-6 h-[100vh] flex items-center">
|
||||
<div class="p-6 bg-white rounded-3xl shadow-all w-full xl:!w-1/3 m-auto">
|
||||
<div class="text-center py-4 mb-8">
|
||||
<div class="text-client-Fontcolor text-2xl font-extrabold mb-4">عضویت در سایت</div>
|
||||
<span class="text-client-Fontcolor text-xs font-black leading-normal">قبلا حساب ساختی؟</span>
|
||||
<Link href="/auth/login" class="font-medium no-underline mr-2 text-xs text-client-Fontcolor cursor-pointer">ورود به سایت</Link>
|
||||
</div>
|
||||
<div class="text-white grid pb-6 grid-cols-2 gap-2">
|
||||
<a class="border-[1px] bg-[#4875e9] flex justify-between px-4 items-center rounded-lg h-11">
|
||||
<span class="text-sm font-normal">ورود با گوگل</span>
|
||||
<i class="pi pi-google"></i>
|
||||
</a>
|
||||
<a class="border-[1px] flex items-center px-4 justify-between bg-client-sidebar-background rounded-lg h-11">
|
||||
<span class="text-sm font-normal">ورود با گیتهاب</span>
|
||||
<i class="pi pi-github"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-[1fr_auto_1fr] gap-2 h-10 mb-6 items-center ">
|
||||
<div class="h-[1px] bg-client-gray/10"></div>
|
||||
<div class="text-client-Fontcolor/90 px-4">یا</div>
|
||||
<div class="h-[1px] bg-client-gray/10"></div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<FloatLabel variant="on"
|
||||
class="!w-full">
|
||||
<InputText id="username" type="text"
|
||||
appendTo="self"
|
||||
v-model="AuthStore.registerForm.name"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !border-gray-500/30 !text-client-Fontcolor w-full"/>
|
||||
<label for="username" class="!text-client-Fontcolor !text-sm !bg-white font-light">نام کاربری</label>
|
||||
</FloatLabel>
|
||||
<Message v-if="AuthStore.errorsRegisterForm.name"
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small">{{AuthStore.errorsRegisterForm.name[0]}}</Message>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FloatLabel variant="on"
|
||||
class="!w-full">
|
||||
<InputText id="email1" type="text"
|
||||
appendTo="self"
|
||||
v-model="AuthStore.registerForm.email"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !border-gray-500/30 !text-client-Fontcolor w-full"/>
|
||||
<label for="email1" class="!text-client-Fontcolor !text-sm !bg-white font-light">ایمیل</label>
|
||||
</FloatLabel>
|
||||
<Message v-if="AuthStore.errorsRegisterForm.email"
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small">{{AuthStore.errorsRegisterForm.email[0]}}</Message>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FloatLabel variant="on" class="!w-full">
|
||||
<Password id="password" type="password" :feedback="false"
|
||||
appendTo="self" toggleMask
|
||||
v-model="AuthStore.registerForm.password"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !border-gray-500/20 !text-client-Fontcolor w-full"/>
|
||||
<label for="on_label" class="!text-client-Fontcolor !bg-white !text-sm font-light">کلمه عبور</label>
|
||||
</FloatLabel>
|
||||
<Message v-if="AuthStore.errorsRegisterForm.password"
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small">{{AuthStore.errorsRegisterForm.password[0]}}</Message>
|
||||
</div>
|
||||
|
||||
|
||||
<FloatLabel variant="on" class="!w-full mb-6">
|
||||
<Password id="password_confirmation" type="password" :feedback="false"
|
||||
appendTo="self" toggleMask
|
||||
v-model="AuthStore.registerForm.password_confirmation"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !border-gray-500/20 !text-client-Fontcolor w-full"/>
|
||||
<label for="password_confirmation" class="!text-client-Fontcolor !bg-white !text-sm font-light">تکرار کلمه عبور</label>
|
||||
</FloatLabel>
|
||||
|
||||
|
||||
<Button label="عضویت در سایت" @click="AuthStore.register()" :loading="AuthStore.loaderRegisterForm" icon="pi pi-user !text-md flex !leading-none" severity="secondary"
|
||||
class="w-1/2 m-auto flex flex-row-reverse items-center !font-normal" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="css">
|
||||
.p-password{
|
||||
@apply !w-full
|
||||
}
|
||||
.p-password-input{
|
||||
@apply !w-full
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,194 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import {onMounted , onUnmounted} from 'vue';
|
||||
import {useDocumentStore} from "../../store/Document.js";
|
||||
import {Link} from "@inertiajs/inertia-vue3";
|
||||
import GlobalSelectMedium from "../../components/medium/GlobalSelectMedium.vue";
|
||||
import Card from "../../components/ui/Card.vue";
|
||||
import UISelect from "../../components/ui/UISelect.vue";
|
||||
|
||||
const documentStore = useDocumentStore();
|
||||
|
||||
const props = defineProps({
|
||||
is_edit: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
document: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
pets: {
|
||||
type: Object,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
documentStore.petOptions = props.pets;
|
||||
if (props.is_edit) {
|
||||
documentStore.setEditData(props.document);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
documentStore.resetCreateForm();
|
||||
documentStore.is_edit_form = false;
|
||||
documentStore.errorsCreateForm = [];
|
||||
})
|
||||
|
||||
const removePrimaryFile = (index) => {
|
||||
documentStore.createForm.primary_files.splice(index, 1);
|
||||
}
|
||||
|
||||
const removeHistoryFile = (index) => {
|
||||
documentStore.createForm.history_files.splice(index, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<Card class="mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-[1fr_auto] justify-between items-center gap-4">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ documentStore.pageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="documentStore.endpoints.index">
|
||||
<span class="text-sm">{{ documentStore.indexPageTitle }}</span>
|
||||
<i class="fi fi-rr-list flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<section class="grid w-full grid-cols-1 lg:grid-cols-[1fr_22rem] gap-4">
|
||||
<Card>
|
||||
<div class="rounded-2xl bg-white">
|
||||
<Form ref="formRef" v-slot="$form" class=" p-4 grid grid-cols-2 gap-4 !w-full">
|
||||
<FormField v-slot="$field" name="modality" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نوع درخواست</label>
|
||||
<UISelect
|
||||
placeholder="انتخاب نوع درخواست"
|
||||
v-model="documentStore.createForm.modality"
|
||||
:options="documentStore.modalityOptions"
|
||||
optionValue="value"
|
||||
optionLabel="label"
|
||||
/>
|
||||
<Message v-if="documentStore.errorsCreateForm?.modality" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.modality[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="priority" initialValue="" class="flex flex-col gap-1">
|
||||
<label>اولویت</label>
|
||||
<UISelect
|
||||
placeholder="انتخاب اولویت"
|
||||
v-model="documentStore.createForm.priority"
|
||||
:options="documentStore.priorityOptions"
|
||||
optionValue="value"
|
||||
optionLabel="label"
|
||||
/>
|
||||
<Message v-if="documentStore.errorsCreateForm?.priority" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.priority[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="status" initialValue="" class="flex flex-col gap-1">
|
||||
<label>وضعیت</label>
|
||||
<UISelect
|
||||
placeholder="انتخاب وضعیت"
|
||||
v-model="documentStore.createForm.status"
|
||||
:options="documentStore.statusOptions"
|
||||
optionValue="value"
|
||||
optionLabel="label"
|
||||
/>
|
||||
<Message v-if="documentStore.errorsCreateForm?.status" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.status[0] }}</Message>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="$field" name="pet_id" initialValue="" class="flex flex-col gap-1">
|
||||
<label>انتخاب کیس</label>
|
||||
<UISelect
|
||||
placeholder="انتخاب کیس"
|
||||
v-model="documentStore.createForm.pet_id"
|
||||
:options="documentStore.petOptions"
|
||||
optionValue="value"
|
||||
optionLabel="label"
|
||||
:filter="true"
|
||||
searchUrl="/admin/pets-options"
|
||||
searchParam="search"
|
||||
@update:options="documentStore.petOptions = $event"
|
||||
/>
|
||||
<Message v-if="documentStore.errorsCreateForm?.pet_id" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.pet_id[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="submitted_at" initialValue="" class="flex flex-col gap-1">
|
||||
<label>تاریخ ثبت</label>
|
||||
<CustomDatePicker v-model="documentStore.createForm.submitted_at" class="!text-xs" type="datetime" compact-time></CustomDatePicker>
|
||||
<Message v-if="documentStore.errorsCreateForm?.submitted_at" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.submitted_at[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="processing_deadline" initialValue="" class="flex flex-col gap-1">
|
||||
<label>مهلت پردازش</label>
|
||||
<CustomDatePicker v-model="documentStore.createForm.processing_deadline" class="!text-xs" type="datetime" compact-time></CustomDatePicker>
|
||||
<Message v-if="documentStore.errorsCreateForm?.processing_deadline" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.processing_deadline[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="note" initialValue="" class="flex flex-col col-span-2 gap-1">
|
||||
<label>متن درخواست</label>
|
||||
<Textarea type="text" class="placeholder:!text-sm" v-model="documentStore.createForm.note" placeholder="متن درخواست را وارد کنید" />
|
||||
</FormField>
|
||||
<div class="grid grid-cols-2 col-span-2 gap-4">
|
||||
<Card class="mt-4">
|
||||
<div class="p-4 rounded-2xl pb-8">
|
||||
<h5>فایلهای اصلی</h5>
|
||||
<global-select-medium v-model="documentStore.createForm.primary_files" :multiple="true" @remove-item="removePrimaryFile" upload-endpoint="/admin/documents-upload"></global-select-medium>
|
||||
<Message v-if="documentStore.errorsCreateForm?.primary_files" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.primary_files[0] }}</Message>
|
||||
|
||||
</div>
|
||||
</Card>
|
||||
<Card class="mt-4">
|
||||
<div class="p-4 rounded-2xl pb-8">
|
||||
<h5>فایلهای سابقه</h5>
|
||||
<global-select-medium v-model="documentStore.createForm.history_files" :multiple="true" @remove-item="removeHistoryFile" upload-endpoint="/admin/documents-upload"></global-select-medium>
|
||||
<Message v-if="documentStore.errorsCreateForm?.history_files" severity="error" size="small" variant="simple">{{ documentStore.errorsCreateForm?.history_files[0] }}</Message>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</Card>
|
||||
<div class="gap-4">
|
||||
<Card class="grid grid-cols-1 content-start justify-start ">
|
||||
<div class="p-4 grid grid-cols-1 gap-4 rounded-2xl">
|
||||
<div class="grid grid-cols-1 justify-between items-center">
|
||||
<span>زمان انتشار:</span>
|
||||
<CustomDatePicker v-model="documentStore.createForm.datetime" class="!text-xs mt-2" type="datetime" compact-time></CustomDatePicker>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 justify-start">
|
||||
<Button :loading="documentStore.loaderCreateForm" @click="documentStore.update_and_store" type="button" severity="secondary" :label="documentStore.buttonLabel" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.p-multiselect {
|
||||
height: auto;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.p-multiselect-label {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-header {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-filter {
|
||||
max-height: calc(100px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-item {
|
||||
font-size: 0.875rem;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,301 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import { ref, reactive } from 'vue';
|
||||
import TableGeneric from "../../components/TableGeneric.vue";
|
||||
import { Link } from "@inertiajs/inertia-vue3";
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useDocumentStore } from "../../store/Document.js";
|
||||
import { useUserStore } from "../../store/User.js";
|
||||
import { usePetStore } from "../../store/Pet.js";
|
||||
const documentStore = useDocumentStore();
|
||||
const toast = useToast();
|
||||
const userStore = useUserStore();
|
||||
const petStore = usePetStore();
|
||||
const props = defineProps({
|
||||
documents: {
|
||||
type: Object,
|
||||
},
|
||||
search: {
|
||||
type: String
|
||||
},
|
||||
status: {
|
||||
type: String
|
||||
},
|
||||
priority: {
|
||||
type: String
|
||||
},
|
||||
pet: {
|
||||
type: String
|
||||
},
|
||||
permissions_edit: {
|
||||
type: Boolean
|
||||
},
|
||||
permissions_delete: {
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
|
||||
const documents = ref(props.documents ?? []);
|
||||
const selectedItems = ref([]);
|
||||
const searchLoader = ref(false);
|
||||
const filterLoader = ref(false);
|
||||
|
||||
const columns = ref(documentStore.columns)
|
||||
|
||||
const queryParams = reactive({
|
||||
search: props.search ?? '',
|
||||
status: props.status ?? '',
|
||||
priority: props.priority ?? '',
|
||||
pet: props.pet ?? '',
|
||||
})
|
||||
|
||||
const filters = ref({
|
||||
status: props.status ?? '',
|
||||
priority: props.priority ?? '',
|
||||
pet: props.pet ?? '',
|
||||
})
|
||||
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onPageChange = (event) => {
|
||||
documentStore.navigateToPage(event.page + 1);
|
||||
};
|
||||
|
||||
const searchData = () => {
|
||||
searchLoader.value = true;
|
||||
|
||||
const searchParams = {
|
||||
...(queryParams.search && { search: queryParams.search }),
|
||||
...(queryParams.status && { status: queryParams.status }),
|
||||
...(queryParams.priority && { priority: queryParams.priority }),
|
||||
...(queryParams.pet && { pet: queryParams.pet }),
|
||||
...documentStore.convertFiltersToArrayParams(filters.value)
|
||||
};
|
||||
documentStore.search(searchParams);
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
const exportParams = {
|
||||
...(queryParams.search && { search: queryParams.search }),
|
||||
...(queryParams.status && { status: queryParams.status }),
|
||||
...(queryParams.priority && { priority: queryParams.priority }),
|
||||
...(queryParams.pet && { pet: queryParams.pet }),
|
||||
...documentStore.convertFiltersToArrayParams(filters.value)
|
||||
};
|
||||
documentStore.exportData(exportParams);
|
||||
}
|
||||
|
||||
const onSelectionChange = (selection) => {
|
||||
selectedItems.value = selection;
|
||||
};
|
||||
|
||||
|
||||
const onSearchChange = (value) => {
|
||||
queryParams.search = value;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const showDeleteConfirm = (data) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
documentStore.deleteRecord(data.id);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const confirmDeleteSelected = (ids) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
documentStore.bulkDelete(ids);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const onFilterChange = (newFilters) => {
|
||||
filters.value = { ...newFilters };
|
||||
Object.keys(newFilters).forEach(key => {
|
||||
if (Array.isArray(newFilters[key])) {
|
||||
queryParams[key] = newFilters[key].join(',');
|
||||
} else {
|
||||
queryParams[key] = newFilters[key] || '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const applyFilters = () => {
|
||||
filterLoader.value = true;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
filters.value = {
|
||||
status: null,
|
||||
priority: null,
|
||||
pet: null,
|
||||
};
|
||||
queryParams.status = '';
|
||||
queryParams.priority = '';
|
||||
queryParams.pet = '';
|
||||
searchData();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ documentStore.indexPageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="documentStore.endpoints.create">
|
||||
<span class="text-sm">{{ documentStore.pageTitle }}</span>
|
||||
<i class="fi fi-rr-plus flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="card border-[1px] border-client-gray/20 bg-white rounded-2xl">
|
||||
<TableGeneric
|
||||
:data="documents.data"
|
||||
:columns="columns"
|
||||
:selectable="true"
|
||||
:pagination="documents.meta"
|
||||
:searchable="true"
|
||||
:searchValue="queryParams.search"
|
||||
:filters="filters"
|
||||
:filterLoader="filterLoader"
|
||||
v-model="selectedItems"
|
||||
@selection-change="onSelectionChange"
|
||||
@page-change="onPageChange"
|
||||
@search-change="onSearchChange"
|
||||
@filter-change="onFilterChange"
|
||||
@clear-filters="clearFilters"
|
||||
@apply-filters="applyFilters"
|
||||
>
|
||||
<template #bulk-action="{ selection }">
|
||||
<Button
|
||||
v-if="!permissions_delete"
|
||||
label="حذف همه"
|
||||
size="small"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-trash"
|
||||
severity="danger"
|
||||
class="!text-xs !flex"
|
||||
outlined
|
||||
@click="() => confirmDeleteSelected(selection.map(document => document.id))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #toolbar-actions>
|
||||
<div class="grid grid-cols-a gap-4 justify-start">
|
||||
<Button
|
||||
label="خروجی اکسل"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-file-export"
|
||||
class="!text-xs !bg-client-primary"
|
||||
@click="exportData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-status="{ data }">
|
||||
<Tag
|
||||
class="!text-xs"
|
||||
:value="data.status"
|
||||
:severity="documentStore.getStatusSeverity(data.status)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #column-priority="{ data }">
|
||||
<Tag
|
||||
class="!text-xs"
|
||||
:value="data.priority"
|
||||
:severity="documentStore.getPrioritySeverity(data.priority)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #column-requester="{ data }">
|
||||
<div class="grid grid-cols-2a justify-start gap-2">
|
||||
<span class="text-right text-sm">{{ data.requester?.fullname }}</span>
|
||||
<Link :href="userStore.endpoints.edit(data.requester?.id)" class="text-sm">
|
||||
<i class="fi fi-rr-eye flex !text-xs !text-client-Fontcolor/40"></i>
|
||||
</Link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-pet="{ data }">
|
||||
<div class="grid grid-cols-2a justify-start gap-2">
|
||||
<span class="text-right text-sm">{{ data.pet?.name }}</span>
|
||||
<Link :href="petStore.endpoints.edit(data.pet?.id)" class="text-sm">
|
||||
<i class="fi fi-rr-eye flex !text-xs !text-client-Fontcolor/40"></i>
|
||||
</Link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-actions="{ data }">
|
||||
<div class="grid grid-cols-2a justify-start gap-2">
|
||||
<Button
|
||||
v-if="!permissions_delete"
|
||||
type="button"
|
||||
class="!w-8 !h-8"
|
||||
variant="outlined"
|
||||
severity="danger"
|
||||
icon="fi fi-rr-trash"
|
||||
:pt="{icon : '!mt-2 !text-xs'}"
|
||||
@click="showDeleteConfirm(data)"
|
||||
/>
|
||||
<Button v-if="!permissions_edit" severity="info" variant="outlined" asChild v-slot="slotProps">
|
||||
<Link :class="[slotProps.class , '!w-8 !h-8']" :href="documentStore.endpoints.edit(data.id)">
|
||||
<i class="fi fi-rr-pen-clip flex !text-xs"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</TableGeneric>
|
||||
</div>
|
||||
</master>
|
||||
</template>
|
||||
@@ -0,0 +1,36 @@
|
||||
<script setup>
|
||||
import Master from "../layouts/master.vue";
|
||||
import AddMedia from "../components/medium/AddMedia.vue";
|
||||
import ListMedia from "../components/medium/ListMedia.vue";
|
||||
import {onMounted} from "vue";
|
||||
import {useMediumStore} from "../store/Medium.js";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
medium : {
|
||||
type: Object,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const MediumStore = useMediumStore();
|
||||
|
||||
onMounted(() => {
|
||||
MediumStore.mediums = props.medium?.data ?? [];
|
||||
MediumStore.paginate.total = props.medium?.total ?? null;
|
||||
MediumStore.paginate.last_page = props.medium?.last_page ?? null;
|
||||
MediumStore.paginate.current_page = props.medium?.current_page ?? null;
|
||||
MediumStore.paginate.links = props.medium?.links ?? null;
|
||||
MediumStore.paginate.per_page = props.medium?.per_page ?? null;
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<add-media></add-media>
|
||||
<list-media></list-media>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import {ref , onMounted , onUnmounted} from 'vue';
|
||||
import {Link} from "@inertiajs/inertia-vue3";
|
||||
import {usePermissionStore} from "../../store/Permission.js";
|
||||
import Card from "../../components/ui/Card.vue";
|
||||
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
const props = defineProps({
|
||||
is_edit : {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
permission: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
permissions: {
|
||||
type: Object,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.is_edit) {
|
||||
permissionStore.setEditData(props.permission);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
permissionStore.resetCreateForm();
|
||||
permissionStore.is_edit_form = false;
|
||||
permissionStore.errorsCreateForm = [];
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<Card class="mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-[1fr_auto] justify-between items-center gap-4">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ permissionStore.pageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps" class="w-full sm:w-auto">
|
||||
<Link :class="slotProps.class" :href="permissionStore.endpoints.index">
|
||||
<span class="text-sm">{{ permissionStore.indexPageTitle }}</span>
|
||||
<i class="fi fi-rr-list flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<section class="grid w-full grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<div class="lg:col-span-2">
|
||||
<Card>
|
||||
<Form ref="formRef" v-slot="$form" class="flex flex-col gap-4 !w-full">
|
||||
<FormField v-slot="$field" name="name" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام سطح</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
permissionStore.errorsCreateForm?.name && !permissionStore.createForm.name ? 'p-invalid' : ''
|
||||
]" v-model="permissionStore.createForm.name" placeholder="نام سطح را وارد کنید" />
|
||||
<Message v-if="permissionStore.errorsCreateForm?.name"
|
||||
severity="error" size="small" variant="simple">{{ permissionStore.errorsCreateForm?.name[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="label" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام فارسی سطح</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
permissionStore.errorsCreateForm?.label && !permissionStore.createForm.label ? 'p-invalid' : ''
|
||||
]" v-model="permissionStore.createForm.label" placeholder="نام فارسی سطح را وارد کنید" />
|
||||
<Message v-if="permissionStore.errorsCreateForm?.label" severity="error" size="small" variant="simple">{{ permissionStore.errorsCreateForm?.label[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="parent_id" initialValue="" class="flex flex-col gap-1">
|
||||
<label> والد (مرتبط با) </label>
|
||||
<Select v-model="permissionStore.createForm.parent_id" optionLabel="label" optionValue="id" :pt="{option : '!text-sm'}"
|
||||
:options="permissions.data" filter
|
||||
placeholder="انتخاب سطح" class="w-full">
|
||||
<template #empty>
|
||||
<div class="flex justify-center py-4 items-center h-full">
|
||||
<span class="text-center text-sm">هیچ سطحی پیدا نشده!</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #emptyfilter>
|
||||
<div class="flex justify-center py-4 items-center h-full">
|
||||
<span class="text-center text-sm">هیچ سطحی پیدا نشده!</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #value="slotProps">
|
||||
<div v-if="slotProps.value" class="flex items-center">
|
||||
<div>
|
||||
{{ permissions.data.find(item => item.id === slotProps.value)?.label || 'انتخاب سطح' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Select>
|
||||
</FormField>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-1">
|
||||
<Card>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<div class="grid grid-cols-1 justify-between items-center">
|
||||
<span>زمان انتشار:</span>
|
||||
<CustomDatePicker v-model="permissionStore.createForm.datetime" class="mt-2" type="datetime" compact-time></CustomDatePicker>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 justify-start">
|
||||
<Button :loading="permissionStore.loaderCreateForm" @click="permissionStore.update_and_store" type="button" severity="secondary" :label="permissionStore.buttonLabel" class="w-full sm:w-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import { ref, reactive } from 'vue';
|
||||
import TableGeneric from "../../components/TableGeneric.vue";
|
||||
import { Link } from "@inertiajs/inertia-vue3";
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import {usePermissionStore} from "../../store/Permission.js";
|
||||
const toast = useToast();
|
||||
|
||||
const props = defineProps({
|
||||
permissions: {
|
||||
type: Object,
|
||||
},
|
||||
search: {
|
||||
type: String
|
||||
},
|
||||
level: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
const permissions = ref(props.permissions ?? []);
|
||||
const selectedItems = ref([]);
|
||||
const searchLoader = ref(false);
|
||||
|
||||
const columns = ref(permissionStore.columns)
|
||||
|
||||
const queryParams = reactive({
|
||||
search: props.search ?? '',
|
||||
level: props.level ?? ''
|
||||
})
|
||||
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onPageChange = (event) => {
|
||||
permissionStore.navigateToPage(event.page + 1);
|
||||
};
|
||||
|
||||
const searchData = () => {
|
||||
searchLoader.value = true;
|
||||
permissionStore.search(queryParams).then((response) => {
|
||||
searchLoader.value = false;
|
||||
permissions.value = response.props.permissions.data;
|
||||
});
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
permissionStore.exportData({
|
||||
level: queryParams.level,
|
||||
search: queryParams.search
|
||||
});
|
||||
}
|
||||
|
||||
const onSelectionChange = (selection) => {
|
||||
selectedItems.value = selection;
|
||||
};
|
||||
|
||||
const onSearchChange = (value) => {
|
||||
queryParams.search = value;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const showDeleteConfirm = (data) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
permissionStore.deleteRecord(data.id);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const confirmDeleteSelected = (ids) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
permissionStore.bulkDelete(ids);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ permissionStore.indexPageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="permissionStore.endpoints.create">
|
||||
<span class="text-sm">{{ permissionStore.pageTitle }}</span>
|
||||
<i class="fi fi-rr-plus flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="card border-[1px] border-client-gray/20 bg-white rounded-2xl">
|
||||
<TableGeneric
|
||||
:data="permissions.data"
|
||||
:columns="columns"
|
||||
:selectable="true"
|
||||
:pagination="permissions.meta"
|
||||
:searchable="true"
|
||||
:searchValue="queryParams.search"
|
||||
v-model="selectedItems"
|
||||
@selection-change="onSelectionChange"
|
||||
@page-change="onPageChange"
|
||||
@search-change="onSearchChange"
|
||||
>
|
||||
<template #bulk-action="{ selection }">
|
||||
<Button
|
||||
label="حذف همه"
|
||||
size="small"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-trash"
|
||||
severity="danger"
|
||||
class="!text-xs !flex"
|
||||
outlined
|
||||
@click="() => confirmDeleteSelected(selection.map(user => user.id))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #toolbar-actions>
|
||||
<div class="grid grid-cols-a gap-4 justify-start">
|
||||
<Button
|
||||
label="خروجی اکسل"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-file-export"
|
||||
class="!text-xs !bg-client-primary"
|
||||
@click="exportData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-actions="{ data }">
|
||||
<div class="grid grid-cols-2a">
|
||||
<Button
|
||||
type="button"
|
||||
class="!w-8 !h-8"
|
||||
variant="outlined"
|
||||
severity="danger"
|
||||
icon="fi fi-rr-trash"
|
||||
:pt="{icon : '!mt-2 !text-xs'}"
|
||||
@click="showDeleteConfirm(data)"
|
||||
/>
|
||||
<Button severity="info" variant="outlined" asChild v-slot="slotProps">
|
||||
<Link :class="[slotProps.class , '!w-8 !h-8']" :href="permissionStore.endpoints.edit(data.id)">
|
||||
<i class="fi fi-rr-pen-clip flex !text-xs"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</TableGeneric>
|
||||
</div>
|
||||
</master>
|
||||
</template>
|
||||
@@ -0,0 +1,144 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import {onMounted , onUnmounted} from 'vue';
|
||||
import {usePetStore} from "../../store/Pet.js";
|
||||
import {Link} from "@inertiajs/inertia-vue3";
|
||||
import GlobalSelectMedium from "../../components/medium/GlobalSelectMedium.vue";
|
||||
import Card from "../../components/ui/Card.vue";
|
||||
|
||||
const petStore = usePetStore();
|
||||
|
||||
const props = defineProps({
|
||||
is_edit: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
pet: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.is_edit) {
|
||||
petStore.setEditData(props.pet);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
petStore.resetCreateForm();
|
||||
petStore.is_edit_form = false;
|
||||
petStore.errorsCreateForm = [];
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<Card class="mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-[1fr_auto] justify-between items-center gap-4">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ petStore.pageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="petStore.endpoints.index">
|
||||
<span class="text-sm">{{ petStore.indexPageTitle }}</span>
|
||||
<i class="fi fi-rr-list flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<section class="grid w-full grid-cols-1 lg:grid-cols-[1fr_22rem] gap-4">
|
||||
<Card>
|
||||
<div class="rounded-2xl bg-white">
|
||||
<Form ref="formRef" v-slot="$form" class=" p-4 grid grid-cols-2 gap-4 !w-full">
|
||||
<FormField v-slot="$field" name="name" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام کیس</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
petStore.errorsCreateForm?.name && !petStore.createForm.name ? 'p-invalid' : ''
|
||||
]" v-model="petStore.createForm.name" placeholder="نام کیس خود را وارد کنید" />
|
||||
<Message v-if="petStore.errorsCreateForm?.name"
|
||||
severity="error" size="small" variant="simple">{{ petStore.errorsCreateForm?.name[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="email" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نوع کیس</label>
|
||||
<Select class="!text-xs !h-full" placeholder="انتخاب نوع کیس" v-model="petStore.createForm.type" :options="petStore.typeOptions" optionValue="value" optionLabel="label" :pt="{ option : '!text-sm' , placeholder : '!text-sm' , label : '!pt-3' }" />
|
||||
<Message v-if="petStore.errorsCreateForm?.type" severity="error" size="small" variant="simple">{{ petStore.errorsCreateForm?.type[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="breed" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نژاد</label>
|
||||
<InputText type="text" class="placeholder:!text-sm" v-model="petStore.createForm.breed" placeholder="نژاد را وارد کنید" />
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="weight" initialValue="" class="flex flex-col gap-1">
|
||||
<label>وزن</label>
|
||||
<InputText type="text" class="placeholder:!text-sm" v-model="petStore.createForm.weight" placeholder="وزن را وارد کنید" />
|
||||
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="sterilized" initialValue="" class="flex flex-col gap-1">
|
||||
<label>عقیم سازی</label>
|
||||
<Select class="!text-xs !h-full" placeholder="انتخاب عقیم سازی" v-model="petStore.createForm.sterilized" optionLabel="label" optionValue="value" :options="petStore.sterilizedOptions" :pt="{ option : '!text-sm' , placeholder : '!text-sm' , label : '!pt-3' }" />
|
||||
<Message v-if="petStore.errorsCreateForm?.sterilized"
|
||||
severity="error" size="small" variant="simple">{{ petStore.errorsCreateForm?.sterilized[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="gender" initialValue="" class="flex flex-col gap-1">
|
||||
<label>جنسیت</label>
|
||||
<Select class="!text-xs !h-full" placeholder="انتخاب جنسیت" v-model="petStore.createForm.gender" optionLabel="label" optionValue="value" :options="petStore.genderOptions" :pt="{ option : '!text-sm' , placeholder : '!text-sm' , label : '!pt-3' }" />
|
||||
<Message v-if="petStore.errorsCreateForm?.gender" severity="error" size="small" variant="simple">{{ petStore.errorsCreateForm?.gender[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="birthday" initialValue="" class="flex flex-col gap-1">
|
||||
<label>تاریخ تولد</label>
|
||||
<CustomDatePicker v-model="petStore.createForm.birthday" class="!text-xs mt-2" type="datetime" compact-time></CustomDatePicker>
|
||||
<Message v-if="petStore.errorsCreateForm?.birthday" severity="error" size="small" variant="simple">{{ petStore.errorsCreateForm?.birthday[0] }}</Message>
|
||||
</FormField>
|
||||
</Form>
|
||||
</div>
|
||||
</Card>
|
||||
<div>
|
||||
<Card>
|
||||
<div class="p-4 rounded-2xl pb-8">
|
||||
<h5>تصویر کاربر</h5>
|
||||
<global-select-medium v-model="petStore.createForm.image"></global-select-medium>
|
||||
</div>
|
||||
</Card>
|
||||
<Card class="mt-4">
|
||||
<div class="p-4 grid grid-cols-1 gap-4 rounded-2xl">
|
||||
<div class="grid grid-cols-1 justify-between items-center">
|
||||
<span>زمان انتشار:</span>
|
||||
<CustomDatePicker v-model="petStore.createForm.datetime" class="!text-xs mt-2" type="datetime" compact-time></CustomDatePicker>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 justify-start">
|
||||
<Button :loading="petStore.loaderCreateForm" @click="petStore.update_and_store" type="button" severity="secondary" :label="petStore.buttonLabel" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.p-multiselect {
|
||||
height: auto;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.p-multiselect-label {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-header {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-filter {
|
||||
max-height: calc(100px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-item {
|
||||
font-size: 0.875rem;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,273 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import { ref, reactive } from 'vue';
|
||||
import TableGeneric from "../../components/TableGeneric.vue";
|
||||
import { Link } from "@inertiajs/inertia-vue3";
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import { usePetStore } from "../../store/Pet.js";
|
||||
import { useUserStore } from "../../store/User.js";
|
||||
import Button from 'primevue/button';
|
||||
import Tag from 'primevue/tag';
|
||||
const petStore = usePetStore();
|
||||
const userStore = useUserStore();
|
||||
const props = defineProps({
|
||||
pets: {
|
||||
type: Object,
|
||||
},
|
||||
search: {
|
||||
type: String
|
||||
},
|
||||
type: {
|
||||
type: [String, Array],
|
||||
default: null
|
||||
},
|
||||
gender: {
|
||||
type: String
|
||||
},
|
||||
permissions_edit: {
|
||||
type: Boolean
|
||||
},
|
||||
permissions_delete: {
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
|
||||
const pets = ref(props.pets ?? []);
|
||||
const selectedItems = ref([]);
|
||||
const searchLoader = ref(false);
|
||||
const filterLoader = ref(false);
|
||||
|
||||
const columns = ref(petStore.columns)
|
||||
|
||||
const queryParams = reactive({
|
||||
search: props.search ?? '',
|
||||
type: props.type ?? '',
|
||||
gender: props.gender ?? '',
|
||||
})
|
||||
|
||||
const filters = ref({
|
||||
gender: (props.gender && typeof props.gender === 'string') ? props.gender : null,
|
||||
type: petStore.parseStringToArray(props.type),
|
||||
})
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onPageChange = (event) => {
|
||||
petStore.navigateToPage(event.page + 1);
|
||||
};
|
||||
|
||||
const searchData = () => {
|
||||
searchLoader.value = true;
|
||||
|
||||
const searchParams = {
|
||||
...(queryParams.search && { search: queryParams.search }),
|
||||
...(queryParams.gender && { gender: queryParams.gender }),
|
||||
...petStore.convertFiltersToArrayParams(filters.value)
|
||||
};
|
||||
petStore.search(searchParams);
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
const exportParams = {
|
||||
...(queryParams.search && { search: queryParams.search }),
|
||||
...(queryParams.gender && { gender: queryParams.gender }),
|
||||
...petStore.convertFiltersToArrayParams(filters.value)
|
||||
};
|
||||
petStore.exportData(exportParams);
|
||||
}
|
||||
|
||||
const onSelectionChange = (selection) => {
|
||||
selectedItems.value = selection;
|
||||
};
|
||||
|
||||
const onSearchChange = (value) => {
|
||||
queryParams.search = value;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const onFilterChange = (newFilters) => {
|
||||
filters.value = { ...newFilters };
|
||||
Object.keys(newFilters).forEach(key => {
|
||||
if (Array.isArray(newFilters[key])) {
|
||||
queryParams[key] = newFilters[key].join(',');
|
||||
} else {
|
||||
queryParams[key] = newFilters[key] || '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const applyFilters = () => {
|
||||
filterLoader.value = true;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
filters.value = {
|
||||
gender: null,
|
||||
type: [],
|
||||
};
|
||||
queryParams.gender = '';
|
||||
queryParams.type = '';
|
||||
searchData();
|
||||
};
|
||||
|
||||
const showDeleteConfirm = (data) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
petStore.deleteRecord(data.id);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const confirmDeleteSelected = (ids) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
petStore.bulkDelete(ids);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ petStore.indexPageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="petStore.endpoints.create">
|
||||
<span class="text-sm">{{ petStore.pageTitle }}</span>
|
||||
<i class="fi fi-rr-plus flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="card border-[1px] border-client-gray/20 bg-white rounded-2xl">
|
||||
<TableGeneric
|
||||
:data="pets.data"
|
||||
:columns="columns"
|
||||
:selectable="true"
|
||||
:pagination="pets.meta"
|
||||
:searchable="true"
|
||||
:searchValue="queryParams.search"
|
||||
:filters="filters"
|
||||
:filterLoader="filterLoader"
|
||||
v-model="selectedItems"
|
||||
@selection-change="onSelectionChange"
|
||||
@page-change="onPageChange"
|
||||
@search-change="onSearchChange"
|
||||
@filter-change="onFilterChange"
|
||||
@apply-filters="applyFilters"
|
||||
@clear-filters="clearFilters"
|
||||
>
|
||||
<template #bulk-action="{ selection }" v-if="!permissions_delete">
|
||||
<Button
|
||||
label="حذف همه"
|
||||
size="small"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-trash"
|
||||
severity="danger"
|
||||
class="!text-xs !flex"
|
||||
outlined
|
||||
@click="() => confirmDeleteSelected(selection.map(pet => pet.id))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #toolbar-actions>
|
||||
<div class="grid grid-cols-a gap-4 justify-start">
|
||||
<Button
|
||||
label="خروجی اکسل"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-file-export"
|
||||
class="!text-xs !bg-client-primary"
|
||||
@click="exportData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-sterilized="{ data }">
|
||||
<Tag
|
||||
class="!text-xs"
|
||||
:value="data.sterilized ? 'عقیم شده' : 'عقیم نشده'"
|
||||
:severity="petStore.getSterilized(data.sterilized)"
|
||||
/>
|
||||
</template>
|
||||
<template #column-owner="{ data }">
|
||||
<div class="grid grid-cols-2a justify-start gap-2">
|
||||
<span class="text-right text-sm">{{ data.owner?.fullname }}</span>
|
||||
<Link :href="userStore.endpoints.edit(data.owner.id)" class="text-sm">
|
||||
<i class="fi fi-rr-eye flex !text-xs !text-client-Fontcolor/40"></i>
|
||||
</Link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-actions="{ data }">
|
||||
<div class="grid grid-cols-2a justify-start gap-2">
|
||||
<Button
|
||||
v-if="!permissions_delete"
|
||||
type="button"
|
||||
class="!w-8 !h-8"
|
||||
variant="outlined"
|
||||
severity="danger"
|
||||
icon="fi fi-rr-trash"
|
||||
:pt="{icon : '!mt-2 !text-xs'}"
|
||||
@click="showDeleteConfirm(data)"
|
||||
/>
|
||||
<Button
|
||||
v-if="!permissions_edit"
|
||||
severity="info"
|
||||
variant="outlined"
|
||||
asChild
|
||||
v-slot="slotProps"
|
||||
>
|
||||
<Link :class="[slotProps.class , '!w-8 !h-8']" :href="petStore.endpoints.edit(data.id)">
|
||||
<i class="fi fi-rr-pen-clip flex !text-xs"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</TableGeneric>
|
||||
</div>
|
||||
</master>
|
||||
</template>
|
||||
@@ -0,0 +1,265 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import TableGeneric from "../../components/TableGeneric.vue";
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useReportStore } from "../../store/Report.js";
|
||||
import UIModal from "../../components/ui/UIModal.vue";
|
||||
import CreateReportForm from "../../components/reports/CreateReportForm.vue";
|
||||
const reportStore = useReportStore();
|
||||
const toast = useToast();
|
||||
|
||||
const props = defineProps({
|
||||
reports: {
|
||||
type: Object,
|
||||
},
|
||||
search: {
|
||||
type: String
|
||||
},
|
||||
status: {
|
||||
type: String
|
||||
},
|
||||
})
|
||||
|
||||
const reports = ref(props.reports ?? []);
|
||||
const selectedItems = ref([]);
|
||||
const searchLoader = ref(false);
|
||||
const editReportData = ref(null);
|
||||
|
||||
const columns = ref(reportStore.columns)
|
||||
|
||||
const queryParams = reactive({
|
||||
search: props.search ?? '',
|
||||
status: props.status ?? '',
|
||||
})
|
||||
|
||||
const confirm = useConfirm();
|
||||
|
||||
watch(() => props.reports, (newReports) => {
|
||||
if (newReports) {
|
||||
reports.value = newReports;
|
||||
}
|
||||
}, { deep: true, immediate: true });
|
||||
|
||||
|
||||
const onPageChange = (event) => {
|
||||
reportStore.navigateToPage(event.page + 1);
|
||||
};
|
||||
|
||||
const searchData = () => {
|
||||
searchLoader.value = true;
|
||||
reportStore.search(queryParams).then((response) => {
|
||||
searchLoader.value = false;
|
||||
reports.value = response.props.reports.data;
|
||||
});
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
reportStore.exportData({
|
||||
search: queryParams.search,
|
||||
status: queryParams.status,
|
||||
});
|
||||
}
|
||||
|
||||
const openCreateModal = () => {
|
||||
reportStore.is_edit_form = false;
|
||||
editReportData.value = null;
|
||||
reportStore.showCreateModal = true;
|
||||
}
|
||||
|
||||
const openEditModal = (reportData) => {
|
||||
reportStore.is_edit_form = true;
|
||||
editReportData.value = reportData;
|
||||
reportStore.showCreateModal = true;
|
||||
}
|
||||
|
||||
const closeCreateModal = () => {
|
||||
reportStore.showCreateModal = false;
|
||||
reportStore.is_edit_form = false;
|
||||
editReportData.value = null;
|
||||
}
|
||||
|
||||
const handleCreateOrUpdateReport = async (reportId = null) => {
|
||||
const formData = reportStore.createForm;
|
||||
|
||||
if (reportStore.is_edit_form && reportId) {
|
||||
await reportStore.update(reportId, formData);
|
||||
} else {
|
||||
await reportStore.create(formData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const onSelectionChange = (selection) => {
|
||||
selectedItems.value = selection;
|
||||
};
|
||||
|
||||
const onSearchChange = (value) => {
|
||||
queryParams.search = value;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const showDeleteConfirm = (data) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
reportStore.deleteRecord(data.id);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const confirmDeleteSelected = (ids) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
reportStore.bulkDelete(ids);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ reportStore.indexPageTitle }}</h2>
|
||||
<Button
|
||||
severity="secondary"
|
||||
@click="openCreateModal"
|
||||
>
|
||||
<span class="text-sm">{{ reportStore.pageTitle }}</span>
|
||||
<i class="fi fi-rr-plus flex text-sm"></i>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="card border-[1px] border-client-gray/20 bg-white rounded-2xl">
|
||||
<TableGeneric
|
||||
:data="reports.data"
|
||||
:columns="columns"
|
||||
:selectable="true"
|
||||
:pagination="reports.meta"
|
||||
:searchable="true"
|
||||
:searchValue="queryParams.search"
|
||||
v-model="selectedItems"
|
||||
@selection-change="onSelectionChange"
|
||||
@page-change="onPageChange"
|
||||
@search-change="onSearchChange"
|
||||
>
|
||||
<template #bulk-action="{ selection }">
|
||||
<Button
|
||||
label="حذف همه"
|
||||
size="small"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-trash"
|
||||
severity="danger"
|
||||
class="!text-xs !flex"
|
||||
outlined
|
||||
@click="() => confirmDeleteSelected(selection.map(report => report.id))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #toolbar-actions>
|
||||
<div class="grid grid-cols-a gap-4 justify-start">
|
||||
<Button
|
||||
label="خروجی اکسل"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-file-export"
|
||||
class="!text-xs !bg-client-primary"
|
||||
@click="exportData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-status="{ data }">
|
||||
<Tag
|
||||
class="!text-xs"
|
||||
:value="data.status"
|
||||
:severity="reportStore.getStatusSeverity(data.status)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #column-actions="{ data }">
|
||||
<div class="grid grid-cols-2a justify-start gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
class="!w-8 !h-8"
|
||||
variant="outlined"
|
||||
severity="danger"
|
||||
icon="fi fi-rr-trash"
|
||||
:pt="{icon : '!mt-2 !text-xs'}"
|
||||
@click="showDeleteConfirm(data)"
|
||||
/>
|
||||
<Button
|
||||
severity="info"
|
||||
variant="outlined"
|
||||
class="!w-8 !h-8"
|
||||
@click="openEditModal(data)"
|
||||
>
|
||||
<i class="fi fi-rr-pen-clip flex !text-xs"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</TableGeneric>
|
||||
</div>
|
||||
|
||||
<UIModal
|
||||
v-model:visible="reportStore.showCreateModal"
|
||||
:title="reportStore.is_edit_form ? 'ویرایش گزارش' : 'ایجاد گزارش جدید'"
|
||||
size="medium"
|
||||
maximizable
|
||||
@hide="closeCreateModal"
|
||||
>
|
||||
<template #default="{ isFullscreen }">
|
||||
<CreateReportForm
|
||||
:reportData="editReportData"
|
||||
:isEdit="reportStore.is_edit_form"
|
||||
:isFullscreen="isFullscreen"
|
||||
@submit="handleCreateOrUpdateReport"
|
||||
@cancel="closeCreateModal"
|
||||
/>
|
||||
</template>
|
||||
</UIModal>
|
||||
</master>
|
||||
</template>
|
||||
@@ -0,0 +1,134 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import {ref , onMounted , onUnmounted} from 'vue';
|
||||
import {Link} from "@inertiajs/inertia-vue3";
|
||||
import {useRoleStore} from "../../store/Role.js";
|
||||
import Card from "../../components/ui/Card.vue";
|
||||
|
||||
|
||||
const roleStore = useRoleStore();
|
||||
const props = defineProps({
|
||||
is_edit : {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
role: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
permissions: {
|
||||
type: Object,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.is_edit) {
|
||||
roleStore.setEditData(props.role);
|
||||
roleStore.createForm.selectedPermissionIds = props.role.permissions;
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
roleStore.resetCreateForm();
|
||||
roleStore.is_edit_form = false;
|
||||
roleStore.errorsCreateForm = [];
|
||||
})
|
||||
|
||||
const managerSelected = () => {
|
||||
const all = {};
|
||||
props.permissions.forEach(permission => {
|
||||
const filtered = permission.permissions_parent
|
||||
.filter(p => !p.name.includes('own'))
|
||||
.map(p => p.id);
|
||||
|
||||
all[permission.id] = filtered;
|
||||
});
|
||||
roleStore.createForm.selectedPermissionIds = all;
|
||||
}
|
||||
const selectAllWithOwn = () => {
|
||||
const all = {};
|
||||
props.permissions.forEach(permission => {
|
||||
all[permission.id] = permission.permissions_parent.map(p => p.id);
|
||||
});
|
||||
roleStore.createForm.selectedPermissionIds = all;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<Card class="mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-[1fr_auto] justify-between items-center gap-4">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ roleStore.pageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps" class="w-full sm:w-auto">
|
||||
<Link :class="slotProps.class" :href="roleStore.endpoints.index">
|
||||
<span class="text-sm">{{ roleStore.indexPageTitle }}</span>
|
||||
<i class="fi fi-rr-list flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<section class="grid w-full grid-cols-1 lg:grid-cols-[1fr_26rem] gap-4">
|
||||
<Card>
|
||||
<div class="">
|
||||
<Form ref="formRef" v-slot="$form" class="bg-white p-4 rounded-2xl flex flex-col gap-4 !w-full">
|
||||
<FormField v-slot="$field" name="name" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام مقام</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
roleStore.errorsCreateForm?.name && !roleStore.createForm.name ? 'p-invalid' : ''
|
||||
]" v-model="roleStore.createForm.name" placeholder="نام مقام را وارد کنید" />
|
||||
<Message v-if="roleStore.errorsCreateForm?.name"
|
||||
severity="error" size="small" variant="simple">{{ roleStore.errorsCreateForm?.name[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="label" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام فارسی مقام</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
roleStore.errorsCreateForm?.label && !roleStore.createForm.label ? 'p-invalid' : ''
|
||||
]" v-model="roleStore.createForm.label" placeholder="نام فارسی مقام را وارد کنید" />
|
||||
<Message v-if="roleStore.errorsCreateForm?.label" severity="error" size="small" variant="simple">{{ roleStore.errorsCreateForm?.label[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="permissions_parent" initialValue="" class="flex flex-col gap-1">
|
||||
<label>انتخاب سطح دسترسی برای این مقام</label>
|
||||
<div class="grid grid-cols-3 max-lg:grid-cols-2 mt-1 gap-4 max-sm:grid-cols-1">
|
||||
<div v-for="(permission , index) in permissions" :key="permission.id" class="grid grid-cols-1 mt-1">
|
||||
<span class="mb-1 text-sm">{{permission.label}}</span>
|
||||
<select v-model="roleStore.createForm.selectedPermissionIds[permission.id]" multiple>
|
||||
<option v-for="(p , index) in permission.permissions_parent" :key="p.id" :value="p.id">{{p.label}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</FormField>
|
||||
</Form>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div class="rounded-3xl lg:col-span-1 z-20">
|
||||
<div class="p-4 bg-white grid grid-cols-1 gap-4 rounded-2xl">
|
||||
<div class="grid grid-cols-1 justify-between items-center">
|
||||
<span>زمان انتشار:</span>
|
||||
<CustomDatePicker v-model="roleStore.createForm.datetime" class="!text-xs mt-2" type="datetime" compact-time></CustomDatePicker>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-x-3 mt-1 justify-between items-center">
|
||||
<span class="col-span-2 mb-2">عملیات سریع:</span>
|
||||
<Button @click="managerSelected" icon="fi fi-rr-choose" :pt="{icon: 'flex'}" size="small" outlined class="!text-xs h-10" label="انتخاب بعنوان مدیر"/>
|
||||
<Button @click="selectAllWithOwn" icon="fi fi-rr-choose" :pt="{icon: 'flex'}" size="small" outlined class="!text-xs h-10 truncate" label="انتخاب با محدودیت کاربری"/>
|
||||
<div class="col-span-2 py-4">
|
||||
<h3 class="text-xs font-black">انتخاب بعنوان مدیر:</h3>
|
||||
<p class="leading-7 font-light text-xs">این گذینه تمامی سطح های مورد نظر <span class="font-bold">به جز سطح محدودیت کاربر</span> رو بصورت خودکار انتخاب کرده و تنها کافی است که نام و نام فارسی مقام را مشخص و ثبت کنید.</p>
|
||||
<h3 class="text-xs font-black mt-2">انتخاب با محدودیت کاربری:</h3>
|
||||
<p class="leading-7 text-xs font-light">این گذینه تمامی سطح های مورد نظر رو بصورت خودکار انتخاب کرده در هر بخش <span class="font-bold"> سطح محدودیت کاربر هم اعمال خواهد</span> شد تنها کافی است که نام و نام فارسی مقام را مشخص و ثبت کنید. </p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 justify-start">
|
||||
<Button :loading="roleStore.loaderCreateForm" @click="roleStore.update_and_store" type="button" severity="secondary" :label="roleStore.buttonLabel" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</section>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import {ref, reactive} from 'vue';
|
||||
import {Link} from "@inertiajs/inertia-vue3";
|
||||
import {useConfirm} from 'primevue/useconfirm';
|
||||
import TableGeneric from "../../components/TableGeneric.vue";
|
||||
import {useRoleStore} from "../../store/Role.js";
|
||||
|
||||
const props = defineProps({
|
||||
roles: {
|
||||
type: Object,
|
||||
},
|
||||
search: {
|
||||
type: String
|
||||
},
|
||||
})
|
||||
const roleStore = useRoleStore();
|
||||
|
||||
const roles = ref(props.roles ?? []);
|
||||
const selectedItems = ref([]);
|
||||
const searchLoader = ref(false);
|
||||
|
||||
const columns = ref(roleStore.columns)
|
||||
|
||||
const queryParams = reactive({
|
||||
search: props.search ?? ''
|
||||
})
|
||||
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onPageChange = (event) => {
|
||||
roleStore.navigateToPage(event.page + 1);
|
||||
};
|
||||
|
||||
const searchData = () => {
|
||||
searchLoader.value = true;
|
||||
roleStore.search(queryParams).then((response) => {
|
||||
searchLoader.value = false;
|
||||
roles.value = response.props.roles.data;
|
||||
});
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
roleStore.exportData({
|
||||
search: queryParams.search
|
||||
});
|
||||
}
|
||||
|
||||
const onSelectionChange = (selection) => {
|
||||
selectedItems.value = selection;
|
||||
};
|
||||
|
||||
const onSearchChange = (value) => {
|
||||
queryParams.search = value;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const showDeleteConfirm = (data) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
roleStore.deleteRecord(data.id);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const confirmDeleteSelected = (ids) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
roleStore.bulkDelete(ids);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ roleStore.indexPageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="roleStore.endpoints.create">
|
||||
<span class="text-sm">{{ roleStore.pageTitle }}</span>
|
||||
<i class="fi fi-rr-plus flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="card border-[1px] overflow-hidden border-client-gray/20 bg-white rounded-2xl">
|
||||
<TableGeneric
|
||||
:data="roles.data"
|
||||
:columns="columns"
|
||||
:selectable="true"
|
||||
:pagination="roles.meta"
|
||||
:searchable="true"
|
||||
:searchValue="queryParams.search"
|
||||
v-model="selectedItems"
|
||||
@selection-change="onSelectionChange"
|
||||
@page-change="onPageChange"
|
||||
@search-change="onSearchChange"
|
||||
>
|
||||
<template #bulk-action="{ selection }">
|
||||
<Button
|
||||
label="حذف همه"
|
||||
size="small"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-trash"
|
||||
severity="danger"
|
||||
class="!text-xs !flex"
|
||||
outlined
|
||||
@click="() => confirmDeleteSelected(selection.map(user => user.id))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #toolbar-actions>
|
||||
<div class="grid grid-cols-a gap-4 justify-start">
|
||||
<Button
|
||||
label="خروجی اکسل"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-file-export"
|
||||
class="!text-xs !bg-client-primary"
|
||||
@click="exportData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-actions="{ data }">
|
||||
<div class="grid grid-cols-2a">
|
||||
<Button
|
||||
type="button"
|
||||
class="!w-8 !h-8"
|
||||
variant="outlined"
|
||||
severity="danger"
|
||||
icon="fi fi-rr-trash"
|
||||
:pt="{icon : '!mt-2 !text-xs'}"
|
||||
@click="showDeleteConfirm(data)"
|
||||
/>
|
||||
<Button severity="info" variant="outlined" asChild v-slot="slotProps">
|
||||
<Link :class="[slotProps.class , '!w-8 !h-8']" :href="roleStore.endpoints.edit(data.id)">
|
||||
<i class="fi fi-rr-pen-clip flex !text-xs"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</TableGeneric>
|
||||
</div>
|
||||
</master>
|
||||
</template>
|
||||
@@ -0,0 +1,138 @@
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {useToast} from "primevue";
|
||||
import Master from "../../../layouts/master.vue";
|
||||
import {useGlobalStore} from "../../../store/Global.js";
|
||||
|
||||
const src = ref(null);
|
||||
const file = ref({});
|
||||
const loading = ref(false);
|
||||
const isUpload = ref(false);
|
||||
const loadingUpload = ref(false);
|
||||
|
||||
const toast = useToast();
|
||||
const globalStore = useGlobalStore();
|
||||
const user_form = ref({
|
||||
name: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
user_form.value = {
|
||||
name: globalStore.current_user?.name ?? '',
|
||||
first_name: globalStore.current_user?.first_name ?? '',
|
||||
last_name: globalStore.current_user?.last_name ?? '',
|
||||
};
|
||||
})
|
||||
const errors = ref([]);
|
||||
const onFileSelect = (event) => {
|
||||
isUpload.value = false;
|
||||
file.value = event.files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = async (e) => {
|
||||
src.value = e.target.result;
|
||||
};
|
||||
reader.readAsDataURL(file.value);
|
||||
}
|
||||
|
||||
const onRemoveTemplatingFile = () => {
|
||||
src.value = null;
|
||||
file.value = {};
|
||||
};
|
||||
|
||||
const onFormSubmit = () => {
|
||||
loading.value = true;
|
||||
axios.post(`/admin/setting-profile/update-data/${globalStore.current_user.id}` , user_form.value ).then(res => {
|
||||
toast.add({ severity: 'success', summary: 'اطلاعات شما با موفقیت ثبت شد.', life: 3000 });
|
||||
errors.value = [];
|
||||
loading.value = false;
|
||||
globalStore.current_user.name = res.data.name;
|
||||
globalStore.current_user.first_name = res.data.first_name;
|
||||
globalStore.current_user.last_name = res.data.last_name;
|
||||
}).catch(error => {
|
||||
errors.value = error.response.data.errors;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const uploadProfile = () => {
|
||||
loadingUpload.value = true;
|
||||
const form = new FormData();
|
||||
form.append('file' , file.value );
|
||||
axios.post('/admin/setting-profile/update-avatar' , form ).then(res => {
|
||||
toast.add({ severity: 'success', summary: 'پروفایل شما با موفقیت آپلود شد', life: 3000 });
|
||||
isUpload.value = true;
|
||||
loadingUpload.value = false;
|
||||
globalStore.current_user.profile_url = res.data.profile_url ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<section class="grid grid-cols-[1fr] pb-10 gap-4 px-10 max-lg:px-4">
|
||||
<div class="!w-1/2 max-lg:!w-full !m-auto grid grid-cols-a text-client-Fontcolor justify-center text-center">
|
||||
<i class="fi fi-rr-user-pen flex text-5xl m-auto"></i>
|
||||
<h4 class="font-extrabold text-2xl mt-4">ویرایش اطلاعات</h4>
|
||||
<p class="leading-6 text-sm font-light mt-2">برای تغییر آواتار خود، تصویر مورد نظر را از دستگاه خود انتخاب کرده و بارگذاری کنید. پس از بارگذاری، آواتار جدید شما بهطور خودکار در پروفایل بهروز میشود.</p>
|
||||
</div>
|
||||
<div class="card grid grid-cols-1 !w-[30rem] max-lg:!w-full !m-auto justify-center">
|
||||
<div class="card flex flex-col items-center gap-6 !mb-8">
|
||||
<FileUpload mode="basic" @select="onFileSelect" customUpload auto severity="secondary" chooseLabel="انتخاب آواتار" class="p-button-outlined flex flex-row-reverse !text-xs !text-white !bg-client-primary" />
|
||||
<img v-if="!src && globalStore.current_user.profile_url" :src="globalStore.current_user.profile_url" alt="Image" class="shadow-md rounded-xl w-auto max-h-28 object-contain" />
|
||||
<img v-if="src" :src="src" alt="Image" class="shadow-md rounded-xl w-auto max-h-28 object-contain" :style="!isUpload ? {filter: 'grayscale(100%)' } : {}" />
|
||||
<div v-if="!isUpload" class="grid grid-cols-2a gap-2 -mt-3 items-center justify-center">
|
||||
<Button v-if="src" icon="fi fi-rr-trash" :pt="{icon: 'flex'}" @click="onRemoveTemplatingFile()" outlined severity="danger" />
|
||||
<Button v-if="src" icon="fi fi-rr-cloud-upload" :pt="{icon: 'flex text-sm'}" label=" آپلود آواتار"
|
||||
class="!text-xs flex flex-row-reverse !py-3 !font-light"
|
||||
@click="uploadProfile()" :loading="loadingUpload" outlined severity="secondary" />
|
||||
</div>
|
||||
</div>
|
||||
<Fieldset legend="ویرایش حساب شما" pt:legend="!text-sm !rounded-lg !border-[1px] !border-gray-200"
|
||||
pt:root:class="!border-[1px] !rounded-xl"
|
||||
pt:content:class="grid grid-cols-1 !p-6 !pt-4 flex justify-center">
|
||||
<Form @submit="onFormSubmit" class="grid grid-cols-1 gap-4 w-full !border-0 !text-sm">
|
||||
<FormField v-slot="$field" name="name" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام کاربری</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm !text-sm',
|
||||
errors?.name && !errors.name ? 'p-invalid' : ''
|
||||
]" v-model="user_form.name" placeholder="نام کاربری را وارد کنید" />
|
||||
<Message v-if="errors?.name"
|
||||
severity="error" size="small" variant="simple">{{ errors?.name[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="first_name" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام کوچک</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm !text-sm',
|
||||
errors?.first_name && !errors.first_name ? 'p-invalid' : ''
|
||||
]" v-model="user_form.first_name" placeholder="نام کوچک خود را وارد کنید" />
|
||||
<Message v-if="errors?.first_name"
|
||||
severity="error" size="small" variant="simple">{{ errors?.first_name[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="last_name" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام خانوادگی</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm !text-sm',
|
||||
errors?.last_name && !errors.last_name ? 'p-invalid' : ''
|
||||
]" v-model="user_form.last_name" placeholder="نام کوچک خود را وارد کنید" />
|
||||
<Message v-if="errors?.last_name"
|
||||
severity="error" size="small" variant="simple">{{ errors?.last_name[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" asChild name="email" :initialValue="globalStore.current_user.email">
|
||||
<section class="flex flex-col gap-2">
|
||||
<InputText type="email" class="flex pt-2 !text-sm !font-light" disabled id="email" fluid />
|
||||
</section>
|
||||
</FormField>
|
||||
<Button type="submit" :loading="loading" severity="secondary" label="ذخیره اطلاعات" />
|
||||
</Form>
|
||||
</Fieldset>
|
||||
</div>
|
||||
</section>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<script setup lang="ts">
|
||||
import {reactive, ref} from "vue";
|
||||
import axios from "axios";
|
||||
import {useToast} from "primevue/usetoast";
|
||||
import Master from "../../../layouts/master.vue";
|
||||
|
||||
interface PasswordInterface {
|
||||
password: string,
|
||||
newPassword: string,
|
||||
newPassword_confirmation: string,
|
||||
}
|
||||
|
||||
|
||||
const toast = useToast();
|
||||
const loader = ref<boolean>(false);
|
||||
const errors = ref<any>([]);
|
||||
const ChangePasswordForm = reactive<PasswordInterface>({
|
||||
password : '',
|
||||
newPassword : '',
|
||||
newPassword_confirmation : '',
|
||||
});
|
||||
|
||||
|
||||
const ChangePasswordHandler = () => {
|
||||
loader.value = true;
|
||||
axios.post('/admin/setting-profile/change-password' , ChangePasswordForm).then(res => {
|
||||
if (res.status == 200){
|
||||
toast.add({severity : 'success' , summary : res.data.message});
|
||||
ChangePasswordForm.password = '';
|
||||
ChangePasswordForm.newPassword = '';
|
||||
ChangePasswordForm.newPassword_confirmation = '';
|
||||
}
|
||||
loader.value = false;
|
||||
errors.value = [];
|
||||
}).catch(error => {
|
||||
errors.value = error.response.data.errors ?? [];
|
||||
if (error.status == 401 && error.response.data.message){
|
||||
toast.add({severity : 'error' , summary : error.response.data.message})
|
||||
}
|
||||
loader.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<Panel toggleable class="w-full !rounded-2xl bg-client-gray/10">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fi fi-rr-lock"></i>
|
||||
<span class="font-semibold text-sm">تغییر کلمه عبور</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="grid grid-cols-1 gap-4 content-start">
|
||||
<p class="mb-4 leading-7 text-sm !font-extralight">برای افزایش امنیت حساب کاربری خود، توصیه میشود که کلمه عبور خود را به طور دورهای تغییر دهید. برای این کار، ابتدا کلمه عبور فعلی خود را وارد کنید، سپس کلمه عبور جدید را وارد کرده و آن را دوباره تایید کنید. توجه داشته باشید که کلمه عبور جدید باید حداقل 8 کاراکتر باشد و شامل ترکیبی از حروف، اعداد و نمادها باشد.</p>
|
||||
<div class="m-auto">
|
||||
<FloatLabel variant="on" class=" m-auto">
|
||||
<Password id="password" type="password" :feedback="false"
|
||||
appendTo="self" toggleMask
|
||||
v-model="ChangePasswordForm.password"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !border-gray-500/20 !text-client-Fontcolor"/>
|
||||
<label for="on_label" class="!text-client-Fontcolor !bg-white !text-sm font-light">کلمه عبور فعلی</label>
|
||||
</FloatLabel>
|
||||
<Message
|
||||
v-if="errors.password"
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small">{{errors.password[0]}}</Message>
|
||||
</div>
|
||||
<div class="m-auto">
|
||||
<FloatLabel variant="on" class="m-auto">
|
||||
<Password id="password" type="password" :feedback="false"
|
||||
appendTo="self" toggleMask
|
||||
v-model="ChangePasswordForm.newPassword"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !border-gray-500/20 !text-client-Fontcolor"/>
|
||||
<label for="on_label" class="!text-client-Fontcolor !bg-white !text-sm font-light"> کلمه عبور جدید</label>
|
||||
</FloatLabel>
|
||||
<Message
|
||||
v-if="errors.newPassword"
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small">{{errors.newPassword[0]}}</Message>
|
||||
</div>
|
||||
<div class="m-auto">
|
||||
<FloatLabel variant="on" class="m-auto">
|
||||
<Password id="password" type="password" :feedback="false"
|
||||
appendTo="self" toggleMask
|
||||
v-model="ChangePasswordForm.newPassword_confirmation"
|
||||
class="placeholder:!font-light !text-sm !bg-white h-12 !border-gray-500/20 !text-client-Fontcolor"/>
|
||||
<label for="on_label" class="!text-client-Fontcolor !bg-white !text-sm font-light">تکرار کلمه عبور جدید</label>
|
||||
</FloatLabel>
|
||||
<Message
|
||||
class="h-7 mt-1 !text-xs px-1"
|
||||
variant="simple"
|
||||
severity="error" size="small"></Message>
|
||||
</div>
|
||||
<Button @click="ChangePasswordHandler" :loading="loader" label=" تغییر کلمه عبور" severity="secondary"
|
||||
class="m-auto flex flex-row-reverse items-center !font-normal" />
|
||||
</div>
|
||||
</Panel>
|
||||
</master>
|
||||
</template>
|
||||
@@ -0,0 +1,149 @@
|
||||
<script setup>
|
||||
import Master from "../../../layouts/master.vue";
|
||||
import { ref, onMounted } from 'vue';
|
||||
import TableGeneric from "../../../components/TableGeneric.vue";
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useSessionStore } from "../../../store/Session.js";
|
||||
|
||||
const toast = useToast();
|
||||
const confirm = useConfirm();
|
||||
|
||||
const props = defineProps({
|
||||
sessions: {
|
||||
type: Object,
|
||||
default: () => ({ data: [], meta: {} })
|
||||
}
|
||||
});
|
||||
|
||||
const sessionStore = useSessionStore();
|
||||
const selectedItems = ref([]);
|
||||
|
||||
const columns = ref(sessionStore.columns);
|
||||
|
||||
onMounted(() => {
|
||||
sessionStore.setSessions(props.sessions || []);
|
||||
});
|
||||
|
||||
const onSelectionChange = (selection) => {
|
||||
selectedItems.value = selection;
|
||||
sessionStore.setSelectedSessions(selection);
|
||||
};
|
||||
|
||||
const showDeleteConfirm = (data) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
sessionStore.deleteSession(data.id)
|
||||
.then((response) => {
|
||||
toast.add({ severity: 'success', summary: response.data.message });
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.add({ severity: 'error', summary: error.response.data.message });
|
||||
});
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const confirmDeleteSelected = (ids) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
sessionStore.bulkDeleteSessions(ids);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ sessionStore.indexPageTitle }}</h2>
|
||||
</div>
|
||||
<div class="card border-[1px] border-client-gray/20 bg-white rounded-2xl">
|
||||
<TableGeneric
|
||||
:data="sessionStore.sessions"
|
||||
:columns="columns"
|
||||
:selectable="true"
|
||||
:pagination="false"
|
||||
:searchable="true"
|
||||
v-model="selectedItems"
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<template #bulk-action="{ selection }">
|
||||
<Button
|
||||
label="حذف همه نشست ها"
|
||||
size="small"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-trash"
|
||||
severity="danger"
|
||||
class="!text-xs !flex"
|
||||
outlined
|
||||
:loading="sessionStore.bulkLoader"
|
||||
@click="() => confirmDeleteSelected(selection.map(item => item.id))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #column-actions="{ data }">
|
||||
<div class="grid grid-cols-1a">
|
||||
<Button
|
||||
type="button"
|
||||
class="!w-8 !h-8"
|
||||
variant="outlined"
|
||||
severity="danger"
|
||||
icon="fi fi-rr-trash"
|
||||
:pt="{icon : '!mt-2 !text-xs'}"
|
||||
:loading="sessionStore.loader"
|
||||
@click="showDeleteConfirm(data)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</TableGeneric>
|
||||
</div>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,170 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import {onMounted , onUnmounted} from 'vue';
|
||||
import {useUserStore} from "../../store/User.js";
|
||||
import {Link} from "@inertiajs/inertia-vue3";
|
||||
import GlobalSelectMedium from "../../components/medium/GlobalSelectMedium.vue";
|
||||
import Card from "../../components/ui/Card.vue";
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const props = defineProps({
|
||||
is_edit: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
user: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
roles: {
|
||||
type: Object,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.is_edit) {
|
||||
userStore.setEditData(props.user);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
userStore.resetCreateForm();
|
||||
userStore.is_edit_form = false;
|
||||
userStore.errorsCreateForm = [];
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<Card class="mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-[1fr_auto] justify-between items-center gap-4">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ userStore.pageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="userStore.endpoints.index">
|
||||
<span class="text-sm">{{ userStore.indexPageTitle }}</span>
|
||||
<i class="fi fi-rr-list flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<section class="grid w-full grid-cols-1 lg:grid-cols-[1fr_22rem] gap-4">
|
||||
<Card>
|
||||
<div class="rounded-2xl bg-white">
|
||||
<Form ref="formRef" v-slot="$form" class=" p-4 flex flex-col gap-4 !w-full">
|
||||
<FormField v-slot="$field" name="username" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام کاربری</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
userStore.errorsCreateForm?.username && !userStore.createForm.username ? 'p-invalid' : ''
|
||||
]" v-model="userStore.createForm.username" placeholder="نام کاربری خود را وارد کنید" />
|
||||
<Message v-if="userStore.errorsCreateForm?.username"
|
||||
severity="error" size="small" variant="simple">{{ userStore.errorsCreateForm?.username[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="email" initialValue="" class="flex flex-col gap-1">
|
||||
<label>ایمیل کاربری</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
userStore.errorsCreateForm?.email && !userStore.createForm.email ? 'p-invalid' : ''
|
||||
]" v-model="userStore.createForm.email" placeholder="ایمیل خود را وارد کنید" />
|
||||
<Message v-if="userStore.errorsCreateForm?.email" severity="error" size="small" variant="simple">{{ userStore.errorsCreateForm?.email[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="national_code" initialValue="" class="flex flex-col gap-1">
|
||||
<label>کد ملی</label>
|
||||
<InputText type="text" :class="[
|
||||
'placeholder:!text-sm',
|
||||
userStore.errorsCreateForm?.national_code && !userStore.createForm.national_code ? 'p-invalid' : ''
|
||||
]" v-model="userStore.createForm.national_code" placeholder="کد ملی خود را وارد کنید" />
|
||||
<Message v-if="userStore.errorsCreateForm?.national_code" severity="error" size="small" variant="simple">{{ userStore.errorsCreateForm?.national_code[0] }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="phone" initialValue="" class="flex flex-col gap-1">
|
||||
<label>شماره تماس</label>
|
||||
<InputText type="text" class="placeholder:!text-sm" v-model="userStore.createForm.phone" placeholder="شماره تماس خود را وارد کنید" />
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="fullname" initialValue="" class="flex flex-col gap-1">
|
||||
<label>نام خانوادگی</label>
|
||||
<InputText type="text" class="placeholder:!text-sm" v-model="userStore.createForm.fullname" placeholder="نام خانوادگی خود را وارد کنید" />
|
||||
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="password" initialValue="" class="flex flex-col gap-1">
|
||||
<label>کلمه عبور</label>
|
||||
<Password type="text" :inputClass="['placeholder:!text-sm' , { 'p-invalid' : userStore.errorsCreateForm?.password && !userStore.createForm.password}]"
|
||||
v-model="userStore.createForm.password" placeholder="کلمه عبور خود را وارد کنید" :feedback="false" toggleMask fluid />
|
||||
<Message v-if="userStore.errorsCreateForm?.password"
|
||||
severity="error" size="small" variant="simple">{{ userStore.errorsCreateForm?.password[0] }}</Message>
|
||||
<p class="text-client-Fontcolor/60 text-xs font-normal">در صورتی که کلمه عبور را نمیخواهید تغییر دهید خالی بگذارید</p>
|
||||
</FormField>
|
||||
<FormField v-slot="$field" name="password_confirmation" initialValue="" class="flex flex-col gap-1">
|
||||
<label>تکرار کلمه عبور</label>
|
||||
<Password type="text" inputClass="placeholder:!text-sm" v-model="userStore.createForm.password_confirmation" placeholder="تکرار کلمه عبور خود را وارد کنید" :feedback="false" toggleMask fluid />
|
||||
<Message v-if="$field?.invalid" severity="error" size="small" variant="simple">{{ $field.error?.message }}</Message>
|
||||
</FormField>
|
||||
</Form>
|
||||
</div>
|
||||
</Card>
|
||||
<div>
|
||||
<Card>
|
||||
<div class="p-4 rounded-2xl pb-8">
|
||||
<h5>تصویر کاربر</h5>
|
||||
<global-select-medium v-model="userStore.createForm.image"></global-select-medium>
|
||||
</div>
|
||||
</Card>
|
||||
<Card class="mt-4">
|
||||
<div class="mt-4 p-4 grid grid-cols-1 gap-4 rounded-2xl">
|
||||
<div class="grid grid-cols-1 justify-between items-center">
|
||||
<span>زمان انتشار:</span>
|
||||
<CustomDatePicker v-model="userStore.createForm.datetime" class="!text-xs mt-2" type="datetime" compact-time></CustomDatePicker>
|
||||
</div>
|
||||
<div class="grid grid-cols-2a justify-between items-center">
|
||||
<span class="flex">سطح:</span>
|
||||
<SelectButton class="!text-xs" v-model="userStore.createForm.level" :options="userStore.levelOptions" />
|
||||
</div>
|
||||
<div class="grid grid-cols-2a justify-between items-center">
|
||||
<span class="flex">مقام:</span>
|
||||
<MultiSelect class="!text-xs"
|
||||
v-model="userStore.createForm.role"
|
||||
:pt="{ option : '!text-sm' }"
|
||||
placeholder="انتخاب مقام"
|
||||
optionLabel="label"
|
||||
optionValue="id"
|
||||
:options="roles" />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 justify-start">
|
||||
<Button :loading="userStore.loaderCreateForm" @click="userStore.update_and_store" type="button" severity="secondary" :label="userStore.buttonLabel" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.p-multiselect {
|
||||
height: auto;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.p-multiselect-label {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-header {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-filter {
|
||||
max-height: calc(100px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.p-multiselect .p-multiselect-item {
|
||||
font-size: 0.875rem;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,196 @@
|
||||
<script setup>
|
||||
import Master from "../../layouts/master.vue";
|
||||
import { ref, reactive } from 'vue';
|
||||
import TableGeneric from "../../components/TableGeneric.vue";
|
||||
import { Link } from "@inertiajs/inertia-vue3";
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useUserStore } from "../../store/User.js";
|
||||
const userStore = useUserStore();
|
||||
const toast = useToast();
|
||||
|
||||
const props = defineProps({
|
||||
users: {
|
||||
type: Object,
|
||||
},
|
||||
search: {
|
||||
type: String
|
||||
},
|
||||
level: {
|
||||
type: String
|
||||
}
|
||||
})
|
||||
|
||||
const users = ref(props.users ?? []);
|
||||
const selectedItems = ref([]);
|
||||
const searchLoader = ref(false);
|
||||
|
||||
const columns = ref(userStore.columns)
|
||||
|
||||
const queryParams = reactive({
|
||||
search: props.search ?? '',
|
||||
level: props.level ?? ''
|
||||
})
|
||||
|
||||
const confirm = useConfirm();
|
||||
|
||||
const onPageChange = (event) => {
|
||||
userStore.navigateToPage(event.page + 1);
|
||||
};
|
||||
|
||||
const searchData = () => {
|
||||
searchLoader.value = true;
|
||||
userStore.search(queryParams).then((response) => {
|
||||
searchLoader.value = false;
|
||||
users.value = response.props.users.data;
|
||||
});
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
userStore.exportData({
|
||||
level: queryParams.level,
|
||||
search: queryParams.search
|
||||
});
|
||||
}
|
||||
|
||||
const onSelectionChange = (selection) => {
|
||||
selectedItems.value = selection;
|
||||
};
|
||||
|
||||
const onSearchChange = (value) => {
|
||||
queryParams.search = value;
|
||||
searchData();
|
||||
};
|
||||
|
||||
const showDeleteConfirm = (data) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
userStore.deleteRecord(data.id);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const confirmDeleteSelected = (ids) => {
|
||||
confirm.require({
|
||||
group: 'deleteTemplate',
|
||||
header: 'مطمعن هستید؟',
|
||||
message: 'اگر به عملیات اطمینان دارید، تأیید کنید.',
|
||||
icon: 'fi fi-rr-info',
|
||||
rejectProps: {
|
||||
label: 'انصراف',
|
||||
icon: 'fi fi-rr-ban',
|
||||
outlined: true,
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
acceptProps: {
|
||||
label: 'حذف',
|
||||
icon: 'fi fi-rr-check',
|
||||
size: 'small',
|
||||
class: '!text-xs !flex pt-1 !item-center',
|
||||
iconClass: '!flex !text-xs',
|
||||
},
|
||||
accept: () => {
|
||||
userStore.bulkDelete(ids);
|
||||
},
|
||||
reject: () => {
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
|
||||
<h2 class="font-bold text-client-Fontcolor">{{ userStore.indexPageTitle }}</h2>
|
||||
<Button severity="secondary" asChild v-slot="slotProps">
|
||||
<Link :class="slotProps.class" :href="userStore.endpoints.create">
|
||||
<span class="text-sm">{{ userStore.pageTitle }}</span>
|
||||
<i class="fi fi-rr-plus flex text-sm"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div class="card border-[1px] border-client-gray/20 bg-white rounded-2xl">
|
||||
<TableGeneric
|
||||
:data="users.data"
|
||||
:columns="columns"
|
||||
:selectable="true"
|
||||
:pagination="users.meta"
|
||||
:searchable="true"
|
||||
:searchValue="queryParams.search"
|
||||
v-model="selectedItems"
|
||||
@selection-change="onSelectionChange"
|
||||
@page-change="onPageChange"
|
||||
@search-change="onSearchChange"
|
||||
>
|
||||
<template #bulk-action="{ selection }">
|
||||
<Button
|
||||
label="حذف همه"
|
||||
size="small"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-trash"
|
||||
severity="danger"
|
||||
class="!text-xs !flex"
|
||||
outlined
|
||||
@click="() => confirmDeleteSelected(selection.map(user => user.id))"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #toolbar-actions>
|
||||
<div class="grid grid-cols-a gap-4 justify-start">
|
||||
<Button
|
||||
label="خروجی اکسل"
|
||||
:pt="{icon : 'flex'}"
|
||||
icon="fi fi-rr-file-export"
|
||||
class="!text-xs !bg-client-primary"
|
||||
@click="exportData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #column-actions="{ data }">
|
||||
<div class="grid grid-cols-2a justify-start gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
class="!w-8 !h-8"
|
||||
variant="outlined"
|
||||
severity="danger"
|
||||
icon="fi fi-rr-trash"
|
||||
:pt="{icon : '!mt-2 !text-xs'}"
|
||||
@click="showDeleteConfirm(data)"
|
||||
/>
|
||||
<Button severity="info" variant="outlined" asChild v-slot="slotProps">
|
||||
<Link :class="[slotProps.class , '!w-8 !h-8']" :href="userStore.endpoints.edit(data.id)">
|
||||
<i class="fi fi-rr-pen-clip flex !text-xs"></i>
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</TableGeneric>
|
||||
</div>
|
||||
</master>
|
||||
</template>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script setup>
|
||||
import Master from "../layouts/master.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<master>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
Radically dashboard
|
||||
</div>
|
||||
</master>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user