aboutsummaryrefslogtreecommitdiff
path: root/src/pages
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/CashoutMethods.tsx48
-rw-r--r--src/pages/Community.tsx50
-rw-r--r--src/pages/Offerwall.tsx101
-rw-r--r--src/pages/Questions.tsx105
-rw-r--r--src/pages/Support.tsx38
5 files changed, 342 insertions, 0 deletions
diff --git a/src/pages/CashoutMethods.tsx b/src/pages/CashoutMethods.tsx
new file mode 100644
index 0000000..86727ee
--- /dev/null
+++ b/src/pages/CashoutMethods.tsx
@@ -0,0 +1,48 @@
+import React, {useEffect, useState} from 'react'
+
+import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from "@/components/ui/card.tsx";
+import {GRLWidgetSettings} from "@/Widget.tsx"
+import {CashoutMethodsResponse, WalletApi} from "@/api"
+import {CashoutMethod} from "@/models/CashoutMethod.ts";
+
+const CashoutMethodPreview: React.FC<{ cashout_method: CashoutMethod }> = ({cashout_method}) => {
+
+ return (
+ <Card key={cashout_method.id}>
+ <CardHeader>
+ {cashout_method.name}
+ </CardHeader>
+
+ <CardContent>
+ <img className="blur-xs grayscale" src={cashout_method.imageUrl}/>
+ </CardContent>
+ </Card>
+ )
+}
+
+const CashoutMethodsPage: React.FC<GRLWidgetSettings> = ({settings}) => {
+ const [cashoutMethods, setCashoutMethods] = useState([]);
+
+ useEffect(() => {
+ const x = new WalletApi();
+ x.getCashoutMethodsProductIdCashoutMethodsGet(settings.bpid, settings.bpuid)
+ .then(res => {
+ const data: CashoutMethodsResponse = res.data;
+ setCashoutMethods(data.cashout_methods);
+ })
+ .catch(err => console.log(err));
+ }, []); // ← empty array means "run once"
+
+ return (
+ <div className="grid grid-cols-3 gap-1 p-1">
+ {
+ cashoutMethods.map((m, index) => {
+ const cm = new CashoutMethod(m);
+ return <CashoutMethodPreview key={index} cashout_method={cm} />;
+ })
+ }
+ </div>
+ );
+}
+
+export {CashoutMethodsPage} \ No newline at end of file
diff --git a/src/pages/Community.tsx b/src/pages/Community.tsx
new file mode 100644
index 0000000..88cb74d
--- /dev/null
+++ b/src/pages/Community.tsx
@@ -0,0 +1,50 @@
+import React, {useEffect, useState} from 'react'
+
+import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from "@/components/ui/card.tsx";
+import {GRLWidgetSettings} from "@/Widget.tsx"
+import {CashoutMethodsResponse, WalletApi} from "@/api"
+import {CashoutMethod} from "@/models/CashoutMethod.ts";
+
+const CashoutMethodPreview: React.FC<{ cashout_method: CashoutMethod }> = ({cashout_method}) => {
+
+ console.log("CashoutMethodPreview", cashout_method)
+
+ return (
+ <Card key={cashout_method.id}>
+ <CardHeader>
+ {cashout_method.name}
+ </CardHeader>
+
+ <CardContent>
+ <img className="blur-xs grayscale" src={cashout_method.imageUrl}/>
+ </CardContent>
+ </Card>
+ )
+}
+
+const CommunityPage: React.FC<GRLWidgetSettings> = ({settings}) => {
+ const [cashoutMethods, setCashoutMethods] = useState([]);
+
+ useEffect(() => {
+ const x = new WalletApi();
+ x.getCashoutMethodsProductIdCashoutMethodsGet(settings.bpid, settings.bpuid)
+ .then(res => {
+ const data: CashoutMethodsResponse = res.data;
+ setCashoutMethods(data.cashout_methods);
+ })
+ .catch(err => console.log(err));
+ }, []); // ← empty array means "run once"
+
+ return (
+ <div className="grid grid-cols-3 gap-1 p-1">
+ {
+ cashoutMethods.map((m, index) => {
+ const cm = new CashoutMethod(m);
+ return <CashoutMethodPreview key={index} cashout_method={cm} />;
+ })
+ }
+ </div>
+ );
+}
+
+export {CommunityPage} \ No newline at end of file
diff --git a/src/pages/Offerwall.tsx b/src/pages/Offerwall.tsx
new file mode 100644
index 0000000..acce696
--- /dev/null
+++ b/src/pages/Offerwall.tsx
@@ -0,0 +1,101 @@
+import React from 'react'
+import {Separator} from "@/components/ui/separator"
+import {Link} from '@mui/material';
+
+import {Card, CardContent, CardFooter, CardHeader, CardTitle} from "@/components/ui/card.tsx";
+import {ScrollArea} from "@/components/ui/scroll-area.tsx";
+import {CheckIcon, MessageCircleQuestionIcon, XIcon} from "lucide-react"
+import {SoftPairBucket} from "@/api/models/soft-pair-bucket.ts"
+import {useAppSelector} from '@/hooks'
+
+const BucketStatus: React.FC<SoftPairBucket> = ({bucket}) => {
+ switch (bucket.eligibility) {
+ case "eligible":
+ return <CheckIcon/>;
+ case "conditional":
+ return <MessageCircleQuestionIcon/>;
+ case "ineligible":
+ return <XIcon/>;
+ }
+}
+
+const CallToAction: React.FC<SoftPairBucket> = ({bucket}) => {
+ switch (bucket.eligibility) {
+ case "eligible":
+ return <Link href={bucket.uri}>
+ <button type="button">
+ Start Survey
+ </button>
+ </Link>;
+ case "conditional":
+ return <button type="button">
+ Unlock Survey
+ </button>;
+
+ case "ineligible":
+ return <button type="button">
+ Ineligible Survey
+ </button>;
+ }
+}
+
+const Offerwall = () => {
+ const buckets = useAppSelector(state => state.buckets)
+
+ return (
+
+ <div className="grid grid-cols-2 gap-2 p-2">
+
+ {buckets.map((bucket) => (
+ <Card key={`${bucket.id}`}>
+ <CardHeader>
+ <BucketStatus bucket={bucket}/>
+ <CardTitle>Card 1</CardTitle>
+ </CardHeader>
+
+ <CardContent className="flex items-center gap-2">
+ {/*<StarIcon className="w-5 h-5 fill-[#FFD700]"/>*/}
+ {/*<span className="text-sm">4.5</span>*/}
+ <CallToAction bucket={bucket}/>
+ </CardContent>
+
+ <CardFooter>
+ {/*<PieChart width={100} height=60}>*/}
+ {/* <Pie*/}
+ {/* dataKey="p"*/}
+ {/* startAngle={180}*/}
+ {/* endAngle={0}*/}
+ {/* data={bucket.category}*/}
+ {/* cx="50%"*/}
+ {/* cy="50%"*/}
+ {/* // onMouseEnter={this.onPieEnter}*/}
+ {/* // outerRadius={80}*/}
+ {/* // label*/}
+ {/* />*/}
+ {/*</PieChart>*/}
+
+ <ScrollArea className="w-48 rounded-md border">
+ <div className="p-4">
+ <h4 className="mb-4 text-sm font-medium leading-none">Tags</h4>
+ {bucket.contents.map((survey) => (
+ <>
+ <div key={`${bucket.id}-${survey.id}`} className="text-sm">
+ {survey.id_code} - {survey.loi} seconds - {survey.payout} cents
+ </div>
+ <Separator className="my-2"/>
+ </>
+ ))}
+ </div>
+ </ScrollArea>
+
+ </CardFooter>
+
+ </Card>
+ ))}
+
+ </div>
+
+ )
+}
+
+export {Offerwall} \ No newline at end of file
diff --git a/src/pages/Questions.tsx b/src/pages/Questions.tsx
new file mode 100644
index 0000000..13d31c4
--- /dev/null
+++ b/src/pages/Questions.tsx
@@ -0,0 +1,105 @@
+import React, {useEffect, useState} from 'react'
+
+import {ProfilingQuestionsApi, UpkQuestionResponse} from "@/api"
+import {ProfilingQuestion} from "@/models/question.ts";
+import {setAnswer} from "@/models/questionSlice.ts"
+import {UpkQuestion} from "@/api/models/upk-question.ts"
+import {Card, CardContent, CardHeader} from "@/components/ui/card.tsx";
+import {useAppDispatch, useAppSelector} from "@/hooks.ts";
+
+
+const TextEntry: React.FC<{ question: UpkQuestion }> = ({question}) => {
+ const dispatch = useAppDispatch()
+ // const buckets = useAppSelector(state => state.buckets)
+
+ const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ // Don't allow any input changes after they triggered submission...
+ if (question._complete || question._processing) {
+ return
+ }
+
+ // Assign the input value as an answer to the question
+ const newValue = event.target.value;
+ dispatch(setAnswer({questionId: question.questionId, val: newValue}))
+ };
+
+ return (
+ <Card className="@container/card">
+ <CardHeader>
+ {question.questionText}
+ </CardHeader>
+
+ <CardContent>
+ <input type="text"
+ id="text-entry-input"
+ aria-describedby=""
+ placeholder=""
+ onInput={handleInputChange}
+ />
+ <small id="text-entry-msg">{question.error_msg}</small>
+ </CardContent>
+ </Card>
+ )
+}
+
+const MultipleChoice: React.FC<{ question: UpkQuestion }> = ({question}) => {
+
+ return (
+ <Card>
+ <CardHeader>
+ {question.questionText}
+ </CardHeader>
+
+ <CardContent>
+ <p>MultipleChoice ... </p>
+ </CardContent>
+ </Card>
+ )
+}
+
+
+const ProfileQuestionFull: React.FC<{ question: UpkQuestion }> = ({question}) => {
+ console.log("ProfileQuestionFull", question, question)
+
+ switch (question.questionType) {
+ case "TE":
+ return <TextEntry question={question}/>
+
+ case "MC":
+ return <MultipleChoice question={question}/>
+
+ default:
+ throw new Error("Incorrect Question Type provided");
+ }
+}
+
+const ProfileQuestionPreview: React.FC<{ question: UpkQuestion }> = ({question}) => {
+
+ return (
+ <div>
+ {question.questionText}
+ </div>
+ )
+};
+
+const QuestionsPage = () => {
+ const questions = useAppSelector(state => state.questions)
+
+ return (
+ <div>
+ <p>
+ A total of {questions.length} questions are available.
+ </p>
+
+ {
+ questions.forEach(q => {
+ return <ProfileQuestionFull key={q.questionId} question={q} className="mt-4 mb-4"/>;
+ })
+ }
+ </div>
+ );
+}
+
+export {
+ QuestionsPage
+} \ No newline at end of file
diff --git a/src/pages/Support.tsx b/src/pages/Support.tsx
new file mode 100644
index 0000000..4746b77
--- /dev/null
+++ b/src/pages/Support.tsx
@@ -0,0 +1,38 @@
+import React, { useEffect } from 'react';
+
+const ChatwootLoader = () => {
+ useEffect(() => {
+ const loadChatwoot = () => {
+ const BASE_URL = "https://chat.g-r-l.com";
+ const script = document.createElement('script');
+ script.src = `${BASE_URL}/packs/js/sdk.js`;
+ script.defer = true;
+ script.async = true;
+
+ script.onload = () => {
+ if (window.chatwootSDK) {
+ window.chatwootSDK.run({
+ websiteToken: 'dEEw3fcexQvnQ5tesJPKFjSb',
+ baseUrl: BASE_URL,
+ });
+ }
+ };
+
+ document.body.appendChild(script);
+ };
+
+ loadChatwoot();
+
+ // Clean up script when the component unmounts
+ return () => {
+ const existingScript = document.querySelector(`script[src="https://app.chatwoot.com/packs/js/sdk.js"]`);
+ if (existingScript) {
+ document.body.removeChild(existingScript);
+ }
+ };
+ }, []);
+
+ return null;
+};
+
+export {ChatwootLoader}; \ No newline at end of file