Skip to content

Commit

Permalink
Add: main page and data explorer
Browse files Browse the repository at this point in the history
  • Loading branch information
Prajjawalk committed May 7, 2024
1 parent d736a88 commit fce5188
Show file tree
Hide file tree
Showing 22 changed files with 1,479 additions and 59 deletions.
27 changes: 20 additions & 7 deletions packages/hardhat/contracts/UserAnalytics.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

contract UserAnalytics {
import "@openzeppelin/contracts/access/Ownable.sol";

contract UserAnalytics is Ownable {
uint256[][] public userActivityMatrix;
mapping(address => uint256) public addressToId;
mapping(uint256 => address) public idToAddress;
mapping(address => uint256) public consumerCredits;
mapping(bytes32 => uint256) public schemaIndex;
uint256 public latestIndex;
uint256 public totalCategories;
uint256 public userRewardPerDatapoint;

enum Category {
Gaming,
Expand Down Expand Up @@ -37,12 +40,13 @@ contract UserAnalytics {

Analytics[] public dappAnalytics;

constructor() {
constructor() Ownable() {
uint256[] memory initialMatrix;
userActivityMatrix.push(initialMatrix);
latestIndex = 0;
totalCategories = 7;
dappAnalytics.push();
userRewardPerDatapoint = 10000000000000000;
}

event NewAnalytics(address user, address provider, uint256 category);
Expand All @@ -68,13 +72,18 @@ contract UserAnalytics {
bytes32[] calldata columns,
Category category
) external {
// initializing schema with defaults
// Cannot have two schema with same name
require(schemaIndex[schemaName] == 0, "SCHEMA NAME EXISTS");

// initializing schema with defaults
Analytics storage analytics = dappAnalytics.push();
analytics.schemaName = schemaName;
analytics.schemaCategory = category;
uint256[] memory initialUser;

uint256[] memory initialUser;
analytics.data.push(initialUser);
for (uint256 i = 0; i < columns.length; i++) {

for (uint256 i = 0; i < columns.length; i++) {
analytics.data[0].push(0);
analytics.columns.push(columns[i]);
analytics.columnToIndex[columns[i]] = i;
Expand Down Expand Up @@ -129,7 +138,7 @@ contract UserAnalytics {
] += 1;

// rewarding the users for sharing data
bool sent = userAddress.send(10000000000000000);
bool sent = userAddress.send(userRewardPerDatapoint);
require(sent, "Failed to reward user");

// increasing credit limit for provider
Expand Down Expand Up @@ -185,7 +194,7 @@ contract UserAnalytics {
] += 1;

// rewarding the users for sharing data
bool sent = userAddress.send(10000000000000000);
bool sent = userAddress.send(userRewardPerDatapoint);
require(sent, "Failed to reward user");

// increasing credit limit for provider
Expand All @@ -198,6 +207,10 @@ contract UserAnalytics {
);
}

function updateUserReward(uint256 newReward) external onlyOwner {
userRewardPerDatapoint = newReward;
}

function getUserActivityMatrix()
external
view
Expand Down
93 changes: 93 additions & 0 deletions packages/nextjs/app/_components/schema/CreateSchema.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useState } from "react";
import { InputBase } from "~~/components/scaffold-eth";

export const CreateSchema = () => {
const [schemaName, setSchemaName] = useState("My Schema");
const [totalCol, setTotalCol] = useState("0");
const [col, setCol] = useState<Record<string, any>>();
const [selectedCategory, setSelectedCategory] = useState<number>();

const categories = [...Array(7).keys()];

const categoryDappMap = {
0: "Gaming",
1: "Marketplace",
2: "Defi",
3: "Dao",
4: "Web3Social",
5: "Identity",
6: "Certificates",
};

return (
<>
<div className="flex flex-col gap-3 py-5 first:pt-0 last:pb-1">
<p className="font-medium my-0 break-words">Schema Name</p>
<div className="flex flex-col gap-1.5 w-full">
<div className="flex items-center ml-2">
<span className="block text-xs font-extralight leading-none">string</span>
</div>
<InputBase name="schema name" placeholder="My Schema" value={schemaName} onChange={setSchemaName} />
</div>
</div>
<div className="flex flex-col gap-3 py-5 first:pt-0 last:pb-1">
<p className="font-medium my-0 break-words">Total Columns</p>
<div className="flex flex-col gap-1.5 w-full">
<div className="flex items-center ml-2">
<span className="block text-xs font-extralight leading-none">number</span>
</div>
<InputBase name="total columns" placeholder="0" value={totalCol} onChange={setTotalCol} />
</div>
</div>
{parseInt(totalCol) > 0
? [...Array(parseInt(totalCol)).keys()].map(i => (
<div className="flex flex-col gap-3 py-5 first:pt-0 last:pb-1" key={i}>
<p className="font-medium my-0 break-words">Column {i}</p>
<div className="flex flex-col gap-1.5 w-full">
<div className="flex items-center ml-2">
<span className="block text-xs font-extralight leading-none">string</span>
</div>
<InputBase
name="total columns"
placeholder="0"
value={col?.[i]}
onChange={(value: any) => {
setCol(cols => ({ ...cols, [i]: value }));
}}
/>
</div>
</div>
))
: null}
<div className="flex flex-col gap-3 py-5 first:pt-0 last:pb-1">
<p className="font-medium my-0 break-words">Select Category</p>
<div className="flex flex-col gap-1.5 w-full">
{categories.length === 0 ? (
<p className="text-3xl mt-14">No categories found!</p>
) : (
<>
{categories.length > 1 && (
<div className="flex flex-row gap-2 w-full max-w-7xl pb-1 px-6 lg:px-10 flex-wrap">
{categories.map(category => (
<button
className={`btn btn-secondary btn-sm font-light hover:border-transparent ${
category === selectedCategory
? "bg-base-300 hover:bg-base-300 no-animation"
: "bg-base-100 hover:bg-secondary"
}`}
key={category}
onClick={() => setSelectedCategory(category)}
>
{categoryDappMap[category as keyof typeof categoryDappMap]}
</button>
))}
</div>
)}
</>
)}
</div>
<button className="btn btn-secondary btn-sm">Save</button>
</div>
</>
);
};
121 changes: 121 additions & 0 deletions packages/nextjs/app/_components/schema/SchemaDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { InputBase } from "~~/components/scaffold-eth";

export const SchemaDetails = () => {
const router = useRouter();
const [expandCol, setExpandCol] = useState<string>();
const [toggleCol, setToggleCol] = useState(false);
const [col, setCol] = useState<Record<string, number>>();

const schemas = [
{
schemaName: "schemaName1",
columns: ["col1", "col2", "col3", "col4", "col5"],
category: "gaming",
createdBy: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
totalRecords: 100,
},
{
schemaName: "schemaName2",
columns: ["col1", "col2", "col3", "col4", "col5"],
category: "marketplace",
createdBy: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
totalRecords: 100,
},
{
schemaName: "schemaName3",
columns: ["col1", "col2", "col3", "col4", "col5"],
category: "social",
createdBy: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
totalRecords: 100,
},
{
schemaName: "schemaName4",
columns: ["col1", "col2", "col3", "col4", "col5"],
category: "gaming",
createdBy: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
totalRecords: 100,
},
{
schemaName: "schemaName5",
columns: ["col1", "col2", "col3", "col4", "col5"],
category: "identity",
createdBy: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
totalRecords: 100,
},
];

return (
<>
{schemas.map(i => (
<div className="flex flex-col gap-3 py-5 first:pt-0 last:pb-1" key={i.schemaName}>
<p className="font-medium my-0 break-words">{i.schemaName}</p>
<div className="flex-col gap-y-1">
<div className="flex">
<div className="font-small">columns: </div>
<div className="font-extralight px-2">{i.columns.join(", ")}</div>
</div>
<div className="flex">
<div className="font-small">category: </div>
<div className="font-extralight px-2">{i.category}</div>
</div>
<div className="flex">
<div className="font-small">created by: </div>
<div className="font-extralight px-2">
{i.createdBy.slice(0, 7)}...{i.createdBy.slice(-4, -1)}
</div>
</div>
<div className="flex">
<div className="font-small">total records: </div>
<div className="font-extralight px-2">{i.totalRecords}</div>
</div>
</div>
<div className="flex justify-between gap-2 flex-wrap">
<button
className="btn btn-secondary btn-sm"
onClick={() => {
setExpandCol(i.schemaName);
setToggleCol(!toggleCol);
}}
>
Add data
</button>
<button
className="btn btn-secondary btn-sm"
onClick={async () => {
router.push("/dataexplorer");
}}
// disabled={isFetching}
>
Explore data 📡
</button>
</div>
{expandCol == i.schemaName && toggleCol == true
? i.columns.map(i => (
<div className="flex flex-col gap-3 py-5 first:pt-0 last:pb-1" key={i}>
<p className="font-medium my-0 break-words">{i}</p>
<div className="flex flex-col gap-1.5 w-full">
<div className="flex items-center ml-2">
<span className="block text-xs font-extralight leading-none">number</span>
</div>
<InputBase
name="total columns"
placeholder="0"
value={col?.[i]}
onChange={(value: any) => {
setCol(cols => ({ ...cols, [i]: value }));
}}
/>
</div>
</div>
))
: null}
{expandCol == i.schemaName && toggleCol == true ? (
<button className="btn btn-secondary btn-sm">Save</button>
) : null}
</div>
))}
</>
);
};
27 changes: 27 additions & 0 deletions packages/nextjs/app/dataexplorer/_components/AddressCodeTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type AddressCodeTabProps = {
bytecode: string;
assembly: string;
};

export const AddressCodeTab = ({ bytecode, assembly }: AddressCodeTabProps) => {
const formattedAssembly = Array.from(assembly.matchAll(/\w+( 0x[a-fA-F0-9]+)?/g))
.map(it => it[0])
.join("\n");

return (
<div className="flex flex-col gap-3 p-4">
Bytecode
<div className="mockup-code -indent-5 overflow-y-auto max-h-[500px]">
<pre className="px-5">
<code className="whitespace-pre-wrap overflow-auto break-words">{bytecode}</code>
</pre>
</div>
Opcodes
<div className="mockup-code -indent-5 overflow-y-auto max-h-[500px]">
<pre className="px-5">
<code>{formattedAssembly}</code>
</pre>
</div>
</div>
);
};
34 changes: 34 additions & 0 deletions packages/nextjs/app/dataexplorer/_components/AddressComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { BackButton } from "./BackButton";
import { Address, Balance } from "~~/components/scaffold-eth";

export const AddressComponent = ({
address,
}: // contractData,
{
address: string;
contractData: { bytecode: string; assembly: string } | null;
}) => {
return (
<div className="m-10 mb-20">
<div className="flex justify-start mb-5">
<BackButton />
</div>
<div className="col-span-5 grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-10">
<div className="col-span-1 flex flex-col">
<div className="bg-base-100 border-base-300 border shadow-md shadow-secondary rounded-3xl px-6 lg:px-8 mb-6 space-y-1 py-4 overflow-x-auto">
<div className="flex">
<div className="flex flex-col gap-1">
<Address address={address} format="long" />
<div className="flex gap-1 items-center">
<span className="font-bold text-sm">Balance:</span>
<Balance address={address} className="text" />
</div>
</div>
</div>
</div>
</div>
</div>
{/* <ContractTabs address={address} contractData={contractData} /> */}
</div>
);
};
21 changes: 21 additions & 0 deletions packages/nextjs/app/dataexplorer/_components/AddressLogsTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Address } from "viem";
import { useContractLogs } from "~~/hooks/scaffold-eth";
import { replacer } from "~~/utils/scaffold-eth/common";

export const AddressLogsTab = ({ address }: { address: Address }) => {
const contractLogs = useContractLogs(address);

return (
<div className="flex flex-col gap-3 p-4">
<div className="mockup-code overflow-auto max-h-[500px]">
<pre className="px-5 whitespace-pre-wrap break-words">
{contractLogs.map((log, i) => (
<div key={i}>
<strong>Log:</strong> {JSON.stringify(log, replacer, 2)}
</div>
))}
</pre>
</div>
</div>
);
};
Loading

0 comments on commit fce5188

Please sign in to comment.