base_ui/components/charts/Tree.vue
2025-02-11 10:38:54 +03:30

806 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<v-chart
:key="treeCounter"
:option="option"
style="height: 90dvh; width: 90%"
ref="chart"
@mouseover="handleChartClick"
lazy
></v-chart>
<template v-if="showDiv">
<div
v-if="showButtons"
class="rounded shadow container-fluid p-2"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
:style="{
position: 'absolute',
top: position.top,
left: position.left,
backgroundColor: 'white',
border: '1px solid #dee2e6',
padding: '5px',
width: '15em',
}"
>
<div class="row me-auto tooltip-button">
<div class="col-9 d-flex border-bottom mb-1">
<button
v-tooltip="`افزودن فرزند`"
class="btn"
style="color: #dee2e6"
@click="onButtonClick('add')"
>
<svg class="icon icon-Component-133--1">
<use xlink:href="#icon-Component-133--1"></use>
</svg>
</button>
<button
v-tooltip="`ویرایش کردن`"
class="btn"
@click="onButtonClick('edit')"
>
<svg class="icon icon-Component-242--1">
<use xlink:href="#icon-Component-242--1"></use>
</svg>
</button>
<button
v-tooltip="`پاک کردن`"
class="btn"
@click="onButtonClick('delete')"
>
<svg class="icon icon-Component-295--1">
<use xlink:href="#icon-Component-295--1"></use>
</svg>
</button>
</div>
<div class="col-3 mb-1">
<button v-tooltip="`خارج شدن`" class="btn" @click="closeButtons">
<svg class="icon icon-Component-21--1">
<use xlink:href="#icon-Component-21--1"></use>
</svg>
</button>
</div>
</div>
<div class="row my-3">
<div v-if="buttonAction == 'add'" class="col w-90 px-3">
<form>
<div class="row align-items-center">
<div class="col-8 m-0">
<input
type="text"
class="form-control"
id="inlineFormInputName"
placeholder="کلمه جدید"
v-model="addText"
/>
</div>
<div class="col-2">
<button
class="btn accept-button"
@click.prevent="addNewChildren(addText)"
>
<svg class="icon icon-Component-233--1">
<use xlink:href="#icon-Component-233--1"></use>
</svg>
</button>
</div>
<div class="col-2">
<button class="btn reject-button" @click="buttonAction = ''">
<svg class="icon icon-Component-21--1">
<use xlink:href="#icon-Component-21--1"></use>
</svg>
</button>
</div>
</div>
</form>
</div>
<div v-else-if="buttonAction == 'edit'" class="col w-90 px-3">
<form>
<div class="row align-items-center">
<div class="col-8">
<input
type="text"
class="form-control"
id="inlineFormInputName"
:placeholder="tooltipText"
v-model="editText"
/>
</div>
<div class="col-2">
<button
class="btn accept-button"
@click.prevent="editNode(editText)"
>
<svg class="icon icon-Component-233--1">
<use xlink:href="#icon-Component-233--1"></use>
</svg>
</button>
</div>
<div class="col-2">
<button class="btn reject-button" @click="buttonAction = ''">
<svg class="icon icon-Component-21--1">
<use xlink:href="#icon-Component-21--1"></use>
</svg>
</button>
</div>
</div>
</form>
</div>
<div v-else class="col">
<p>{{ tooltipText }}</p>
</div>
</div>
</div>
</template>
</div>
</template>
<script>
import researchApi from "@apis/researchApi";
import HttpService from "@services/httpService";
import { mapActions, mapState } from "pinia";
import { useCommonStore } from "~/stores/commonStore";
//
import { use } from "echarts/core";
import { TreeChart } from "echarts/charts";
import { TooltipComponent, ToolboxComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
use([TooltipComponent, TreeChart, CanvasRenderer, ToolboxComponent]);
/**
* @vue-prop {Array} [dataTreeMap=] - آرایه‌ای از داده‌های نقشه درخت
*
* @vue-data {Number} treeCounter - شمارنده درخت
* @vue-data {Boolean} showButtons - وضعیت نمایش دکمه‌ها
* @vue-data {Boolean} showDiv - وضعیت نمایش divدکمه ها
* @vue-data {String} buttonAction - اقدام دکمه
* @vue-data {String} addText - متن افزودن
* @vue-data {String} editText - متن ویرایش
* @vue-data {undefined|Object} httpService - سرویس HTTP
* @vue-data {Object} itemData - داده آیتم
* @vue-data {String} tooltipText - متن ابزارک راهنما
* @vue-data {Object} position - موقعیت ابزارک راهنما
* @vue-data {String} position.top - موقعیت عمودی ابزارک راهنما
* @vue-data {String} position.left - موقعیت افقی ابزارک راهنما
* @vue-data {Object} option - گزینه‌های پیکربندی نمودار
* @vue-data {Object} option.tooltip - تنظیمات ابزارک راهنما
* @vue-data {String} option.tooltip.trigger - نوع راه‌اندازی ابزارک راهنما
* @vue-data {String} option.tooltip.triggerOn - رویدادی که ابزارک راهنما بر اساس آن راه‌اندازی می‌شود
* @vue-data {Array} option.series - آرایه‌ای از سری‌های نمودار
* @vue-data {Object} option.series[0] - سری اول نمودار
* @vue-data {String} option.series[0].type - نوع سری (درخت)
* @vue-data {Number} option.series[0].id - شناسه سری
* @vue-data {String} option.series[0].name - نام سری
* @vue-data {Array} option.series[0].data - داده‌های سری
* @vue-data {String} option.series[0].top - موقعیت بالای نمودار
* @vue-data {String} option.series[0].left - موقعیت چپ نمودار
* @vue-data {String} option.series[0].bottom - موقعیت پایین نمودار
* @vue-data {String} option.series[0].right - موقعیت راست نمودار
* @vue-data {String} option.series[0].while - عرض نمودار
* @vue-data {String} option.series[0].height - ارتفاع نمودار
* @vue-data {Number} option.series[0].zoom - میزان بزرگ‌نمایی نمودار
* @vue-data {Number} option.series[0].symbolSize - اندازه نماد
* @vue-data {String} option.series[0].edgeForkPosition - موقعیت شاخه‌های لبه
* @vue-data {String} option.series[0].edgeShape - شکل لبه
* @vue-data {Number} option.series[0].initialTreeDepth - عمق اولیه درخت
* @vue-data {String} option.series[0].orient - جهت نمودار
* @vue-data {Object} option.series[0].lineStyle - سبک خط
* @vue-data {Number} option.series[0].lineStyle.width - عرض خط
* @vue-data {Object} option.series[0].label - برچسب‌ها
* @vue-data {String} option.series[0].label.backgroundColor - رنگ پس‌زمینه برچسب
* @vue-data {Array} option.series[0].label.position - موقعیت برچسب
* @vue-data {Number} option.series[0].label.distance - فاصله برچسب
* @vue-data {String} option.series[0].label.verticalAlign - تراز عمودی برچسب
* @vue-data {String} option.series[0].label.align - تراز برچسب
* @vue-data {Number} option.series[0].label.borderWidth - عرض مرز برچسب
* @vue-data {Number} option.series[0].label.borderDashOffset - فاصله نقطه‌چین مرز برچسب
* @vue-data {Array} option.series[0].label.borderRadius - شعاع گوشه‌های مرز برچسب
* @vue-data {Array} option.series[0].label.padding - پدینگ برچسب
* @vue-data {Number} option.series[0].label.lineHeight - ارتفاع خط برچسب
* @vue-data {Object} option.series[0].leaves - برگ‌ها
* @vue-data {Object} option.series[0].leaves.label - برچسب‌های برگ
* @vue-data {String} option.series[0].leaves.label.position - موقعیت برچسب برگ
* @vue-data {String} option.series[0].leaves.label.verticalAlign - تراز عمودی برچسب برگ
* @vue-data {String} option.series[0].leaves.label.align - تراز برچسب برگ
* @vue-data {Object} option.series[0].emphasis - تاکید
* @vue-data {String} option.series[0].emphasis.focus - تمرکز تاکید
* @vue-data {String} option.series[0].emphasis.blurScope - محدوده مات شدن تاکید
* @vue-data {Boolean} option.series[0].expandAndCollapse - وضعیت باز و بسته شدن
* @vue-data {Number} option.series[0].animationDuration - مدت زمان انیمیشن
* @vue-data {Number} option.series[0].animationDurationUpdate - مدت زمان به‌روزرسانی انیمیشن
*/
export default {
props: {
dataTreeMap: {
default() {
return [];
},
type: Array,
},
},
data() {
return {
treeCounter: 1,
showButtons: false,
showDiv: false,
buttonAction: "",
addText: "",
editText: "",
httpService: undefined,
itemData: {},
tooltipText: "",
position: { top: "0px", left: "0px" },
option: {
tooltip: {
trigger: "item",
triggerOn: "mousemove",
},
toolbox: {
show: true,
itemSize: 15,
feature: {
myTool1: {
show: true,
title: " نمایش ",
icon: '<path d="M19.017 16.243c-1.431-0.009-2.588-1.166-2.597-2.596v-0.001c0.004-0.466 0.133-0.902 0.354-1.276l-0.006 0.012c-0.112-0.009-0.229-0.035-0.349-0.035-2.152 0-3.896 1.744-3.896 3.896s1.744 3.896 3.896 3.896c2.152 0 3.896-1.744 3.896-3.896v0c0-0.12-0.025-0.231-0.035-0.348-0.362 0.215-0.797 0.344-1.261 0.348h-0.001z"></path><path d="M16.42 7.152c-0.067-0.001-0.146-0.002-0.225-0.002-5.746 0-10.661 3.558-12.662 8.591l-0.032 0.092-0.133 0.411 0.133 0.411c2.035 5.125 6.951 8.683 12.697 8.683 0.079 0 0.157-0.001 0.236-0.002h-0.012c0.067 0.001 0.146 0.002 0.225 0.002 5.746 0 10.661-3.558 12.662-8.591l0.032-0.092 0.133-0.411-0.133-0.411c-2.035-5.125-6.951-8.683-12.697-8.683-0.079 0-0.157 0.001-0.236 0.002h0.012zM16.42 22.737c-0.107 0.004-0.234 0.006-0.36 0.006-4.417 0-8.218-2.642-9.907-6.432l-0.027-0.069c1.718-3.858 5.518-6.499 9.935-6.499 0.127 0 0.253 0.002 0.378 0.006l-0.018-0.001c0.107-0.004 0.234-0.006 0.36-0.006 4.417 0 8.218 2.642 9.907 6.432l0.027 0.069c-1.718 3.858-5.518 6.499-9.935 6.499-0.127 0-0.253-0.002-0.378-0.006h0.018z"></path>',
onclick: function () {
this.myToolClick("نمایش");
}.bind(this),
iconStyle: {
color: "",
},
},
myTool2: {
show: true,
title: " ویرایش ",
icon: '<path d="M9.333 25.856c-0.001 0-0.001 0-0.002 0-0.127 0-0.246-0.031-0.352-0.085l0.004 0.002-0.027 0.019-0.123-0.117c-0.024-0.021-0.046-0.041-0.066-0.064l-0-0-3.249-3.105 2.035-3.256 4.533 4.38-1.007 0.695h12.192c0.423 0 0.765 0.343 0.765 0.765s-0.343 0.765-0.765 0.765v0zM8.332 17.375l11.501-10.416c0.521-0.631 1.304-1.031 2.18-1.031 0.735 0 1.404 0.281 1.906 0.741l-0.002-0.002 1.333 1.5c0.385 0.41 0.621 0.963 0.621 1.571 0 0.714-0.325 1.351-0.836 1.773l-0.004 0.003-10.74 11.569z"></path>',
onclick: function () {
this.myToolClick("ویرایش");
}.bind(this),
iconStyle: {
color: "black",
},
},
},
},
series: [
{
type: "tree",
id: 0,
name: "tree1",
data: [],
top: "0%",
left: "40%",
bottom: "30%",
right: "5%",
while: "100%",
height: "100%",
zoom: 1,
symbolSize: 10,
// roam: true,
edgeForkPosition: "30%",
edgeShape: "curve",
// expandAndCollapse: true,
initialTreeDepth: 5,
orient: "RL",
// symbol: "none",
lineStyle: {
width: 2,
},
label: {
color: "#fff",
fontFamily: "sahel",
fontSize: 15,
align: "center",
position: "left",
verticalAlign: "middle",
borderRadius: 3,
padding: 6,
borderWidth: 2,
// shadowColor: "rgba(51, 41, 41, 1)",
// shadowBlur: 2.5,
// shadowOffsetX: 2,
// shadowOffsetY: 2,
width: 140,
height: 12,
backgroundColor: "#ee6666",
distance: 80,
formatter: function (params) {
const maxLength = 15;
const text =
params.name.length > maxLength
? params.name.substring(0, maxLength) + "..."
: params.name;
return text;
},
},
leaves: {
label: {
distance: 80,
color: "#fff",
fontFamily: "sahel",
fontSize: 15,
align: "center",
position: "left",
verticalAlign: "middle",
borderRadius: 3,
padding: 6,
borderWidth: 2,
// shadowColor: "rgba(51, 41, 41, 1)",
// shadowBlur: 2.5,
// shadowOffsetX: 2,
// shadowOffsetY: 2,
width: 140,
height: 12,
backgroundColor: "#fff",
formatter: function (params) {
const maxLength = 15;
const text =
params.name.length > maxLength
? params.name.substring(0, maxLength) + "..."
: params.name;
return text;
},
},
},
lineStyle: {
curveness: 0.6,
},
emphasis: {
focus: "relative",
blurScope: "coordinateSystem",
},
expandAndCollapse: true,
animationDuration: 550,
animationDurationUpdate: 750,
symbolOffset: [30, 0],
},
],
},
};
},
watch: {
dataTreeMap: {
handler(newData) {
this.option.series[0].data = newData;
this.treeCounter++;
},
deep: true,
immediate: true,
},
dataForTreeMapGetter(newValue) {
this.option.series[0].data = newValue;
this.treeCounter++;
},
},
beforeMount() {
this.httpService = new HttpService();
},
mounted() {
this.option.series[0].data = this.dataForTreeMapGetter;
this.option.toolbox.feature.myTool1.iconStyle.color = "black";
this.option.toolbox.feature.myTool2.iconStyle.color = "#fff";
},
computed: {
...mapState(useCommonStore, [
// "dataForTreeMapGetter",
"researchTermsGetter",
]),
},
methods: {
// ...mapActions(useResearchStore, ["dataForTreeMapSetter"]),
/**
*تغیرات مربوط به حالت نمایش و ویرایش در چارت
*/
myToolClick(item) {
if (item == "نمایش") {
this.showDiv = false;
this.option.toolbox.feature.myTool1.iconStyle.color = "black";
this.option.toolbox.feature.myTool2.iconStyle.color = "#fff";
} else {
this.showDiv = true;
this.option.toolbox.feature.myTool1.iconStyle.color = "#fff";
this.option.toolbox.feature.myTool2.iconStyle.color = "black";
}
},
/**
* نشان دادن دکمه‌ها در هنگام وارد شدن ماوس
*/
onMouseEnter() {
this.showButtons = true;
},
/**
* پنهان کردن دکمه‌ها در هنگام خارج شدن ماوس
*/
onMouseLeave() {
this.showButtons = false;
},
/**
* وقتی کاربر روی نقاط چارت کلیک می‌کند، این تابع وظیفه نمایش اطلاعات مربوط به آن نقطه را دارد.
* @param {Object} params - اطلاعات مربوط به کلیک شده روی چارت
*/
handleChartClick(params) {
const { offsetX, offsetY } = params.event;
// تنظیم موقعیت نمایش اطلاعات
this.position = { top: `${offsetY - 150}px`, left: `${offsetX}px` };
// نمایش متن توضیحات
this.tooltipText = params.name || "No data";
// ذخیره اطلاعات آیتم کلیک شده
this.itemData = params;
this.buttonAction = "";
this.addText = "";
this.editText = "";
// نمایش دکمه‌های مربوط به این آیتم
this.showButtons = true;
},
/**
* وقتی کاربر روی دکمه‌های اضافه کردن یا حذف کلیک می‌کند، این تابع عملکرد متناسب با دکمه انجام می‌دهد.
* @param {string} buttonName - نام دکمه‌ای که کلیک شده است ('add' یا 'delete')
*/
onButtonClick(buttonName) {
this.buttonAction = buttonName;
// تعیین عملیات اضافه کردن یا حذف بر اساس دکمه انتخاب شده
if (buttonName == "add") this.fetchingExistingChildren();
else if (buttonName == "delete") this.removeNode();
// else if (buttonName == "edit") this.editNode();
},
/**
* این تابع دکمه‌های نمایش داده شده بر روی چارت را پنهان می‌کند.
*/
closeButtons() {
this.showButtons = false;
},
/**
* این تابع مسئول اضافه کردن آیتم‌های جدید به چارت است.
* @param {string} item - عنوان آیتم جدید
*/
addNewChildren(item) {
let data = this.itemData.data;
// تهیه پارامترهای مورد نیاز برای ارسال درخواست
const payload = {
projectid: this.researchTermsGetter?.id,
parent: "",
listtype: 0,
title: item,
listid: "",
id: "",
};
// تنظیم مقادیر پارامترها بر اساس شرایط
if (data.pid === "Root") {
payload.id = undefined;
payload.parent = 0;
delete payload.listid;
} else {
payload.listid = data?.id;
payload.parent = data?.id;
delete payload.id;
}
// ارسال درخواست به سرور
let url = researchApi.subject.add;
this.httpService
.postRequest(url, payload)
.then((res) => {
// نمایش پیام موفقیت
this.mySwalToast({
title: "موفق",
html: " با موفقیت انجام شد ",
icon: "success",
});
// اضافه کردن آیتم‌های جدید به داده‌های مربوطه
res.data.forEach((element, index) => {
var node = {
item: element,
text: element.title,
name: element.title,
id: element.id,
pid: 0,
opened: false,
selected: false,
disabled: false,
loading: false,
children: [],
};
data.children.push(node);
// اضافه کردن آیتم به چارت
if (payload.parent === 0) {
this.option.series[0].data[0].children.push(node);
} else {
this.addChildToParent(
this.option.series[0].data[0],
payload.parent,
node
);
}
this.treeCounter++;
});
})
.catch((error) => {
// نمایش پیام خطا
console.error("Error adding new children:", error);
this.mySwalToast({
title: "خطا",
html: "مشکلی پیش آمد",
icon: "error",
});
})
.finally(() => {
this.buttonAction = "";
});
},
/**
* این تابع مسئول اضافه کردن یک آیتم فرزند به آیتم پدر می‌باشد.
* @param {Object} node - آیتم پدر
* @param {string} parentId - شناسه آیتم پدر
* @param {Object} newChild - آیتم فرزند جدید
* @returns {boolean} - مقدار منطقی True در صورت موفقیت آمیز بودن اضافه کردن، در غیر این صورت False
*/
addChildToParent(node, parentId, newChild) {
if (node.id === parentId) {
node.children.push(newChild);
return true;
}
if (node.children) {
for (let child of node.children) {
if (this.addChildToParent(child, parentId, newChild)) {
return true;
}
}
}
return false;
},
/**
* این تابع مسئول دریافت و نمایش آیتم‌های موجود زیر یک آیتم پدر است.
*/
fetchingExistingChildren() {
let dataParent = this.itemData.data;
if (dataParent.children.length > 1) {
return;
}
try {
// دریافت لیست آیتم‌های زیر یک آیتم پدر از سرور
this.getListTree(dataParent.id).then((list) => {
// اضافه کردن هر آیتم به لیست فرزندان آیتم پدر
list.forEach((element, index) => {
const dataColor = dataParent.label.backgroundColor;
var node = {
item: element,
text: element.title,
name: element.title,
id: element.id,
pid: dataParent.id,
opened: false,
selected: false,
disabled: false,
loading: false,
children: [],
label: {
backgroundColor: dataColor,
},
itemStyle: {
color: dataColor,
},
};
dataParent.children.push(node);
// اضافه کردن آیتم به چارت
if (dataParent.id === 0) {
this.option.series[0].data[0].children.push(node);
} else {
this.addChildToParent(
this.option.series[0].data[0],
dataParent.id,
node
);
}
this.treeCounter++;
});
});
} catch (error) {
console.error("Error fetching children nodes:", error);
}
},
/**
* این تابع مسئول حذف یک آیتم از چارت می‌باشد.
*/
removeNode() {
let removeData = this.itemData.data;
if (removeData.pid === "Root") return;
this.mySwalConfirm({
title: "هشدار!!!",
html: `از حذف <b>${removeData.text}</b> اطمینان دارید؟ `,
icon: "warning",
}).then((result) => {
if (result.isConfirmed) {
this.getRemov(removeData);
this.closeButtons();
}
});
},
/**
* این تابع مسئول حذف یک آیتم از چارت می‌باشد.
* @param {Object} nodeToRemove - آیتمی که قرار است حذف شود
*/
removeNodeFromChart(nodeToRemove) {
// حذف آیتم از چارت
if (nodeToRemove.pid === 0) {
let index = this.option.series[0].data[0].children.findIndex(
(child) => child.item.id === nodeToRemove.item.id
);
if (index !== -1) {
this.option.series[0].data[0].children.splice(index, 1);
}
} else {
let parentData = this.getParentData(nodeToRemove);
if (parentData) {
let index = parentData.children.findIndex(
(child) => child.item.id === nodeToRemove.item.id
);
if (index !== -1) {
parentData.children.splice(index, 1);
}
}
}
this.treeCounter++; // افزایش شمارنده درخت
},
/**
* این تابع مسئول یافتن و بازگشت آیتم پدر مربوط به یک آیتم است.
* @param {Object} node - آیتم فرزند
* @returns {Object} - آیتم پدر
*/
getParentData(node) {
let parentData = null;
// جستجوی آیتم پدر با استفاده از شناسه آن
function findParent(nodeId, data) {
for (let i = 0; i < data.length; i++) {
if (data[i].id === nodeId) {
parentData = data[i];
break;
} else if (data[i].children && data[i].children.length) {
findParent(nodeId, data[i].children);
}
}
}
// فراخوانی تابع جستجوی آیتم پدر
findParent(node.pid, this.option.series[0].data[0].children);
return parentData;
},
/**
* این تابع مسئول دریافت لیست آیتم‌های زیر یک آیتم پدر از سرور است.
* @param {string} parentId - شناسه آیتم پدر
* @returns {Promise} - لیست آیتم‌های زیر آیتم پدر
*/
getListTree(parentId = 0) {
const payload = {
projectid: this.researchTermsGetter?.id,
parent: parentId,
sortby: "id",
offset: 0,
limit: 100,
listtype: 0,
};
let url = researchApi.subject.list;
// ارسال درخواست به سرور و بازگشت پاسخ به صورت Promise
return this.httpService.formDataRequest(url, payload).then((res) => {
return res.data;
});
},
/**
* این تابع مسئول حذف یک آیتم از چارت می‌باشد.
* @param {Object} item - آیتمی که قرار است حذف شود
*/
getRemov(item) {
const payload = {
subjectid: item.id,
projectid: this.researchTermsGetter?.id,
listid: item.id,
};
let url = researchApi.subject.delete;
this.httpService.postRequest(url, payload).then((res) => {
this.mySwalToast({
title: "موفق",
html: "با موفقیت حذف شد",
icon: "success",
});
// حذف آیتم از چارت
this.removeNodeFromChart(item);
});
},
/**
* این تابع مسئول ویرایش یک آیتم از چارت می‌باشد.
* @param {string} item - عنوان جدید آیتم
*/
editNode(item) {
let data = this.itemData.data;
const payload = {
subjectid: data.id,
projectid: this.researchTermsGetter?.id,
title: item,
};
let url = researchApi.subject.edit;
this.httpService
.formDataRequest(url, payload)
.then((res) => {
this.mySwalToast({
title: "موفق",
html: "تغییر نام با موفقیت انجام شد ",
icon: "success",
});
data.name = item;
data.text = item;
// پیدا کردن و به‌روز رسانی آیتم در چارت
const updateNodeText = (node, id, newText) => {
if (node.id === id) {
node.name = newText;
node.text = newText;
return true;
}
if (node.children) {
for (let child of node.children) {
if (updateNodeText(child, id, newText)) {
return true;
}
}
}
return false;
};
updateNodeText(this.option.series[0].data[0], data.id, item);
this.treeCounter++;
this.closeButtons();
})
.finally(() => {
this.buttonAction = "";
});
},
},
};
</script>
<style lang="scss">
.tooltip-button {
button {
color: #dee2e6 !important;
&:hover {
color: black !important;
}
}
}
.reject-button {
color: #dee2e6 !important;
&:hover {
color: rgb(231, 10, 10) !important;
}
}
.accept-button {
color: #dee2e6 !important;
&:hover {
color: rgb(37, 223, 12) !important;
}
}
</style>