From d7bb480ab6bd2172a04ecb304d012206e0c03e8f Mon Sep 17 00:00:00 2001
From: Max Nanis
Date: Tue, 10 Jun 2025 05:21:19 +0700
Subject: Adding taskStatus fetch. Showing list of Task Attempts & starting
heatmap calendar.
---
src/Widget.tsx | 15 +++-
src/components/app-sidebar.tsx | 16 ++++-
src/components/nav-main.tsx | 17 ++++-
src/components/site-header.tsx | 4 +-
src/models/app.ts | 2 +-
src/models/taskStatusSlice.ts | 24 +++++++
src/models/transactionHistorySlice.ts | 24 +++++++
src/pages/TaskAttemptHistory.tsx | 129 ++++++++++++++++++++++++++++++++++
src/pages/TransactionHistory.tsx | 24 +++++++
src/store.ts | 6 +-
10 files changed, 253 insertions(+), 8 deletions(-)
create mode 100644 src/models/taskStatusSlice.ts
create mode 100644 src/models/transactionHistorySlice.ts
create mode 100644 src/pages/TaskAttemptHistory.tsx
create mode 100644 src/pages/TransactionHistory.tsx
diff --git a/src/Widget.tsx b/src/Widget.tsx
index 16dba81..0fc0dca 100644
--- a/src/Widget.tsx
+++ b/src/Widget.tsx
@@ -5,6 +5,9 @@ import {SidebarInset, SidebarProvider} from "@/components/ui/sidebar"
import {Offerwall} from "@/pages/Offerwall.tsx"
import {QuestionsPage} from "@/pages/Questions.tsx";
import {Demographics} from "@/pages/Demographics.tsx"
+import {CashoutMethodsPage} from "@/pages/CashoutMethods.tsx";
+import {TransactionHistoryPage} from "@/pages/TransactionHistory.tsx"
+import {TaskAttemptHistoryPage} from "@/pages/TaskAttemptHistory.tsx";
import {useAppDispatch, useAppSelector} from "@/hooks.ts";
import {
@@ -14,6 +17,7 @@ import {
ProductUserApi,
ProfilingQuestionsApi,
QuestionInfo,
+ StatusApi,
UserProfileKnowledge,
UserWalletBalance,
WalletApi
@@ -22,12 +26,12 @@ import {ProfileQuestion, setQuestions} from "@/models/questionSlice.ts";
import {setBuckets} from "@/models/bucketSlice.ts";
import {setCashoutMethods} from "@/models/cashoutMethodSlice.ts";
import {setWallet} from "@/models/walletSlice.ts"
-import {CashoutMethodsPage} from "@/pages/CashoutMethods.tsx";
import {setUpkQuestions} from "@/models/upkQuestionSlice.ts"
import {setAvailabilityCount, setOfferwallId} from "@/models/appSlice.ts"
import {setUpkAnswers} from "@/models/userUpkAnswerSlice.ts";
import {setMarketplaceAnswers} from "@/models/userMarketplaceAnswerSlice.ts";
import {setUserProfile} from "@/models/userProfileSlice.ts";
+import {setTaskStatuses} from "@/models/taskStatusSlice.ts"
import './index.css';
@@ -52,6 +56,12 @@ const Widget = () => {
})
.catch(err => console.log(err));
+ new StatusApi().listTaskStatusesProductIdStatusGet(app.bpid, app.bpuid)
+ .then(res => {
+ dispatch(setTaskStatuses(res.data.tasks_status))
+ })
+ .catch(err => console.log(err))
+
new ProductUserApi().userProfileProductIdUserProductUserIdProfileGet(app.bpid, app.bpuid)
.then(res => {
dispatch(setUserProfile(res.data["user-profile"]))
@@ -106,7 +116,10 @@ const Widget = () => {
{app.currentPage === 'offerwall' && }
{app.currentPage === 'questions' && }
{app.currentPage === 'cashout_methods' && }
+ {app.currentPage === 'task_attempts' && }
+
{app.currentPage === 'demographics' && }
+ {app.currentPage === 'transaction_history' && }
diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx
index f314c9f..9a13aff 100644
--- a/src/components/app-sidebar.tsx
+++ b/src/components/app-sidebar.tsx
@@ -21,11 +21,13 @@ import {setPage} from "@/models/appSlice.ts";
import {Badge} from "@/components/ui/badge.tsx";
import {useSelector} from "react-redux";
import {selectCashoutMethods} from "@/models/cashoutMethodSlice.ts";
+import {selectTransactionHistory} from "@/models/transactionHistorySlice.ts";
export function AppSidebar({...props}: React.ComponentProps) {
const app = useAppSelector(state => state.app)
const dispatch = useAppDispatch()
const cashoutMethods = useSelector(selectCashoutMethods)
+ const transactionHistory = useSelector(selectTransactionHistory)
const {isMobile} = useSidebar()
@@ -76,11 +78,19 @@ export function AppSidebar({...props}: React.ComponentProps) {
-
+
-
+ dispatch(setPage("transaction_history"))}
+ >
- History
+ History {transactionHistory.length.toLocaleString()}
diff --git a/src/components/nav-main.tsx b/src/components/nav-main.tsx
index 949ced3..9ed1b24 100644
--- a/src/components/nav-main.tsx
+++ b/src/components/nav-main.tsx
@@ -1,6 +1,6 @@
"use client"
-import {ListIcon, NotebookText, Users, User} from "lucide-react"
+import {ListIcon, NotebookText, Users, User, Activity} from "lucide-react"
import {
SidebarGroup,
SidebarGroupContent,
@@ -21,6 +21,7 @@ export function NavMain() {
const app = useAppSelector(state => state.app)
const questions = useSelector(selectQuestions)
const upkAnswers = useSelector(selectUserUpkAnswers)
+ const taskAttempts = useAppSelector(state => state.taskStatus)
return (
@@ -72,6 +73,20 @@ export function NavMain() {
+ dispatch(setPage("task_attempts"))}
+ >
+
+
+
+ Survey History {taskAttempts.length.toLocaleString()}
+
+
+
+
diff --git a/src/components/site-header.tsx b/src/components/site-header.tsx
index f3e8851..25b3e52 100644
--- a/src/components/site-header.tsx
+++ b/src/components/site-header.tsx
@@ -20,8 +20,10 @@ const SiteHeader = () => {
{app.currentPage === 'offerwall' && "Offerwall"}
{app.currentPage === 'questions' && "Profiling Questions"}
- {app.currentPage === 'cashouts' && "Cashout Methods"}
{app.currentPage === 'demographics' && "User Demographics"}
+ {app.currentPage === 'task_attempts' && "Task Attempts"}
+ {app.currentPage === 'cashouts' && "Cashout Methods"}
+ {app.currentPage === 'transaction_history' && 'Transaction History'}
diff --git a/src/models/app.ts b/src/models/app.ts
index 27923db..8ae71ec 100644
--- a/src/models/app.ts
+++ b/src/models/app.ts
@@ -1,4 +1,4 @@
-export type Page = 'offerwall' | 'questions' | 'demographics' | 'cashout_methods';
+export type Page = 'offerwall' | 'questions' | 'demographics' | 'task_attempts' | 'cashout_methods' | 'transaction_history';
export interface App {
targetId: string,
diff --git a/src/models/taskStatusSlice.ts b/src/models/taskStatusSlice.ts
new file mode 100644
index 0000000..41fc0b4
--- /dev/null
+++ b/src/models/taskStatusSlice.ts
@@ -0,0 +1,24 @@
+import {createSlice, PayloadAction} from '@reduxjs/toolkit'
+import {TaskStatusResponseOut} from "@/api";
+import type {RootState} from "@/store.ts";
+
+
+const initialState: TaskStatusResponseOut[] = []
+
+const transactionHistorySlice = createSlice({
+ name: 'taskStatus',
+ initialState,
+ reducers: {
+ setTaskStatuses(state, action: PayloadAction) {
+ return action.payload;
+ },
+ // taskStatusAdd(state, action: PayloadAction) {
+ // state.push(action.payload);
+ // }
+ }
+})
+
+export const {setTaskStatuses} = transactionHistorySlice.actions;
+export default transactionHistorySlice.reducer
+
+export const selectTransactionHistory = (state: RootState) => state.taskStatus
diff --git a/src/models/transactionHistorySlice.ts b/src/models/transactionHistorySlice.ts
new file mode 100644
index 0000000..71bbf0d
--- /dev/null
+++ b/src/models/transactionHistorySlice.ts
@@ -0,0 +1,24 @@
+import {createSlice, PayloadAction} from '@reduxjs/toolkit'
+import {UserTransactionRow} from "@/api";
+import type {RootState} from "@/store.ts";
+
+
+const initialState: UserTransactionRow[] = []
+
+const transactionHistorySlice = createSlice({
+ name: 'transactionHistory',
+ initialState,
+ reducers: {
+ setBuckets(state, action: PayloadAction) {
+ return action.payload;
+ },
+ transactionAdded(state, action: PayloadAction) {
+ state.push(action.payload);
+ }
+ }
+})
+
+export const {setBuckets, bucketAdded} = transactionHistorySlice.actions;
+export default transactionHistorySlice.reducer
+
+export const selectTransactionHistory = (state: RootState) => state.transactionHistory
diff --git a/src/pages/TaskAttemptHistory.tsx b/src/pages/TaskAttemptHistory.tsx
new file mode 100644
index 0000000..311c32e
--- /dev/null
+++ b/src/pages/TaskAttemptHistory.tsx
@@ -0,0 +1,129 @@
+import {useAppSelector} from "@/hooks.ts";
+import React from "react";
+import {ColumnDef, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table";
+import {TaskStatusResponseOut} from "@/api";
+import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table.tsx";
+import {formatDistanceToNow} from 'date-fns'
+import {formatSecondsVerbose} from "@/lib/utils.ts";
+import {Calendar} from "@/components/ui/calendar"
+
+export const TaskAttemptTable = () => {
+ const data = useAppSelector(state => state.taskStatus)
+
+ const columns: ColumnDef[] = [
+ {
+ accessorKey: "started",
+ header: "Started",
+ cell: ({getValue}) => formatDistanceToNow(new Date(getValue()), {addSuffix: true})
+ },
+ {
+ accessorKey: "finished",
+ header: "Duration",
+ cell: ({row}) => {
+ const started = new Date(row.original.started);
+ const finished = new Date(row.original.finished);
+ const durationInSec = Math.floor((finished.getTime() - started.getTime()) / 1000);
+ return formatSecondsVerbose(durationInSec);
+ }
+ }, {
+ accessorKey: "status",
+ header: "Status",
+ }, {
+ accessorKey: "status_code_1",
+ header: "Status 1",
+ }, {
+ accessorKey: "status_code_2",
+ header: "Status 2",
+ }
+ ]
+
+ const table = useReactTable({
+ data,
+ columns,
+ getCoreRowModel: getCoreRowModel(),
+ })
+
+ return (
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => {
+ return (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ )
+ })}
+
+ ))}
+
+
+
+ {
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ ))
+ }
+
+
+
+ )
+}
+
+const TaskAttemptHistoryPage = () => {
+ const taskStatuses = useAppSelector(state => state.taskStatus)
+
+ const countByDate: Record = taskStatuses.reduce((acc, item) => {
+ const date: string = item.started.split("T")[0]
+ acc[date] = (acc[date] || 0) + 1
+ return acc
+ }, {} as Record)
+
+ return (
+ <>
+
+ // !!countByDate[date.toISOString().split("T")[0]],
+ // },
+ // showOutsideDays: true,
+ // }}
+
+ // dayClassName={(date) => {
+ // const key = date.toISOString().split("T")[0]
+ // console.log(key)
+ // return (countByDate[key] ?? 0) > 0 ? "bg-blue-200 text-blue-800" : "bg-white"
+ // }}
+ />
+
+ >
+ )
+}
+
+export {
+ TaskAttemptHistoryPage
+}
\ No newline at end of file
diff --git a/src/pages/TransactionHistory.tsx b/src/pages/TransactionHistory.tsx
new file mode 100644
index 0000000..a99dd8b
--- /dev/null
+++ b/src/pages/TransactionHistory.tsx
@@ -0,0 +1,24 @@
+import {useAppDispatch, useAppSelector} from "@/hooks.ts";
+import {Pagination, PaginationContent, PaginationItem, PaginationNext} from "@/components/ui/pagination.tsx";
+import React from "react";
+import {ProfileQuestionFull} from "@/pages/Questions.tsx";
+
+const TransactionHistoryPage = () => {
+ const dispatch = useAppDispatch()
+ const transactionHistory = useAppSelector(state => state.transactionHistory)
+
+
+ return (
+ <>
+ {
+ transactionHistory.map(th => {
+
+ })
+ }
+ >
+ )
+}
+
+export {
+ TransactionHistoryPage
+}
\ No newline at end of file
diff --git a/src/store.ts b/src/store.ts
index 7ff1bfc..db283f8 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -10,6 +10,8 @@ import upkQuestionReducers from "@/models/upkQuestionSlice"
import userUpkAnswerReducers from "@/models/userUpkAnswerSlice.ts"
import userMarketplaceReducers from "@/models/userMarketplaceAnswerSlice.ts"
import userProfileReducers from "@/models/userProfileSlice.ts"
+import transactionHistoryReducers from "@/models/transactionHistorySlice.ts"
+import taskStatusReducers from "@/models/taskStatusSlice.ts"
export const store = configureStore({
reducer: {
@@ -18,6 +20,7 @@ export const store = configureStore({
// - Read Only
// -- These act as API cache stores to allow background loading
buckets: bucketReducers,
+ taskStatus: taskStatusReducers,
questions: questionReducers,
upkQuestions: upkQuestionReducers,
@@ -31,7 +34,8 @@ export const store = configureStore({
answers: answerReducers,
cashoutMethods: cashoutMethodReducers,
- wallet: walletReducers
+ wallet: walletReducers,
+ transactionHistory: transactionHistoryReducers
}
})
--
cgit v1.2.3