Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Statements | Branches | Functions | Lines |
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
| ![Statements](https://img.shields.io/badge/statements-6.38%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-1.42%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-4.45%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-6.07%25-red.svg?style=flat) |
| ![Statements](https://img.shields.io/badge/statements-6.34%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-1.39%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-4.43%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-6.03%25-red.svg?style=flat) |

Front-end repository for Certification Service integration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import './PaymentDetailsVerification.css'
interface Props {
content?: any;
data: {
fee: BigNum,
subscription: ISubscription,
subscription: any,
balance: number,
profile: any
} | undefined | null;
onAccept: () => void;
Expand Down Expand Up @@ -46,8 +46,8 @@ const PaymentDetailsVerification = ( props: Props ) => {
{props?.data?.subscription && (<>
<ListItem><ListItemText primary="Subscription Type" secondary={formatToTitleCase(props?.data?.subscription.type)} /></ListItem>
<ListItem><ListItemText primary="Tier" secondary={"Tier " + props?.data?.subscription.tierId}/></ListItem>
<ListItem><ListItemText primary="Amount in USD" secondary={`$${Math.round(props?.data?.subscription.price/1000000*props?.data?.subscription.adaUsdPrice*100)/100}/year`} /></ListItem>
<ListItem><ListItemText primary="Amount in ADA" secondary={`₳ ${Math.round(props?.data?.subscription.price*100/1000000)/100}`} /></ListItem>
<ListItem><ListItemText primary="Amount in USD" secondary={`$${props?.data?.subscription.usdPrice}/year`} /></ListItem>
<ListItem><ListItemText primary="Expected Amount in ADA" secondary={`₳ ${props?.data?.subscription.adaPrice} ${props?.data?.balance ? `- Will be deducted from the available balance ₳${props?.data?.balance} in your profile` : ''}`} /></ListItem>
</>)}
</List>
</>
Expand Down
5 changes: 3 additions & 2 deletions src/pages/landing/components/RegisterSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const REFRESH_TIME = 30;

interface Props {
tier: Tier,
onSubmit: (form: RegisterForm) => void
onClickPay: (form: RegisterForm) => void,
submitForm: boolean
}

const RegisterSection = (props: Props) => {
Expand Down Expand Up @@ -65,7 +66,7 @@ const RegisterSection = (props: Props) => {
);

const onSubmit = (form: RegisterForm) => {
props.onSubmit({
props.onClickPay({
...form,
linkedin: form.linkedin!.length > 0 ? form.linkedin : undefined,
twitter: form.twitter!.length > 0 ? form.twitter : undefined,
Expand Down
41 changes: 29 additions & 12 deletions src/pages/landing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import RegisterModal from "./components/RegisterModal";

import { useAppDispatch, useAppSelector } from "store/store";
import { fetchActiveSubscription } from "store/slices/auth.slice";
import { register, clear, payForRegister } from "store/slices/register.slice";
import { register, clear, payForRegister, calculateFee } from "store/slices/register.slice";
import type { Tier } from "store/slices/tiers.slice";
import type { RegisterForm } from "store/slices/register.slice";

Expand All @@ -35,12 +35,16 @@ export default function LandingPage() {
const dispatch = useAppDispatch();
const { wallet } = useAppSelector((state) => state.walletConnection);
const { transactionId, processing, success } = useAppSelector((state) => state.register);
const { price } = useAppSelector((state) => state.price);
const { profileBalance } = useAppSelector((state) => state.profile);

const [step, setStep] = useState<string>('connect');
const [selectedTier, setSelectedTier] = useState<Tier|null>(null);
const [showVerificationModal, setShowVerificationModal] = useState<boolean>(false);
const [registerResponse, setRegisterResponse] = useState<any>(null);
const [detailsToBeVerified, setDetailsToBeVerified] = useState<any>(null)
const [detailsToBeVerified, setDetailsToBeVerified] = useState<any>(null);
const [submitForm, setSubmitForm] = useState<boolean>(false);
const [form, setForm] = useState<RegisterForm>({ address: "", companyName: "", contactEmail: "", email: "", fullName: ""});

useEffect(() => {
if (wallet !== null && step === 'connect') {
Expand All @@ -54,22 +58,35 @@ export default function LandingPage() {


const handleRegistration = async (form: RegisterForm) => {
const response: any = await dispatch(register({ form, tierId: selectedTier!.id }));
if (typeof response.payload !== 'string') {
setRegisterResponse(response.payload);
setDetailsToBeVerified({
...response.payload,
if (selectedTier?.id) {
setForm(form)
const adaPrice: number = selectedTier.usdPrice / price;
const lovelacesPrice: number = Math.round(adaPrice * 1000000);
const { lessBalance } = await calculateFee(lovelacesPrice, profileBalance);
await setDetailsToBeVerified({
subscription: {
type: selectedTier.type,
tierId: selectedTier.id,
adaPrice: adaPrice.toFixed(2),
usdPrice: selectedTier.usdPrice
},
balance: !lessBalance ? Math.round(profileBalance*100/1000000) / 100 : null,
profile: form
})
setShowVerificationModal(true);
} else {
// do nothin;
await setShowVerificationModal(true);
}
};

const onPaymentDetailsVerified = async () => {
setShowVerificationModal(false)
await dispatch(payForRegister(registerResponse));
const response: any = await dispatch(register({ form, tierId: selectedTier!.id }));
if (response?.payload?.subscription) {
setRegisterResponse(response.payload);
await dispatch(payForRegister(response.payload));
setSubmitForm(true)
} else {
// do nothing;
}
}

const handleContinue = () => {
Expand All @@ -83,7 +100,7 @@ export default function LandingPage() {
<Box className="flex flex-row h-screen bg-cover bg-center bg-landing">
{ step === 'connect' && <ConnectSection /> }
{ step === 'subscription' && <SubscriptionSection onSelectTier={setSelectedTier}/> }
{ selectedTier !== null && <RegisterSection tier={selectedTier} onSubmit={handleRegistration} /> }
{ selectedTier !== null && <RegisterSection tier={selectedTier} onClickPay={handleRegistration} submitForm={submitForm} /> }
<RegisterModal show={processing||success} onClose={handleContinue} success={success} transactionId={transactionId} />

{showVerificationModal ?
Expand Down
2 changes: 2 additions & 0 deletions src/store/slices/auth.slice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "store/rootReducer";
import { fetch } from "../../api";
import { fetchProfileBalance } from "./profile.slice";

interface AuthState {
isSessionFetched: boolean;
Expand All @@ -18,6 +19,7 @@ export const fetchActiveSubscription = createAsyncThunk('fetchActiveSubscription
try {
const {impersonate, retainId} = (thunkApi.getState() as RootState).profile;
const response: any = await fetch<string[]>(thunkApi, { method: 'GET', url: `/profile/${impersonate ? retainId : "current"}/subscriptions/active-features` });
thunkApi.dispatch(fetchProfileBalance({}))
if (response.status !== 200) throw new Error();
return response.data;
} catch (error) {
Expand Down
23 changes: 22 additions & 1 deletion src/store/slices/profile.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ interface ProfileState {
runHistory: Run[];
impersonate: boolean;
retainId: number | null;
profileBalance: number;
}

export interface IUpdateProfile {
Expand Down Expand Up @@ -123,7 +124,8 @@ const initialState: ProfileState = {
loadingHistory: false,
runHistory: [],
impersonate: false,
retainId: null
retainId: null,
profileBalance: 0
};

// Add this export to your profileSlice file
Expand Down Expand Up @@ -235,6 +237,16 @@ export const fetchProfileRunHistory = createAsyncThunk("fetchProfileRunHistory",
}
})

export const fetchProfileBalance = createAsyncThunk("fetchProfileBalance", async( payload: any, thunkApi) => {
try {
const res: any = await fetch<number>(thunkApi, { method: 'GET', url: '/profile/current/balance' });
if (res.status !== 200) throw { message: res.data };
return res.data;
} catch (e: any) {
return thunkApi.rejectWithValue(e.response.data);
}
})

export const profileSlice = createSlice({
name: "profile",
initialState,
Expand Down Expand Up @@ -351,6 +363,15 @@ export const profileSlice = createSlice({
state.loadingHistory = false;
state.runHistory = [];
})
.addCase(fetchProfileBalance.pending, (state) => {
state.profileBalance = 0;
})
.addCase(fetchProfileBalance.fulfilled, (state, actions) => {
state.profileBalance = actions.payload;
})
.addCase(fetchProfileBalance.rejected, (state, actions) => {
state.profileBalance = 0;
})
},
});

Expand Down
39 changes: 17 additions & 22 deletions src/store/slices/register.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,10 @@ const getUserSubscriptionById = async (thunkApi: any, subscriptionId: string) =>
return res.data.find(item => item.id === subscriptionId);
}

const getUserBalance = async (thunkApi: any) => {
const res = await fetch<number>(thunkApi, { method: 'GET', url: '/profile/current/balance' });
if (res.status !== 200) throw { message: res.data };
return BigNum.from_str(res.data.toString());
}

const calculateFee = async (subscriptionPrice: BigNum, balance: BigNum) => {
export const calculateFee = async (subscriptionPriceNum: number, balanceNum: number) => {
let lessBalance = false;
const subscriptionPrice: BigNum = BigNum.from_str(subscriptionPriceNum.toString())
const balance: BigNum = BigNum.from_str(balanceNum.toString())
if (balance.less_than(subscriptionPrice)) {
lessBalance = true;
} else {
Expand Down Expand Up @@ -130,19 +126,14 @@ export const register = createAsyncThunk("register", async (request: RegisterReq
if (!subscription) throw { message: 'There\'s no subscription registered' };
if (subscription.status !== 'pending') throw { message: 'The subscription it\'s not pending' };

const subscriptionPrice = BigNum.from_str(subscription.price.toString());
const balance = await getUserBalance(thunkApi);
const { profileBalance } = (thunkApi.getState() as RootState).profile;

const { lessBalance, fee } = await calculateFee(subscriptionPrice, balance);
if (!lessBalance) {
// do nothing; auto pay from balance
return '';
} else {
return {
fee: fee,
subscription: subscription
};
}
const { lessBalance, fee } = await calculateFee(subscription.price, profileBalance);
return {
fee: fee,
subscription: subscription,
balance: !lessBalance ? profileBalance : null
};
}
else {
const currentSub = await getUserSubscriptionById(thunkApi, pendingSubscription.id);
Expand All @@ -157,11 +148,15 @@ export const register = createAsyncThunk("register", async (request: RegisterReq
}
});

export const payForRegister = createAsyncThunk("payForRegister", async (payload: {fee: BigNum, subscription: any}, thunkApi) => {
export const payForRegister = createAsyncThunk("payForRegister", async (payload: {fee: BigNum, subscription: any, balance: number}, thunkApi) => {
const { wallet, walletAddress, stakeAddress } = (thunkApi.getState() as RootState).walletConnection;
let transactionId: string | null;
let transactionId: string | null = '';
try {
transactionId = await doPayment(thunkApi.dispatch, wallet, walletAddress!, stakeAddress!, payload.fee);
if (payload.balance) {
// prevent triggering wallet payments as account has balance
} else {
transactionId = await doPayment(thunkApi.dispatch, wallet, walletAddress!, stakeAddress!, payload.fee);
}

await hasActiveSubscription(thunkApi, payload.subscription.id).catch(error => {
return thunkApi.rejectWithValue(error.message.toString());
Expand Down
3 changes: 3 additions & 0 deletions src/store/slices/tiers.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export interface Tier {
features: TierFeature[];
usdPrice: number;
enabled: boolean;
type: string;
duration: number;
description: string;
}

interface TiersState {
Expand Down