Skip to content

Commit 2064a4e

Browse files
committed
subscriptions w/o payment
1 parent f69d4f2 commit 2064a4e

File tree

4 files changed

+221
-7
lines changed

4 files changed

+221
-7
lines changed

Govt-Billing-React-Ad-Subscriptions/src/components/Login/Login.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const Login: React.FC = () => {
2323
};
2424
const doSignUp = async (email: string, password: string) => {
2525
try {
26-
await signUpWithEmailAndPassword(email, password);
26+
await signUpWithEmailAndPassword(email, password, "bronze");
2727
closeLoginModal();
2828
} catch {
2929
console.error("Something Went Wrong");

Govt-Billing-React-Ad-Subscriptions/src/components/Menu/Menu.tsx

+123-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from "react";
1+
import React, { useEffect, useState } from "react";
22
import * as AppGeneral from "../socialcalc/index.js";
33
import { File, Local } from "../Storage/LocalStorage";
44
import { isPlatform, IonToast } from "@ionic/react";
@@ -8,6 +8,13 @@ import { IonActionSheet, IonAlert } from "@ionic/react";
88
import { saveOutline, save, mail, print } from "ionicons/icons";
99
import { APP_NAME } from "../../app-data.js";
1010

11+
import {
12+
canUserPerformAction,
13+
updateUserQuota,
14+
updateUserSubscription,
15+
} from "../../firebase/firestore.js";
16+
import useUser from "../../hooks/useUser.js";
17+
1118
const Menu: React.FC<{
1219
showM: boolean;
1320
setM: Function;
@@ -22,6 +29,28 @@ const Menu: React.FC<{
2229
const [showAlert4, setShowAlert4] = useState(false);
2330
const [showToast1, setShowToast1] = useState(false);
2431
const [toastMessage, setToastMessage] = useState("");
32+
const [canPrint, setCanPrint] = useState(false);
33+
const [canEmail, setCanEmail] = useState(false);
34+
const { user, isLoading } = useUser();
35+
const [showUpgradeAlert, setShowUpgradeAlert] = useState(false);
36+
37+
const handleUpgradeSubscription = async (tier: string) => {
38+
if (user) {
39+
try {
40+
await updateUserSubscription(user.uid, tier);
41+
setToastMessage(`Successfully upgraded to ${tier} tier!`);
42+
setShowToast1(true);
43+
checkUserPermissions();
44+
} catch (error) {
45+
console.error("Error upgrading subscription:", error);
46+
setToastMessage("Failed to upgrade subscription. Please try again.");
47+
setShowToast1(true);
48+
}
49+
} else {
50+
setToastMessage("Please log in to upgrade your subscription.");
51+
setShowToast1(true);
52+
}
53+
};
2554
/* Utility functions */
2655
const _validateName = async (filename) => {
2756
filename = filename.trim();
@@ -55,19 +84,52 @@ const Menu: React.FC<{
5584
}
5685
return filename;
5786
};
87+
useEffect(() => {
88+
checkUserPermissions();
89+
}, [isLoading]);
90+
91+
const checkUserPermissions = async () => {
92+
if (user) {
93+
const printPermission = await canUserPerformAction(user.uid, "print");
94+
const emailPermission = await canUserPerformAction(user.uid, "email");
95+
setCanPrint(printPermission);
96+
setCanEmail(emailPermission);
97+
}
98+
};
99+
100+
const doPrint = async () => {
101+
if (!canPrint) {
102+
setToastMessage("You've reached your print quota limit.");
103+
setShowToast1(true);
104+
return;
105+
}
106+
107+
if (user) {
108+
const updated = await updateUserQuota(user.uid, "print");
109+
if (!updated) {
110+
setToastMessage(
111+
"Failed to update quota. You may have reached your limit."
112+
);
113+
setShowToast1(true);
114+
return;
115+
}
116+
}
58117

59-
const doPrint = () => {
60118
if (isPlatform("hybrid")) {
61119
const printer = Printer;
62120
printer.print(AppGeneral.getCurrentHTMLContent());
63121
} else {
64122
const content = AppGeneral.getCurrentHTMLContent();
65-
// useReactToPrint({ content: () => content });
66123
const printWindow = window.open("/printwindow", "Print Invoice");
67124
printWindow.document.write(content);
68125
printWindow.print();
69126
}
127+
128+
setToastMessage("Print job sent successfully.");
129+
setShowToast1(true);
130+
checkUserPermissions(); // Update permissions after printing
70131
};
132+
71133
const doSave = () => {
72134
if (props.file === "default") {
73135
setShowAlert1(true);
@@ -113,7 +175,24 @@ const Menu: React.FC<{
113175
}
114176
};
115177

116-
const sendEmail = () => {
178+
const sendEmail = async () => {
179+
if (!canEmail) {
180+
setToastMessage("You've reached your email quota limit.");
181+
setShowToast1(true);
182+
return;
183+
}
184+
185+
if (user) {
186+
const updated = await updateUserQuota(user.uid, "email");
187+
if (!updated) {
188+
setToastMessage(
189+
"Failed to update quota. You may have reached your limit."
190+
);
191+
setShowToast1(true);
192+
return;
193+
}
194+
}
195+
117196
if (isPlatform("hybrid")) {
118197
const content = AppGeneral.getCurrentHTMLContent();
119198
const base64 = btoa(content);
@@ -127,9 +206,15 @@ const Menu: React.FC<{
127206
subject: `${APP_NAME} attached`,
128207
isHtml: true,
129208
});
209+
210+
setToastMessage("Email sent successfully.");
211+
setShowToast1(true);
130212
} else {
131-
alert("This Functionality works on Anroid/IOS devices");
213+
setToastMessage("This functionality works on Android/iOS devices only.");
214+
setShowToast1(true);
132215
}
216+
217+
checkUserPermissions();
133218
};
134219

135220
return (
@@ -172,6 +257,14 @@ const Menu: React.FC<{
172257
console.log("Email clicked");
173258
},
174259
},
260+
{
261+
text: "Upgrade Subscription",
262+
icon: "arrow-up-circle-outline",
263+
handler: () => {
264+
setShowUpgradeAlert(true);
265+
console.log("Upgrade Subscription clicked");
266+
},
267+
},
175268
]}
176269
/>
177270
<IonAlert
@@ -225,6 +318,31 @@ const Menu: React.FC<{
225318
}
226319
buttons={["Ok"]}
227320
/>
321+
<IonAlert
322+
animated
323+
isOpen={showUpgradeAlert}
324+
onDidDismiss={() => setShowUpgradeAlert(false)}
325+
header="Upgrade Subscription"
326+
message="Choose a subscription tier to upgrade:"
327+
buttons={[
328+
{
329+
text: "Bronze ($150/month)",
330+
handler: () => handleUpgradeSubscription("bronze"),
331+
},
332+
{
333+
text: "Silver ($200/month)",
334+
handler: () => handleUpgradeSubscription("silver"),
335+
},
336+
{
337+
text: "Gold ($250/month)",
338+
handler: () => handleUpgradeSubscription("gold"),
339+
},
340+
{
341+
text: "Cancel",
342+
role: "cancel",
343+
},
344+
]}
345+
/>
228346
<IonToast
229347
animated
230348
isOpen={showToast1}

Govt-Billing-React-Ad-Subscriptions/src/firebase/auth.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@ import {
44
signOut,
55
} from "firebase/auth";
66
import { auth } from "./index";
7+
import { initializeUserSubscription, subscriptionTiers } from "./firestore";
78

8-
const signUpWithEmailAndPassword = async (email: string, password: string) => {
9+
const signUpWithEmailAndPassword = async (
10+
email: string,
11+
password: string,
12+
tier: keyof typeof subscriptionTiers
13+
) => {
914
try {
1015
const userCredential = await createUserWithEmailAndPassword(
1116
auth,
1217
email,
1318
password
1419
);
1520
const user = userCredential.user;
21+
22+
await initializeUserSubscription(user.uid, tier);
23+
1624
return user;
1725
} catch (error) {
1826
const errorCode = error.code;

Govt-Billing-React-Ad-Subscriptions/src/firebase/firestore.ts

+88
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
where,
1010
getDoc,
1111
deleteDoc,
12+
updateDoc,
1213
} from "firebase/firestore";
1314
import { Local } from "../components/Storage/LocalStorage";
1415

@@ -129,10 +130,97 @@ const deleteFileFromFirebase = async (userId, key, onSuccess) => {
129130
alert("Something went wrong");
130131
}
131132
};
133+
const subscriptionTiers = {
134+
bronze: { prints: 10, emails: 10, price: 150 },
135+
silver: { prints: 20, emails: 20, price: 200 },
136+
gold: { prints: 30, emails: 30, price: 250 },
137+
};
138+
139+
interface UserSubscription {
140+
tier: keyof typeof subscriptionTiers;
141+
remainingPrints: number;
142+
remainingEmails: number;
143+
expirationDate: Date;
144+
}
145+
146+
const getUserSubscription = async (
147+
userId: string
148+
): Promise<UserSubscription | null> => {
149+
const docRef = doc(db, "subscriptions", userId);
150+
const docSnap = await getDoc(docRef);
151+
152+
if (docSnap.exists()) {
153+
return docSnap.data() as UserSubscription;
154+
} else {
155+
console.log("No subscription found for this user");
156+
return null;
157+
}
158+
};
159+
160+
const initializeUserSubscription = async (
161+
userId: string,
162+
tier: keyof typeof subscriptionTiers
163+
) => {
164+
const subscriptionData: UserSubscription = {
165+
tier,
166+
remainingPrints: subscriptionTiers[tier].prints,
167+
remainingEmails: subscriptionTiers[tier].emails,
168+
expirationDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now
169+
};
170+
171+
await setDoc(doc(db, "subscriptions", userId), subscriptionData);
172+
};
173+
174+
const updateUserQuota = async (
175+
userId: string,
176+
action: "print" | "email"
177+
): Promise<boolean> => {
178+
const subscription = await getUserSubscription(userId);
179+
if (!subscription) return false;
180+
181+
const quotaField = action === "print" ? "remainingPrints" : "remainingEmails";
182+
if (subscription[quotaField] > 0) {
183+
await updateDoc(doc(db, "subscriptions", userId), {
184+
[quotaField]: subscription[quotaField] - 1,
185+
});
186+
return true;
187+
}
188+
return false;
189+
};
190+
191+
const canUserPerformAction = async (
192+
userId: string,
193+
action: "print" | "email"
194+
): Promise<boolean> => {
195+
const subscription = await getUserSubscription(userId);
196+
if (!subscription) return false;
197+
198+
const quotaField = action === "print" ? "remainingPrints" : "remainingEmails";
199+
return subscription[quotaField] > 0;
200+
};
201+
202+
const updateUserSubscription = async (userId: string, tier: string) => {
203+
const subscriptionData = {
204+
tier,
205+
remainingPrints: subscriptionTiers[tier].prints,
206+
remainingEmails: subscriptionTiers[tier].emails,
207+
expirationDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now
208+
};
209+
210+
await setDoc(doc(db, "subscriptions", userId), subscriptionData, {
211+
merge: true,
212+
});
213+
};
132214

133215
export {
134216
uploadFileToCloud,
135217
getFilesKeysFromFirestore,
136218
downloadFileFromFirebase,
137219
deleteFileFromFirebase,
220+
getUserSubscription,
221+
updateUserQuota,
222+
canUserPerformAction,
223+
initializeUserSubscription,
224+
subscriptionTiers,
225+
updateUserSubscription,
138226
};

0 commit comments

Comments
 (0)