From 9e1b8ba984fc4bb5443b3110e0591c36f491e58f Mon Sep 17 00:00:00 2001 From: Rojhat Toptamus Date: Sun, 23 Jun 2024 01:07:50 +0300 Subject: [PATCH] add schema page and boile plate forms --- .../app/dashboard/attestations/page.tsx | 148 ++++++++++++++++++ packages/nextjs/app/dashboard/page.tsx | 133 +--------------- .../app/dashboard/schema/[uid]/page.tsx | 59 +++++++ .../nextjs/app/dashboard/schemas/page.tsx | 131 ++++++++++++++++ packages/nextjs/components/Modal.tsx | 34 ++++ .../forms/CreateAttestationForm.tsx | 117 ++++++++++++++ .../components/forms/RegisterSchemaForm.tsx | 117 ++++++++++++++ .../utils/solas-abis/SchemaRegistry.json | 115 ++++++++++++++ packages/nextjs/utils/utils.ts | 45 +++++- 9 files changed, 769 insertions(+), 130 deletions(-) create mode 100644 packages/nextjs/app/dashboard/attestations/page.tsx create mode 100644 packages/nextjs/app/dashboard/schema/[uid]/page.tsx create mode 100644 packages/nextjs/app/dashboard/schemas/page.tsx create mode 100644 packages/nextjs/components/Modal.tsx create mode 100644 packages/nextjs/components/forms/CreateAttestationForm.tsx create mode 100644 packages/nextjs/components/forms/RegisterSchemaForm.tsx create mode 100644 packages/nextjs/utils/solas-abis/SchemaRegistry.json diff --git a/packages/nextjs/app/dashboard/attestations/page.tsx b/packages/nextjs/app/dashboard/attestations/page.tsx new file mode 100644 index 0000000..8abb210 --- /dev/null +++ b/packages/nextjs/app/dashboard/attestations/page.tsx @@ -0,0 +1,148 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import Link from "next/link"; +import { fetchAllAttestations, fetchStats, shortAddress} from "~~/utils/utils"; +import Modal from "~~/components/Modal"; +import CreateAttestationForm from "~~/components/forms/CreateAttestationForm"; +const Dashboard = () => { + const [attestations, setAttestations] = useState([]); + const [stats, setStats] = useState<{ + totalAtestations: number; + totalSchemas: number; + totalAttestors: number; + }>({ + totalAtestations: 0, + totalSchemas: 0, + totalAttestors: 0, + }); + + const getAttestations = async () => { + try { + const attestationData = await fetchAllAttestations(); + const stats = await fetchStats(); + setAttestations(attestationData); + setStats(stats); + } catch (error) { + console.error("Error fetching attestations:", error); + } + }; + + const AttestationLink = ({ uid }: { uid: string }) => ( + + {shortAddress(uid)} + + ); + + const DashboardStats = ({ + totalAtestations, + totalSchemas, + totalAttestors, + }: { + totalAtestations: number; + totalSchemas: number; + totalAttestors: number; + }) => ( +
+
+
+ {totalAtestations} +
+
Total Attestations
+
+
+
{totalSchemas}
+
Total Schemas
+
+
+
+ {totalAttestors} +
+
Total Attestors
+
+
+ ); + + useEffect(() => { + getAttestations(); + }, []); + return ( +
+
+
+
+
+

+ Attestation Scan +

+

+ Showing the most recent Solas Attestations. +

+
+ ( + + )} + > + + +
+ +
+ + + + + + + + + + + + + {attestations.map((attestation) => ( + + + + + + + + + ))} + +
UIDSchemaFromToTypeAge
+ + + {attestation.schema} + + {attestation.from} + + {attestation.to} + + {attestation.type} + + {attestation.age} +
+
+ +
+
+
+ ); +}; + +export default Dashboard; diff --git a/packages/nextjs/app/dashboard/page.tsx b/packages/nextjs/app/dashboard/page.tsx index 765ac7c..5c17d05 100644 --- a/packages/nextjs/app/dashboard/page.tsx +++ b/packages/nextjs/app/dashboard/page.tsx @@ -1,134 +1,15 @@ "use client"; -import React, { useState, useEffect } from "react"; import Link from "next/link"; -import { fetchAllAttestations, fetchStats, shortAddress} from "~~/utils/utils"; +import React, { useState, useEffect } from "react"; const Dashboard = () => { - const [attestations, setAttestations] = useState([]); - const [stats, setStats] = useState<{ - totalAtestations: number; - totalSchemas: number; - totalAttestors: number; - }>({ - totalAtestations: 0, - totalSchemas: 0, - totalAttestors: 0, - }); - - const getAttestations = async () => { - try { - const attestationData = await fetchAllAttestations(); - const stats = await fetchStats(); - setAttestations(attestationData); - setStats(stats); - } catch (error) { - console.error("Error fetching attestations:", error); - } - }; - - const AttestationLink = ({ uid }: { uid: string }) => ( - - {shortAddress(uid)} - - ); - - const DashboardStats = ({ - totalAtestations, - totalSchemas, - totalAttestors, - }: { - totalAtestations: number; - totalSchemas: number; - totalAttestors: number; - }) => ( -
-
-
- {totalAtestations} -
-
Total Attestations
-
-
-
{totalSchemas}
-
Total Schemas
-
-
-
- {totalAttestors} -
-
Total Attestors
-
-
- ); - - useEffect(() => { - getAttestations(); - }, []); return (
-
-
-
-
-

- Attestation Scan -

-

- Showing the most recent Solas Attestations. -

-
- -
- -
- - - - - - - - - - - - - {attestations.map((attestation) => ( - - - - - - - - - ))} - -
UIDSchemaFromToTypeAge
- - - {attestation.schema} - - {attestation.from} - - {attestation.to} - - {attestation.type} - - {attestation.age} -
-
- -
+
+

Welcome to Dashboard!

+ Attestations +

+

+ Schemas
); diff --git a/packages/nextjs/app/dashboard/schema/[uid]/page.tsx b/packages/nextjs/app/dashboard/schema/[uid]/page.tsx new file mode 100644 index 0000000..5bab581 --- /dev/null +++ b/packages/nextjs/app/dashboard/schema/[uid]/page.tsx @@ -0,0 +1,59 @@ +'use client' +import React, { useState, useEffect } from "react"; +import { useRouter,useSearchParams } from "next/navigation"; +import { fetchSchema,Schema } from "~~/utils/utils"; + + + + export default function AttestationPage({ params }: { params: { uid: string } }) { + const uid = params.uid; + const [schema, setSchema] = useState(null); + + useEffect(() => { + console.log('UUID:', uid); + if (uid) { + fetchSchema(uid) + .then(data => { + console.log('Fetched data:', data); + setSchema(data); + }) + .catch(error => { + console.error('Error fetching schema:', error); + }); + } + }, [uid]); + + if (!schema) { + return
Loading...
; + } + + return ( +
+
+ UID: + {schema.uid} +
+
+ + + + + + + + + + + + + + + + +
UID{schema.uid ? 'True' : 'False'}
Schema{schema.schema}
Attestations{schema.attestations ? 'True' : 'False'}
+
+
+ ); + }; + + \ No newline at end of file diff --git a/packages/nextjs/app/dashboard/schemas/page.tsx b/packages/nextjs/app/dashboard/schemas/page.tsx new file mode 100644 index 0000000..72c0ebb --- /dev/null +++ b/packages/nextjs/app/dashboard/schemas/page.tsx @@ -0,0 +1,131 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import Link from "next/link"; +import { fetchAllSchemas, fetchStats, shortAddress } from "~~/utils/utils"; +import Modal from "~~/components/Modal"; +import RegisterSchemaForm from "~~/components/forms/RegisterSchemaForm"; +const Dashboard = () => { + const [schemas, setSchemas] = useState([]); + const [stats, setStats] = useState<{ + totalAtestations: number; + totalSchemas: number; + totalAttestors: number; + }>({ + totalAtestations: 0, + totalSchemas: 0, + totalAttestors: 0, + }); + + const getAllSchemas = async () => { + try { + const schemaData = await fetchAllSchemas(); + const stats = await fetchStats(); + setSchemas(schemaData); + setStats(stats); + } catch (error) { + console.error("Error fetching schemas:", error); + } + }; + + const SchemaLink = ({ uid }: { uid: string }) => ( + + {shortAddress(uid)} + + ); + + const DashboardStats = ({ + totalAtestations, + totalSchemas, + totalAttestors, + }: { + totalAtestations: number; + totalSchemas: number; + totalAttestors: number; + }) => ( +
+
+
+ {totalAtestations} +
+
Total Schemas
+
+
+
{totalSchemas}
+
Total Schemas
+
+
+ ); + + useEffect(() => { + getAllSchemas(); + }, []); + return ( +
+
+
+
+
+

+ Attestation Scan +

+

+ Showing the most recent Solas Schemas. +

+
+ + ( + + )} + > + + +
+ +
+ + + + + + + + + + {schemas.map((schema) => ( + + + + + + ))} + +
UIDSchemaAttestations
+ + + {schema.schema} + + {schema.attestations} +
+
+ +
+
+
+ ); +}; + +export default Dashboard; diff --git a/packages/nextjs/components/Modal.tsx b/packages/nextjs/components/Modal.tsx new file mode 100644 index 0000000..c62e16a --- /dev/null +++ b/packages/nextjs/components/Modal.tsx @@ -0,0 +1,34 @@ +import React, { ReactNode, ReactElement, useState } from 'react'; + +interface ModalProps { + children: ReactNode; + trigger: ({ openModal }: { openModal: () => void }) => ReactElement; +} + +const Modal: React.FC = ({ children, trigger }) => { + const [isOpen, setIsOpen] = useState(false); + + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + return ( + <> + {trigger({ openModal })} + {isOpen && ( +
+
+ {children} + +
+
+ )} + + ); +}; + +export default Modal; \ No newline at end of file diff --git a/packages/nextjs/components/forms/CreateAttestationForm.tsx b/packages/nextjs/components/forms/CreateAttestationForm.tsx new file mode 100644 index 0000000..1555533 --- /dev/null +++ b/packages/nextjs/components/forms/CreateAttestationForm.tsx @@ -0,0 +1,117 @@ +'use client'; +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; + +const CreateAttestationForm = () => { + const router = useRouter(); + + const isPending = false; + const isConfirming = false; + const isConfirmed = false; + const isSuccess = false; + + const LoadingSpinner = ( + + ); + + useEffect(() => { + if (isConfirmed) { + // redirect to dashboard + setTimeout(() => { + router.push('/dashboard'); + }, 5000); + } + }, [isPending, isConfirming, isConfirmed]); + + + const handleSubmit = (formData: FormData) => { + const name = formData.get('name'); + const email = formData.get('email'); + const profileImage = formData.get('profileImage'); + + }; + + return ( +
+

Make Attestation

+
+ + + {!isPending && !isConfirming && ( + + )} + {(isPending || isConfirming) && ( +
+ {LoadingSpinner} +
+ )} +
+ {isPending && !isSuccess && ( +
+ Please sign the transaction with your wallet. +
+ )} + {isSuccess && isConfirming && ( +
+ Waiting for confirmation... +
+ )} + {isConfirmed && ( +
+ Transaction confirmed. You will be redirected to your dashboard. +

+ View on Etherscan: + + Transaction Link + +

+

+ Go to your{' '} + + dashboard + + . +

+
+ )} + +
+ ); +}; +export default CreateAttestationForm; \ No newline at end of file diff --git a/packages/nextjs/components/forms/RegisterSchemaForm.tsx b/packages/nextjs/components/forms/RegisterSchemaForm.tsx new file mode 100644 index 0000000..04d3c3a --- /dev/null +++ b/packages/nextjs/components/forms/RegisterSchemaForm.tsx @@ -0,0 +1,117 @@ +'use client'; +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; + +const RegisterSchemaForm = () => { + const router = useRouter(); + + const isPending = false; + const isConfirming = false; + const isConfirmed = false; + const isSuccess = false; + + const LoadingSpinner = ( + + ); + + useEffect(() => { + if (isConfirmed) { + // redirect to dashboard + setTimeout(() => { + router.push('/dashboard'); + }, 5000); + } + }, [isPending, isConfirming, isConfirmed]); + + + const handleSubmit = (formData: FormData) => { + const name = formData.get('name'); + const email = formData.get('email'); + const profileImage = formData.get('profileImage'); + + }; + + return ( +
+

Register Schema

+
+ + + {!isPending && !isConfirming && ( + + )} + {(isPending || isConfirming) && ( +
+ {LoadingSpinner} +
+ )} +
+ {isPending && !isSuccess && ( +
+ Please sign the transaction with your wallet. +
+ )} + {isSuccess && isConfirming && ( +
+ Waiting for confirmation... +
+ )} + {isConfirmed && ( +
+ Transaction confirmed. You will be redirected to your dashboard. +

+ View on Etherscan: + + Transaction Link + +

+

+ Go to your{' '} + + dashboard + + . +

+
+ )} + +
+ ); +}; +export default RegisterSchemaForm; \ No newline at end of file diff --git a/packages/nextjs/utils/solas-abis/SchemaRegistry.json b/packages/nextjs/utils/solas-abis/SchemaRegistry.json new file mode 100644 index 0000000..62858ea --- /dev/null +++ b/packages/nextjs/utils/solas-abis/SchemaRegistry.json @@ -0,0 +1,115 @@ +[ + { + "name": "SchemaRegistryImpl", + "type": "impl", + "interface_name": "contracts::SchemaRegistry::ISchemaRegistry" + }, + { + "name": "core::byte_array::ByteArray", + "type": "struct", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "name": "core::bool", + "type": "enum", + "variants": [ + { + "name": "False", + "type": "()" + }, + { + "name": "True", + "type": "()" + } + ] + }, + { + "name": "contracts::SchemaRegistry::ISchemaRegistry", + "type": "interface", + "items": [ + { + "name": "register", + "type": "function", + "inputs": [ + { + "name": "schema", + "type": "core::byte_array::ByteArray" + }, + { + "name": "revocable", + "type": "core::bool" + } + ], + "outputs": [ + { + "type": "core::integer::u128" + } + ], + "state_mutability": "external" + }, + { + "name": "get_schema", + "type": "function", + "inputs": [ + { + "name": "uid", + "type": "core::integer::u128" + } + ], + "outputs": [ + { + "type": "(core::integer::u128, core::bool, core::byte_array::ByteArray)" + } + ], + "state_mutability": "view" + } + ] + }, + { + "kind": "struct", + "name": "contracts::SchemaRegistry::SchemaRegistry::Registered", + "type": "event", + "members": [ + { + "kind": "key", + "name": "uid", + "type": "core::integer::u128" + }, + { + "kind": "data", + "name": "caller", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "schema_record", + "type": "core::byte_array::ByteArray" + } + ] + }, + { + "kind": "enum", + "name": "contracts::SchemaRegistry::SchemaRegistry::Event", + "type": "event", + "variants": [ + { + "kind": "nested", + "name": "Registered", + "type": "contracts::SchemaRegistry::SchemaRegistry::Registered" + } + ] + } + ] \ No newline at end of file diff --git a/packages/nextjs/utils/utils.ts b/packages/nextjs/utils/utils.ts index 2f84c4c..a16825e 100644 --- a/packages/nextjs/utils/utils.ts +++ b/packages/nextjs/utils/utils.ts @@ -20,6 +20,11 @@ export type Attestation = { expiration: string; revoked: string; }; +export type Schema = { + uid: string; + schema: string; + attestations: number; +}; export const fetchStats = async (): Promise<{ totalAtestations: number, totalSchemas: number, totalAttestors: number }> => { const totalAtestations: number = 100; @@ -32,6 +37,39 @@ export const fetchStats = async (): Promise<{ totalAtestations: number, totalSch totalAttestors, }; } +export const fetchAllSchemas = async () => { + // Replace this with your actual fetch call to get attestation data + return [ + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "address author uint stakeAmaount uint royaltyAmount", + attestations: 100 + }, + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "address author uint stakeAmaount uint royaltyAmount", + attestations: 45 + }, + { + uid: "0x041e7455a1009c150268b1bfec337246e4539f07885315b69495dac1abf5ff4c", + schema: "address author uint stakeAmaount uint royaltyAmount", + attestations: 33 + }, + ]; + +} + +// Dummy fetch function to simulate fetching attestation data +export const fetchSchema = async (uuid: string): Promise => { + // Replace this with your actual fetch call to get attestation data + return { + uid: uuid, + schema: 'address author uint stakeAmaount uint royaltyAmount', + attestations: 100 + + }; +}; + export const fetchAllAttestations = async () => { // Replace this with your actual fetch call to get attestation data return [ @@ -119,7 +157,7 @@ export const timeAgo = (timestamp: number): string => { const now = Date.now(); const past = timestamp * 1000; // Convert seconds to milliseconds const diff = now - past; - + const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); @@ -127,7 +165,7 @@ export const timeAgo = (timestamp: number): string => { const weeks = Math.floor(days / 7); const months = Math.floor(days / 30); const years = Math.floor(days / 365); - + if (years > 0) return `${years} year${years > 1 ? 's' : ''} ago`; if (months > 0) return `${months} month${months > 1 ? 's' : ''} ago`; if (weeks > 0) return `${weeks} week${weeks > 1 ? 's' : ''} ago`; @@ -135,5 +173,4 @@ export const timeAgo = (timestamp: number): string => { if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''} ago`; if (minutes > 0) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`; return `${seconds} second${seconds > 1 ? 's' : ''} ago`; - }; - \ No newline at end of file +};