diff --git a/packages/react/package.json b/packages/react/package.json
new file mode 100644
index 0000000..e213eff
--- /dev/null
+++ b/packages/react/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "@talentlayer/react",
+ "version": "0.0.1",
+ "description": "The TalentLayer Client for React with abstractions for easy interaction with the TalentLayer protocol",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "license": "Apache-2.0",
+ "private": false,
+ "scripts": {
+ "lint": "eslint .",
+ "prepare": "npm run build",
+ "build": "tsc"
+ },
+ "devDependencies": {
+ "@turbo/gen": "^1.10.12",
+ "@types/node": "^20.6.0",
+ "eslint-config-custom": "*",
+ "tsconfig": "*",
+ "typescript": "^5.2.2"
+ },
+ "dependencies": {
+ "@talentlayer/client": "^0.1.7",
+ "net": "^1.0.2",
+ "react": "^18.2.0",
+ "wagmi": "^1.4.2"
+ }
+}
diff --git a/packages/react/src/components/TalentLayerError.tsx b/packages/react/src/components/TalentLayerError.tsx
new file mode 100644
index 0000000..e7dfd1c
--- /dev/null
+++ b/packages/react/src/components/TalentLayerError.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+
+interface TalentLayerErrorProps {
+ error: any;
+}
+
+export default function TalentLayerError(props: TalentLayerErrorProps) {
+ const { error } = props;
+
+ return (
+
+
TalentLayer Error
+ {(() => {
+ try {
+ return JSON.stringify(error, null, 4);
+ } catch {
+ return `Error : ${error}`;
+ }
+ })()}
+
+ );
+}
diff --git a/packages/react/src/contexts/talentLayerContext.tsx b/packages/react/src/contexts/talentLayerContext.tsx
new file mode 100644
index 0000000..49d5bf0
--- /dev/null
+++ b/packages/react/src/contexts/talentLayerContext.tsx
@@ -0,0 +1,116 @@
+'use client';
+
+import React, { ContextType } from 'react';
+import { createContext, ReactNode, useEffect, useMemo, useState } from 'react';
+import { useAccount } from 'wagmi';
+import { IAccount, IUser, NetworkEnum } from '../types';
+import { TalentLayerClient } from '@talentlayer/client';
+
+interface TalentLayerProviderProps {
+ children: ReactNode;
+ config: ConstructorParameters[0] & {
+ account: ReturnType;
+ };
+}
+
+export interface Subgraph {
+ query: (query: string) => any;
+}
+
+const TalentLayerContext = createContext<{
+ user?: IUser;
+ account?: IAccount;
+ refreshData: () => Promise;
+ loading: boolean;
+ client: TalentLayerClient | undefined;
+ platformId: number;
+ chainId?: NetworkEnum;
+ subgraph: Subgraph;
+}>({
+ user: undefined,
+ account: undefined,
+ refreshData: async () => {
+ return false;
+ },
+ loading: true,
+ client: {} as TalentLayerClient,
+ platformId: -1,
+ chainId: undefined,
+ subgraph: { query: _ => new Promise(resolve => resolve(null)) },
+});
+
+export function TalentLayerProvider(props: TalentLayerProviderProps) {
+ const { children, config } = props;
+ const { chainId, platformId } = config;
+
+ const account = config.account;
+
+ const [user, setUser] = useState();
+ const [loading, setLoading] = useState(true);
+
+ const [talentLayerClient, setTalentLayerClient] = useState();
+
+ async function loadData() {
+ if (!talentLayerClient) return false;
+
+ if (!account.address || !account.isConnected) {
+ setLoading(false);
+ return false;
+ }
+
+ try {
+ const userResponse = await talentLayerClient.profile.getByAddress(account.address);
+
+ const currentUser = userResponse;
+
+ const platformResponse = await talentLayerClient.platform.getOne(
+ config.platformId.toString(),
+ );
+
+ const platform = platformResponse;
+ currentUser.isAdmin = platform?.address === currentUser?.address;
+
+ setUser(currentUser);
+
+ return true;
+ } catch (err: any) {
+ console.error(err);
+
+ return false;
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ if (chainId && account.address && !talentLayerClient) {
+ const tlClient = new TalentLayerClient(config);
+ setTalentLayerClient(tlClient);
+ }
+ }, [chainId, account.address]);
+
+ useEffect(() => {
+ if (!talentLayerClient) return;
+
+ loadData();
+ }, [talentLayerClient]);
+
+ const value = useMemo>(() => {
+ return {
+ user,
+ account: account ? account : undefined,
+ refreshData: loadData,
+ loading,
+ client: talentLayerClient,
+ chainId,
+ platformId,
+ subgraph: {
+ query: (query: string) => (talentLayerClient as TalentLayerClient).graphQlClient.get(query),
+ },
+ } as any;
+ }, [account.address, user?.id, loading]);
+
+ return {children};
+}
+
+export default TalentLayerContext;
diff --git a/packages/react/src/hooks/useFees.ts b/packages/react/src/hooks/useFees.ts
new file mode 100644
index 0000000..c24d690
--- /dev/null
+++ b/packages/react/src/hooks/useFees.ts
@@ -0,0 +1,49 @@
+import { useEffect, useState } from 'react';
+import { IFees } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useFees(
+ originServicePlatformId: string,
+ originValidatedProposalPlatformId: string,
+) {
+ const [fees, setFees] = useState({
+ protocolEscrowFeeRate: 0,
+ originServiceFeeRate: 0,
+ originValidatedProposalFeeRate: 0,
+ });
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ setLoading(true);
+ const fees: IFees = {
+ protocolEscrowFeeRate: 0,
+ originServiceFeeRate: 0,
+ originValidatedProposalFeeRate: 0,
+ };
+ try {
+ const response = await talentLayer.client?.escrow.getProtocolAndPlatformsFees(originServicePlatformId, originValidatedProposalPlatformId)
+
+ if (response) {
+ fees.protocolEscrowFeeRate = response.protocols[0].protocolEscrowFeeRate;
+ fees.originServiceFeeRate = response.servicePlatform.originServiceFeeRate;
+ fees.originValidatedProposalFeeRate = response.proposalPlatform.originValidatedProposalFeeRate;
+ }
+
+ setFees(fees);
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, [originServicePlatformId, originValidatedProposalPlatformId]);
+
+ return [fees, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/useMintFee.ts b/packages/react/src/hooks/useMintFee.ts
new file mode 100644
index 0000000..cd42e4f
--- /dev/null
+++ b/packages/react/src/hooks/useMintFee.ts
@@ -0,0 +1,36 @@
+import { useEffect, useState } from 'react';
+import useTalentLayer from './useTalentLayer';
+
+export default function useMintFee() {
+ const [mintFee, setMintFee] = useState(0);
+ const [shortHandlesMaxPrice, setShortHandlesMaxPrice] = useState(0);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ try {
+ const response = await talentLayer.client?.profile.getMintFees();
+
+ if (response) {
+ const protocol = response.protocols[0];
+ setMintFee(protocol.userMintFee);
+ setShortHandlesMaxPrice(protocol.shortHandlesMaxPrice);
+ }
+ } catch (err: any) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, [talentLayer.chainId]);
+
+ const calculateMintFee = (handle: string) => {
+ const length = handle.length;
+ const handlePrice = length > 4 ? mintFee : shortHandlesMaxPrice / Math.pow(2, length - 1);
+ return handlePrice;
+ };
+
+ return { calculateMintFee };
+}
diff --git a/packages/react/src/hooks/usePaymentsByService.ts b/packages/react/src/hooks/usePaymentsByService.ts
new file mode 100644
index 0000000..aa4e43d
--- /dev/null
+++ b/packages/react/src/hooks/usePaymentsByService.ts
@@ -0,0 +1,32 @@
+import { useEffect, useState } from 'react';
+import { IPayment, PaymentTypeEnum } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function usePaymentsByService(id: string, paymentType?: PaymentTypeEnum) {
+ const [payments, setPayments] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(true);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ try {
+ const response = await talentLayer.client?.escrow.getByService(id)
+
+ if (response) {
+ setPayments(response);
+ }
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, [id]);
+
+ return [payments, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/usePaymentsForUser.ts b/packages/react/src/hooks/usePaymentsForUser.ts
new file mode 100644
index 0000000..a9f1f5a
--- /dev/null
+++ b/packages/react/src/hooks/usePaymentsForUser.ts
@@ -0,0 +1,72 @@
+import { useEffect, useState } from 'react';
+import { IPayment } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function usePaymentsForUser(options: {
+ id: string;
+ numberPerPage: number;
+ startDate?: string;
+ endDate?: string;
+}) {
+ const { id, numberPerPage } = options;
+ const startDate = options.startDate;
+ const endDate = options.endDate;
+
+ const [payments, setPayments] = useState([]);
+ const [hasMoreData, setHasMoreData] = useState(true);
+ const [offset, setOffset] = useState(1);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ const total = offset * numberPerPage;
+
+ const start = startDate ? new Date(startDate).getTime() / 1000 : '';
+ const end = endDate ? new Date(endDate).getTime() / 1000 : '';
+
+ async function loadData() {
+ if (!talentLayer.client) return;
+
+ setLoading(true);
+ try {
+ const response = await talentLayer.client?.profile.getPayments(
+ options.id,
+ options.numberPerPage,
+ offset,
+ options.startDate,
+ options.endDate,
+ );
+
+ if (response) {
+ setPayments([...response]);
+
+ if (response.length < total) {
+ setHasMoreData(false);
+ }
+ }
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, [total, id, start, end]);
+
+ useEffect(() => {
+ if (!!start && !!end) {
+ setOffset(1);
+ setHasMoreData(true);
+ }
+ }, [start, end]);
+
+ function loadMore() {
+ setOffset(offset + 1);
+ }
+
+ return [{ items: payments, hasMoreData, loadMore } as const, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/usePlatform.ts b/packages/react/src/hooks/usePlatform.ts
new file mode 100644
index 0000000..0aed553
--- /dev/null
+++ b/packages/react/src/hooks/usePlatform.ts
@@ -0,0 +1,37 @@
+import { useEffect, useState } from 'react';
+import { IPlatform } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function usePlatform(id?: string) {
+ const [platforms, setAddresses] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ setLoading(true);
+
+ if (!talentLayer.client) return;
+
+ try {
+ const response = await talentLayer.client.platform.getOne(
+ id || talentLayer.platformId.toString(),
+ );
+
+ if (response) setAddresses(response);
+ else throw new Error('Unable to find platform');
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, [id]);
+
+ return [platforms, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/useProposal.ts b/packages/react/src/hooks/useProposal.ts
new file mode 100644
index 0000000..c5c371d
--- /dev/null
+++ b/packages/react/src/hooks/useProposal.ts
@@ -0,0 +1,36 @@
+import { useEffect, useState } from 'react';
+import { IProposal } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useProposal(proposalId: string) {
+ const [data, setData] = useState();
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ setLoading(true);
+ if (!talentLayer.client) return;
+
+ try {
+ if (proposalId !== undefined) {
+ const proposal = await talentLayer.client.proposal.getOne(proposalId);
+ return setData(proposal);
+ }
+
+ throw new Error('Proposal not found');
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, [proposalId]);
+
+ return [data, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/useProposals.ts b/packages/react/src/hooks/useProposals.ts
new file mode 100644
index 0000000..291c880
--- /dev/null
+++ b/packages/react/src/hooks/useProposals.ts
@@ -0,0 +1,49 @@
+import { useEffect, useState } from 'react';
+import { IProposal, OnlyOne } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useProposals(
+ filter?: OnlyOne<{
+ serviceId?: string;
+ userId?: string;
+ }>,
+) {
+ const [data, setData] = useState();
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ setLoading(true);
+ if (!talentLayer.client) return;
+
+ try {
+ if (filter === undefined) return setData(undefined);
+
+ if (filter.serviceId !== undefined) {
+ const proposals = await talentLayer.client.proposal.getByServiceId(filter.serviceId);
+ return setData(proposals.data.proposals as IProposal[]);
+ }
+
+ if (filter.userId !== undefined) {
+ const proposals = await talentLayer.client.proposal.getByUser(filter.userId);
+
+ return setData(proposals.data.proposals as IProposal[]);
+ }
+
+ throw new Error('Proposal not found');
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, []);
+
+ return [data, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/useReviews.ts b/packages/react/src/hooks/useReviews.ts
new file mode 100644
index 0000000..edf30d1
--- /dev/null
+++ b/packages/react/src/hooks/useReviews.ts
@@ -0,0 +1,33 @@
+import { useEffect, useState } from 'react';
+import { IReview } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useReviews(serviceId: string) {
+ const [data, setData] = useState();
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ setLoading(true);
+ if (!talentLayer.client) return;
+
+ try {
+ const reviews = await talentLayer.client.review.getByService(serviceId);
+
+ return setData(reviews.data);
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, []);
+
+ return [data, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/useService.ts b/packages/react/src/hooks/useService.ts
new file mode 100644
index 0000000..d7b986a
--- /dev/null
+++ b/packages/react/src/hooks/useService.ts
@@ -0,0 +1,32 @@
+import { useState, useEffect } from 'react';
+import { IService } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useService(serviceId: string) {
+ const [service, setService] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ setLoading(true);
+ if (!talentLayer.client) return;
+
+ try {
+ const response = await talentLayer.client.service.getOne(serviceId);
+ setService(response);
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, [serviceId]);
+
+ return [service, loading, error];
+}
diff --git a/packages/react/src/hooks/useServices.ts b/packages/react/src/hooks/useServices.ts
new file mode 100644
index 0000000..715d5a2
--- /dev/null
+++ b/packages/react/src/hooks/useServices.ts
@@ -0,0 +1,70 @@
+import { useEffect, useState } from 'react';
+import { IService, ServiceStatusEnum } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useServices(filters: {
+ serviceStatus?: ServiceStatusEnum;
+ buyerId?: string;
+ sellerId?: string;
+ searchQuery?: string;
+ numberPerPage?: number;
+ platformId?: string;
+}) {
+ const [services, setServices] = useState([]);
+ const [canLoadMore, setCanLoadMore] = useState(true);
+ const [loading, setLoading] = useState(false);
+ const [offset, setOffset] = useState(0);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ if (!talentLayer.client) return;
+
+ try {
+ setLoading(true);
+ let response;
+ let newServices: IService[] = [];
+
+ response = await talentLayer.client.service.getServices({
+ offset,
+ ...filters,
+ platformId: filters.platformId || talentLayer.platformId.toString(),
+ });
+
+ if (filters.searchQuery) {
+ newServices = response.data;
+ } else {
+ newServices = response.data.services;
+ }
+
+ setServices(offset === 0 ? newServices || [] : [...services, ...newServices]);
+
+ if (filters.numberPerPage && newServices.length < filters.numberPerPage) {
+ setCanLoadMore(false);
+ } else {
+ setCanLoadMore(true);
+ }
+ } catch (err: any) {
+ console.error(err);
+ setError(err);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ setServices([]);
+ setOffset(0);
+ }, [filters.searchQuery]);
+
+ useEffect(() => {
+ loadData();
+ }, [filters.numberPerPage, offset, filters.searchQuery, filters.buyerId, filters.serviceStatus]);
+
+ function loadMore() {
+ filters.numberPerPage ? setOffset(offset + filters.numberPerPage) : '';
+ }
+
+ return [{ canLoadMore, items: services, loadMore } as const, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/useTalentLayer.ts b/packages/react/src/hooks/useTalentLayer.ts
new file mode 100644
index 0000000..abb5f80
--- /dev/null
+++ b/packages/react/src/hooks/useTalentLayer.ts
@@ -0,0 +1,6 @@
+import { useContext } from "react";
+import TalentLayerContext from "../contexts/talentLayerContext";
+
+export default function useTalentLayer() {
+ return useContext(TalentLayerContext);
+}
diff --git a/packages/react/src/hooks/useUser.ts b/packages/react/src/hooks/useUser.ts
new file mode 100644
index 0000000..1331cf3
--- /dev/null
+++ b/packages/react/src/hooks/useUser.ts
@@ -0,0 +1,40 @@
+import { useEffect, useState } from 'react';
+import { IUser, OnlyOne } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useUser(options: OnlyOne<{ userId: string; address: `0x${string}` }>) {
+ const [user, setUser] = useState();
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ if (!talentLayer.client) return;
+
+ try {
+ if (options.userId) {
+ const response = await talentLayer.client.profile.getById(options.userId);
+ setUser(response);
+ }
+
+ if (options.address) {
+ const response = await talentLayer.client.profile.getByAddress(options.address);
+ setUser(response);
+ }
+
+ setLoading(true);
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ loadData();
+ }, []);
+
+ return [user, loading, error] as const;
+}
diff --git a/packages/react/src/hooks/useUsers.ts b/packages/react/src/hooks/useUsers.ts
new file mode 100644
index 0000000..1380b6c
--- /dev/null
+++ b/packages/react/src/hooks/useUsers.ts
@@ -0,0 +1,54 @@
+import { useEffect, useState } from 'react';
+import { IUser } from '../types';
+import useTalentLayer from './useTalentLayer';
+
+export default function useUsers(options: { searchQuery?: string; numberPerPage?: number }) {
+ const [users, setUsers] = useState([]);
+ const [hasMoreData, setHasMoreData] = useState(true);
+ const [offset, setOffset] = useState(0);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const talentLayer = useTalentLayer();
+
+ async function loadData() {
+ if (!talentLayer.client) return;
+
+ try {
+ setLoading(true);
+ const response = await talentLayer.client.profile.getBy({});
+
+ if (offset === 0) {
+ setUsers(response.data.users || []);
+ } else {
+ setUsers([...users, ...response.data.users]);
+ }
+
+ if (options.numberPerPage && response?.data?.users.length < options.numberPerPage) {
+ setHasMoreData(false);
+ } else {
+ setHasMoreData(true);
+ }
+ } catch (error: any) {
+ console.error(error);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ useEffect(() => {
+ setUsers([]);
+ setOffset(0);
+ }, [options.searchQuery]);
+
+ useEffect(() => {
+ loadData();
+ }, [options.numberPerPage, options.searchQuery, offset]);
+
+ function loadMore() {
+ options.numberPerPage ? setOffset(offset + options.numberPerPage) : '';
+ }
+
+ return [{ items: users, hasMoreData, loadMore } as const, loading, error] as const;
+}
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
new file mode 100644
index 0000000..056637d
--- /dev/null
+++ b/packages/react/src/index.ts
@@ -0,0 +1,31 @@
+'use client';
+
+import { TalentLayerProvider } from './contexts/talentLayerContext';
+import useTalentLayer from './hooks/useTalentLayer';
+import useServices from './hooks/useServices';
+import useService from './hooks/useService';
+import usePlatform from './hooks/usePlatform';
+import useProposals from './hooks/useProposals';
+import useProposal from './hooks/useProposal';
+import useFees from './hooks/useFees';
+import useMintFee from './hooks/useMintFee';
+import useUsers from './hooks/useUsers';
+import useUser from './hooks/useUser';
+import usePaymentsByService from './hooks/usePaymentsByService';
+import usePaymentsForUser from './hooks/usePaymentsForUser';
+
+export {
+ TalentLayerProvider,
+ useTalentLayer,
+ useServices,
+ useService,
+ usePlatform,
+ useProposal,
+ useProposals,
+ useFees,
+ useMintFee,
+ useUsers,
+ useUser,
+ usePaymentsByService,
+ usePaymentsForUser,
+};
diff --git a/packages/react/src/types/index.ts b/packages/react/src/types/index.ts
new file mode 100644
index 0000000..3d1a044
--- /dev/null
+++ b/packages/react/src/types/index.ts
@@ -0,0 +1,329 @@
+import { Connector } from 'wagmi';
+
+export type IHive = {
+ id: string;
+ handle: string;
+ address: string;
+ description?: IHiveDetails;
+ members: string[];
+ honeyFee: number;
+ owner: string;
+ ownerIdentity?: IUser;
+ identity: IUser;
+ paymasterAddress: string;
+};
+
+export type IHiveDetails = {
+ manifesto: string;
+ offeredServices: string;
+};
+
+export type IUser = {
+ id: string;
+ handle: string;
+ address: string;
+ rating: string;
+ description?: IUserDetails;
+ userStats: IUserStats;
+ delegates?: string[];
+ isAdmin?: boolean;
+};
+
+export type IUserDetails = {
+ id: string;
+ title: string;
+ name: string;
+ role?: string;
+ image_url?: string;
+ video_url?: string;
+ about?: string;
+ skills_raw?: string;
+ user: IUser;
+ web3mailPreferences?: IWeb3mailPreferences;
+};
+export type IWeb3mailPreferences = {
+ activeOnNewService: boolean;
+ activeOnNewProposal: boolean;
+ activeOnProposalValidated: boolean;
+ activeOnFundRelease: boolean;
+ activeOnReview: boolean;
+ activeOnPlatformMarketing: boolean;
+};
+
+export type IUserStats = {
+ numReceivedReviews: number;
+ numGivenReviews?: number;
+ numCreatedServices?: number;
+ numFinishedServicesAsBuyer?: number;
+ numCreatedProposals?: number;
+ numFinishedServicesAsSeller?: number;
+};
+
+export type IAccount = {
+ address?: `0x${string}`;
+ connector?: Connector;
+ isConnecting: boolean;
+ isReconnecting: boolean;
+ isConnected: boolean;
+ isDisconnected: boolean;
+ status: 'connecting' | 'reconnecting' | 'connected' | 'disconnected';
+};
+
+export type ICompletionScores = {
+ total: ICompletionScore;
+ userDetails: ICompletionScore;
+ web3mail: ICompletionScore;
+};
+
+export type ICompletionScore = {
+ score: number;
+ totalPoint: number;
+ percentage: number;
+};
+
+// TODO: add the rest of the fields
+export type ITransaction = {
+ id: string;
+};
+
+export type IService = {
+ id: string;
+ status: ServiceStatusEnum;
+ buyer: IUser;
+ seller: IUser;
+ sender: IUser;
+ recipient: IUser;
+ cid: string;
+ createdAt: string;
+ updatedAt: string;
+ transaction: ITransaction;
+ platform: IPlatform;
+ proposals: IProposal[];
+ validatedProposal: IProposal[];
+ description?: IServiceDetails;
+};
+
+export type IFeeType = {
+ platform: IPlatform;
+ OriginPlatform: IPlatform;
+};
+
+export type IFeePayment = {
+ id: string; // autogenerated id
+ createdAt: string; // timestamp of block creation
+ platform: IPlatform; // platform entity
+ service: IService; // service entity
+ type: IFeeType; // fee type
+ token: IToken; // token entity
+ amount: string; // platform fee
+};
+
+export type IFeeClaim = {
+ id: string; // autogenerated id
+ createdAt: string; // timestamp of block creation
+ platform: IPlatform; // platform entity
+ token: IToken; // token entity
+ amount: string; // claim amount
+ transactionHash: string; // Transaction hash of the transfer
+};
+
+export type IPlatform = {
+ id: string; // platform id
+ address: `0x${string}`; // wallet address of platform owner
+ name: string; // name of the platform
+ createdAt: string;
+ updatedAt: string;
+ feePayments: IFeePayment[]; // Platform-related fee payments
+ totalPlatformGains: IPlatformGain; // Platform-related total gains per token
+ feeClaims: IFeeClaim[]; // Platform-related fee claims
+ originServiceFeeRate: number; // Fee asked by the platform which created the service
+ originValidatedProposalFeeRate: number; // Fee asked by the platform on which the proposal was validated
+ servicePostingFee: string; // Flat fee asked by the platform to post a service
+ proposalPostingFee: string; // Flat fee asked by the platform to post a proposal
+ arbitrator: `0x${string}`; // address of the arbitrator contract
+ arbitratorExtraData: `0x${string}`; // extra data for the arbitrator
+ arbitrationFeeTimeout: string; // timeout for the arbitration fee to be paid
+ cid: string; //cid of description
+ description: IPlatformDescription;
+ signer: `0x${string}`; // address of the platform signer
+};
+
+export type IPlatformDescription = {
+ id: string; // cid
+ name: string; // Not implement yet on 06.09.23
+ about: string; // text
+ website: string; // url
+ platform: IPlatform;
+ video_url: string;
+ image_url: string;
+};
+
+export type IPlatformGain = {
+ id: string;
+ platform: IPlatform;
+ token: IToken;
+ totalPlatformFeeGain: string;
+ totalOriginPlatformFeeGain: string;
+};
+
+export enum MintStatusEnum {
+ ON_PAUSE,
+ ONLY_WHITELIST,
+ PUBLIC,
+}
+
+export type IKeyword = {
+ id: string;
+};
+
+export type IServiceDetails = {
+ id: string;
+ title?: string;
+ about?: string;
+ keywords: IKeyword[];
+ rateAmount?: string;
+ rateToken?: string;
+ keywords_raw?: string;
+ startDate?: string;
+ expectedEndDate?: string;
+};
+
+export type IServiceDetailsBuyer = {
+ title: string;
+ about: string;
+ rateAmount: string;
+ rateToken: string;
+ buyerHandle: string;
+ buyerId: string;
+ buyerServiceCount: string;
+ buyerRating: string;
+ serviceId: string;
+ createdAt: string;
+ updatedAt: string;
+};
+
+export type IReview = {
+ id: string;
+ service: IService;
+ to: IUser;
+ uri: string;
+ rating: number;
+ createdAt: string;
+ description?: IReviewDetails;
+};
+
+export type IReviewDetails = {
+ id: string;
+ content: string;
+};
+
+export enum ServiceStatusEnum {
+ Opened = 'Opened',
+ Confirmed = 'Confirmed',
+ Finished = 'Finished',
+ Cancelled = 'Cancelled',
+ Uncompleted = 'Uncompleted',
+}
+
+export enum ProposalStatusEnum {
+ Pending = 'Pending',
+ Validated = 'Validated',
+ Rejected = 'Rejected',
+}
+
+export type IProposalDetails = {
+ id: string;
+ title: string;
+ about: string;
+ startDate: string;
+ expectedHours: string;
+ service: IService;
+ expirationDate: string;
+ video_url?: string;
+};
+
+export type IProposal = {
+ id: string;
+ cid: string;
+ status: ProposalStatusEnum;
+ seller: IUser;
+ buyer: IUser;
+ rateToken: IToken;
+ rateAmount: string;
+ service: IService;
+ // transaction: ITransaction;
+ platform: IPlatform;
+ createdAt: string;
+ updatedAt: string;
+ description?: IProposalDetails;
+ expirationDate: string;
+};
+
+export type IFees = {
+ protocolEscrowFeeRate: number;
+ originServiceFeeRate: number;
+ originValidatedProposalFeeRate: number;
+};
+
+export enum ProposalTypeEnum {
+ Hourly = 1,
+ Flat,
+ Milestone,
+}
+
+export enum ProfileTypeEnum {
+ Buyer = 1,
+ Seller,
+}
+
+export enum PaymentTypeEnum {
+ Release = 'Release',
+ Reimburse = 'Reimburse',
+}
+
+export enum NetworkEnum {
+ LOCAL = 1337,
+ MUMBAI = 80001,
+ IEXEC = 134,
+}
+
+export type IToken = {
+ name: string;
+ address: `0x${string}`;
+ symbol: string;
+ decimals: number;
+ minimumTransactionAmount?: string;
+};
+
+export type ITokenFormattedValues = {
+ roundedValue: string;
+ exactValue: string;
+};
+
+export type IPayment = {
+ createdAt: number;
+ id: string;
+ amount: string;
+ rateToken: IToken;
+ paymentType: PaymentTypeEnum;
+ transactionHash: string;
+ service: IService;
+};
+
+export type IUserGain = {
+ id: string;
+ user: IUser;
+ token: IToken;
+ totalGain: string;
+};
+
+export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (
+ k: infer I,
+) => void
+ ? I
+ : never;
+
+export type OnlyOne = Pick> &
+ {
+ [K in Keys]-?: Required> & Partial, undefined>>;
+ }[Keys];
diff --git a/packages/react/src/utils/index.ts b/packages/react/src/utils/index.ts
new file mode 100644
index 0000000..e69de29
diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json
new file mode 100644
index 0000000..649ef00
--- /dev/null
+++ b/packages/react/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "tsconfig/base.json",
+ "include": ["."],
+ "exclude": ["dist", "build", "node_modules"],
+ "compilerOptions": {
+ "jsx": "react",
+ "outDir": "dist",
+ "lib": [ "ES2023", "DOM"]
+ }
+}