Commit 9ff5f1a8 by asranov0003

feat: add unsubscribe btn

parent b4eebb44
......@@ -126,7 +126,9 @@
"deleteImages": "Delete images",
"deleteImagesDesc": "Are you sure you want to delete all images?",
"deleteMedia": "Delete media",
"deleteMediaDesc": "Are you sure you want to delete all media files?"
"deleteMediaDesc": "Are you sure you want to delete all media files?",
"unsubscribe": "Unsubscribe",
"unsubscribeDesc": "Are you sure you want to unsubscribe?"
},
"permissions": {
"title": "Permissions",
......
......@@ -126,7 +126,9 @@
"deleteImages": "Удалить изображения",
"deleteImagesDesc": "Вы действительно хотите удалить все изображения?",
"deleteMedia": "Удалить медиа",
"deleteMediaDesc": "Вы действительно хотите удалить все медиа-файлы?"
"deleteMediaDesc": "Вы действительно хотите удалить все медиа-файлы?",
"unsubscribe": "Отписаться",
"unsubscribeDesc": "Вы уверены, что хотите отписаться?"
},
"permissions": {
"title": "Разрешения",
......
......@@ -126,7 +126,9 @@
"deleteImages": "Rasm fayllarni o'chirish",
"deleteImagesDesc": "Siz haqiqatan ham barcha rasm fayllarni o'chirmoqchimisiz?",
"deleteMedia": "Media fayllarni o'chirish",
"deleteMediaDesc": "Siz haqiqatan ham barcha media fayllarni o'chirmoqchimisiz?"
"deleteMediaDesc": "Siz haqiqatan ham barcha media fayllarni o'chirmoqchimisiz?",
"unsubscribe": "Obunani bekor qilish",
"unsubscribeDesc": "Obunani bekor qilishni xohlaysizmi?"
},
"permissions": {
"title": "Ruxsatlar",
......
......@@ -7,7 +7,11 @@ import ru from "../../assets/images/flag-ru.png";
import uz from "../../assets/images/flag-uz.png";
import { FiUserMinus } from "react-icons/fi";
import { IoKeyOutline, IoPhonePortraitOutline } from "react-icons/io5";
import { MdOutlineEdit, MdOutlineLogout } from "react-icons/md";
import {
MdOutlineCreditCardOff,
MdOutlineEdit,
MdOutlineLogout,
} from "react-icons/md";
import { useTranslation } from "react-i18next";
import {
useAppDispatch,
......@@ -36,6 +40,10 @@ import {
editDevice,
refreshDevices,
} from "../../stores/slices/deviceSlice";
import {
checkBalance,
toggleSubscriptionAuto,
} from "../../stores/slices/billingSlice";
const deleteDatas = [
{
......@@ -76,6 +84,7 @@ const Settings: React.FC = () => {
const [isOpenEditDevice, setIsOpenEditDevice] = useState(false);
const [isOpenDeleteDevice, setIsOpenDeleteDevice] = useState(false);
const [isOpenDeleteDataModal, setIsOpenDeleteDataModal] = useState(false);
const [isOpenUnsubscribeModal, setIsOpenUnsubscribeModal] = useState(false);
const { language, changeLanguage } = useLanguage();
const { t } = useTranslation();
const { isCheckPasswordLoading, errorCheckPassword } = useAppSelector(
......@@ -87,6 +96,9 @@ const Settings: React.FC = () => {
const { devices, isDeletingDevice, isEditingDevice } = useAppSelector(
(state: RootState) => state.device
);
const { balance, isToggleSubAutoLoading } = useAppSelector(
(state: RootState) => state.billing
);
const dispatch = useAppDispatch();
const toggleLogoutModal = () => {
......@@ -117,6 +129,10 @@ const Settings: React.FC = () => {
setIsOpenDeleteDevice((prev) => !prev);
};
const toggleIsOpenUnsubscribeModal = () => {
setIsOpenUnsubscribeModal((prev) => !prev);
};
const handleEditDevice = async () => {
try {
if (selectedDevice) {
......@@ -175,6 +191,10 @@ const Settings: React.FC = () => {
}
}, [session]);
useEffect(() => {
dispatch(checkBalance());
}, [dispatch]);
return (
<div className="settings">
<h3 className="settings__title">{t("settings.title")}</h3>
......@@ -327,6 +347,16 @@ const Settings: React.FC = () => {
{t("settings.deleteAccount")}
</div>
{balance.subAuto && (
<div
className="settings__content__action"
onClick={toggleIsOpenUnsubscribeModal}
>
<MdOutlineCreditCardOff className="settings__content__action__icon" />
{t("settings.unsubscribe")}
</div>
)}
<Link
to={"/settings/recover-password"}
className="settings__content__action"
......@@ -482,6 +512,34 @@ const Settings: React.FC = () => {
</div>
}
/>
<CModal
isOpen={isOpenUnsubscribeModal}
onToggle={toggleIsOpenUnsubscribeModal}
content={
<div className="modal__box">
<h3 className="modal__box__title">{t("settings.unsubscribe")}</h3>
<p style={{ textAlign: "center" }}>
{t("settings.unsubscribeDesc")}
</p>
<div className="modal__box__actions">
<CButton
title={t("button.cancel")}
onClick={toggleIsOpenUnsubscribeModal}
/>
<CButton
title={t("button.ok")}
variant="danger"
isLoading={isToggleSubAutoLoading}
onClick={async () => {
await dispatch(toggleSubscriptionAuto(false));
toggleIsOpenUnsubscribeModal();
}}
/>
</div>
</div>
}
/>
</div>
);
};
......
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { sendRpcRequest } from "../../services/apiClient";
import type { IPaymentMethod, ITariff } from "../../types/billing.types";
import type {
IBalance,
IPaymentMethod,
ITariff,
} from "../../types/billing.types";
interface IBillingState {
tariffs: ITariff[];
......@@ -11,6 +15,8 @@ interface IBillingState {
paymentMethodsError: string | null;
payUrl: string;
isPayLoading: boolean;
balance: IBalance;
isToggleSubAutoLoading: boolean;
}
const initialState: IBillingState = {
......@@ -22,6 +28,8 @@ const initialState: IBillingState = {
paymentMethodsError: null,
payUrl: "",
isPayLoading: false,
balance: { balance: 0, subAuto: false },
isToggleSubAutoLoading: false,
};
export const fetchTariffs = createAsyncThunk(
......@@ -87,6 +95,38 @@ export const pay = createAsyncThunk(
}
);
export const checkBalance = createAsyncThunk(
"billing/checkBalance",
async (_, { rejectWithValue }) => {
try {
const response = await sendRpcRequest<IBalance>("billing.balance");
return response;
} catch (error: unknown) {
if (typeof error === "object" && error !== null && "message" in error) {
return rejectWithValue(error.message);
}
return rejectWithValue("Unknown error occurred");
}
}
);
export const toggleSubscriptionAuto = createAsyncThunk(
"billing/toggleSubscriptionAuto",
async (value: boolean, { rejectWithValue }) => {
try {
await sendRpcRequest("billing.setsubsauto", { value });
return value;
} catch (error: unknown) {
if (typeof error === "object" && error !== null && "message" in error) {
return rejectWithValue(error.message);
}
return rejectWithValue("Unknown error occurred");
}
}
);
const billingSlice = createSlice({
name: "billing",
initialState,
......@@ -130,6 +170,21 @@ const billingSlice = createSlice({
.addCase(pay.rejected, (state, action) => {
state.isPayLoading = false;
state.payUrl = action.payload as string;
})
.addCase(checkBalance.fulfilled, (state, action) => {
state.balance = action.payload;
})
.addCase(toggleSubscriptionAuto.pending, (state) => {
state.isToggleSubAutoLoading = true;
})
.addCase(toggleSubscriptionAuto.fulfilled, (state, action) => {
state.isToggleSubAutoLoading = false;
state.balance.subAuto = action.payload;
})
.addCase(toggleSubscriptionAuto.rejected, (state) => {
state.isToggleSubAutoLoading = false;
});
},
});
......
......@@ -12,3 +12,8 @@ export interface IPaymentMethod {
icon: string;
enabled: boolean;
}
export interface IBalance {
balance: number;
subAuto: boolean;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment