Commit 85392706 by asranov0003

feat: fetch tariffs and payment methods

parent dddd8417
.subscription { .subscription {
padding-top: 60px; padding-top: 60px;
} }
.subscription .cbtn {
margin: 1rem 0;
}
.subscription__tariffs__list {
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.5rem;
margin: 1rem 0;
}
.subscription__tariff {
min-width: 100px;
padding: 1rem;
border-radius: 10px;
background: var(--content-bg-color);
text-align: center;
cursor: pointer;
}
.subscription__tariff-selected {
background: var(--primary-color);
color: var(--on-text-color);
}
.subscription__payments__list {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1rem 0;
}
.subscription__payment {
width: 100px;
height: 100px;
padding: 1rem;
border-radius: 10px;
background: var(--content-bg-color);
text-align: center;
cursor: pointer;
}
.subscription__payment-selected {
background: var(--primary-color);
color: var(--on-text-color);
}
.subscription__payment__icon {
width: 30px;
height: 30px;
object-fit: contain;
}
.subscription__payment__name {
font-size: 0.65rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
\ No newline at end of file
import React from "react"; import React, { useEffect, useState } from "react";
import "./Subscription.css"; import "./Subscription.css";
import CButton from "../../components/CButton"; import CButton from "../../components/CButton";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import {
const tariffs = [ useAppDispatch,
{ useAppSelector,
id: 1, type RootState,
title: "Yearly", } from "../../stores/store";
amount: 25, import {
}, fetchPaymentMethods,
{ fetchTariffs,
id: 2, } from "../../stores/slices/billingSlice";
title: "3-Months", import type { IPaymentMethod, ITariff } from "../../types/billing.types";
amount: 15, import CLoading from "../../components/CLoading";
},
{
id: 3,
title: "Monthly",
amount: 10,
},
];
const paymentMethods = [
{
id: 1,
paymethod_name: "Карта РФ: Мир, Visa, Mastercard",
},
{
id: 2,
paymethod_name: "СБП (Все банки РФ)",
},
{
id: 3,
paymethod_name: "МИР",
},
];
const Subscription: React.FC = () => { const Subscription: React.FC = () => {
const [selectedTariff, setSelectedTariff] = useState<ITariff | null>(null);
const [selectedPaymentMethod, setSelectedPaymentMethod] =
useState<IPaymentMethod | null>(null);
const { tariffs, paymentMethods, isTariffsLoading, isPaymentMethodsLoading } =
useAppSelector((state: RootState) => state.billing);
const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => {
dispatch(fetchTariffs());
dispatch(fetchPaymentMethods());
}, [dispatch]);
useEffect(() => {
if (tariffs.length > 0) {
setSelectedTariff(tariffs[0]);
}
}, [tariffs]);
useEffect(() => {
if (paymentMethods.length > 0) {
setSelectedPaymentMethod(paymentMethods[0]);
}
}, [paymentMethods]);
return ( return (
<div className="subscription wrapper"> <div className="subscription wrapper">
{isTariffsLoading &&
isPaymentMethodsLoading &&
tariffs.length === 0 &&
paymentMethods.length === 0 && (
<div className="cloading__center">
<CLoading />
</div>
)}
<div className="subscription__tariffs"> <div className="subscription__tariffs">
<h3 className="subscription__tariffs__title"> <h3 className="subscription__tariffs__title">
{t("subscription.selectTariff")} {t("subscription.selectTariff")}
...@@ -48,7 +58,15 @@ const Subscription: React.FC = () => { ...@@ -48,7 +58,15 @@ const Subscription: React.FC = () => {
<div className="subscription__tariffs__list"> <div className="subscription__tariffs__list">
{tariffs.map((tariff) => ( {tariffs.map((tariff) => (
<div key={tariff.id} className="subscription__tariff"> <div
key={tariff.id}
className={`subscription__tariff ${
selectedTariff?.id === tariff?.id
? "subscription__tariff-selected"
: ""
}`}
onClick={() => setSelectedTariff(tariff)}
>
<h4 className="subscription__tariff__title">{tariff.title}</h4> <h4 className="subscription__tariff__title">{tariff.title}</h4>
<p className="subscription__tariff__amount">${tariff.amount}</p> <p className="subscription__tariff__amount">${tariff.amount}</p>
</div> </div>
...@@ -63,10 +81,21 @@ const Subscription: React.FC = () => { ...@@ -63,10 +81,21 @@ const Subscription: React.FC = () => {
<div className="subscription__payments__list"> <div className="subscription__payments__list">
{paymentMethods.map((method) => ( {paymentMethods.map((method) => (
<div key={method.id} className="subscription__payment"> <div
<p className="subscription__payment__name"> key={method.id}
{method.paymethod_name} className={`subscription__payment ${
</p> selectedPaymentMethod?.id === method?.id
? "subscription__payment-selected"
: ""
}`}
onClick={() => setSelectedPaymentMethod(method)}
>
<img
src={`https://cabinet.thecybernanny.com/new/payicons/${method.icon}`}
alt={method.icon}
className="subscription__payment__icon"
/>
<p className="subscription__payment__name">{method.name}</p>
</div> </div>
))} ))}
</div> </div>
......
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { sendRpcRequest } from "../../services/apiClient";
import type { IPaymentMethod, ITariff } from "../../types/billing.types";
interface IBillingState {
tariffs: ITariff[];
isTariffsLoading: boolean;
tariffsError: string | null;
paymentMethods: IPaymentMethod[];
isPaymentMethodsLoading: boolean;
paymentMethodsError: string | null;
}
const initialState: IBillingState = {
tariffs: [],
isTariffsLoading: false,
tariffsError: null,
paymentMethods: [],
isPaymentMethodsLoading: false,
paymentMethodsError: null,
};
export const fetchTariffs = createAsyncThunk(
"billing/fetchTariffs",
async (_, { rejectWithValue }) => {
try {
const response = await sendRpcRequest<{ list: ITariff[] }>(
"billing.tariffs"
);
return response.list;
} catch (error: unknown) {
if (typeof error === "object" && error !== null && "message" in error) {
return rejectWithValue(error.message);
}
return rejectWithValue("Unknown error occurred");
}
}
);
export const fetchPaymentMethods = createAsyncThunk(
"billing/fetchPaymentMethods",
async (_, { rejectWithValue }) => {
try {
const response = await sendRpcRequest<{ list: IPaymentMethod[] }>(
"billing.paymentMethods"
);
return response.list;
} 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,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchTariffs.pending, (state) => {
state.isTariffsLoading = true;
state.tariffsError = null;
})
.addCase(fetchTariffs.fulfilled, (state, action) => {
state.isTariffsLoading = false;
state.tariffs = action.payload;
})
.addCase(fetchTariffs.rejected, (state, action) => {
state.isTariffsLoading = false;
state.tariffsError = action.payload as string;
})
.addCase(fetchPaymentMethods.pending, (state) => {
state.isPaymentMethodsLoading = true;
state.paymentMethodsError = null;
})
.addCase(fetchPaymentMethods.fulfilled, (state, action) => {
state.isPaymentMethodsLoading = false;
state.paymentMethods = action.payload;
})
.addCase(fetchPaymentMethods.rejected, (state, action) => {
state.isPaymentMethodsLoading = false;
state.paymentMethodsError = action.payload as string;
});
},
});
export default billingSlice.reducer;
...@@ -10,6 +10,7 @@ import callHistorySlice from "./slices/callHistorySlice"; ...@@ -10,6 +10,7 @@ import callHistorySlice from "./slices/callHistorySlice";
import messengerHistorySlice from "./slices/messengerHistorySlice"; import messengerHistorySlice from "./slices/messengerHistorySlice";
import usageLimitSlice from "./slices/usageLimitSlice"; import usageLimitSlice from "./slices/usageLimitSlice";
import dataSlice from "./slices/dataSlice"; import dataSlice from "./slices/dataSlice";
import billingSlice from "./slices/billingSlice";
import { import {
useDispatch, useDispatch,
useSelector, useSelector,
...@@ -29,6 +30,7 @@ export const store = configureStore({ ...@@ -29,6 +30,7 @@ export const store = configureStore({
messengerHistory: messengerHistorySlice, messengerHistory: messengerHistorySlice,
usageLimit: usageLimitSlice, usageLimit: usageLimitSlice,
data: dataSlice, data: dataSlice,
billing: billingSlice,
}, },
}); });
......
export interface ITariff {
id: string;
title: string;
amount: string;
}
export interface IPaymentMethod {
id: string;
name: string;
icon: string;
enabled: 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