diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx
index 0981bc9a..8e020f61 100644
--- a/packages/nextjs/components/Header.tsx
+++ b/packages/nextjs/components/Header.tsx
@@ -1,11 +1,10 @@
"use client";
import React, { useCallback, useRef, useState } from "react";
-import Image from "next/image";
import Link from "next/link";
import { usePathname } from "next/navigation";
-import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline";
-import { FaucetButton, RainbowKitCustomConnectButton } from "~~/components/scaffold-eth";
+import { Bars3Icon } from "@heroicons/react/24/outline";
+import { RainbowKitCustomConnectButton } from "~~/components/scaffold-eth";
import { useOutsideClick } from "~~/hooks/scaffold-eth";
type HeaderMenuLink = {
@@ -14,16 +13,11 @@ type HeaderMenuLink = {
icon?: React.ReactNode;
};
+// TODO: Hardcoded. Fix later
export const menuLinks: HeaderMenuLink[] = [
{
- label: "Home",
- href: "/",
- },
-
- {
- label: "Debug Contracts",
- href: "/debug",
- icon:
,
+ label: "Portfolio",
+ href: "/portfolio",
},
];
@@ -41,7 +35,7 @@ export const HeaderMenuLinks = () => {
passHref
className={`${
isActive ? "bg-secondary shadow-md" : ""
- } hover:bg-secondary hover:shadow-md focus:!bg-secondary active:!text-neutral py-1.5 px-3 text-sm rounded-full gap-2 grid grid-flow-col`}
+ } hover:bg-secondary hover:shadow-md focus:!bg-secondary active:!text-neutral py-1.5 lg:py-2 px-3 lg:px-4 text-sm rounded-full gap-2 grid grid-flow-col`}
>
{icon}
{label}
@@ -65,7 +59,7 @@ export const Header = () => {
);
return (
-
+
{
{isDrawerOpen && (
{
setIsDrawerOpen(false);
}}
@@ -89,22 +83,12 @@ export const Header = () => {
)}
-
-
-
-
-
- Scaffold-ETH
- Ethereum dev stack
-
-
-
);
diff --git a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
index a9e31251..06937df2 100644
--- a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
+++ b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
@@ -6,7 +6,6 @@ import { Address } from "viem";
import { useDisconnect } from "wagmi";
import {
ArrowLeftOnRectangleIcon,
- ArrowTopRightOnSquareIcon,
ArrowsRightLeftIcon,
CheckCircleIcon,
ChevronDownIcon,
@@ -30,6 +29,7 @@ export const AddressInfoDropdown = ({
address,
ensAvatar,
displayName,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
blockExplorerAddressLink,
}: AddressInfoDropdownProps) => {
const { disconnect } = useDisconnect();
@@ -48,9 +48,12 @@ export const AddressInfoDropdown = ({
return (
<>
-
+
-
+
{isENS(displayName) ? displayName : checkSumAddress?.slice(0, 6) + "..." + checkSumAddress?.slice(-4)}
@@ -95,19 +98,6 @@ export const AddressInfoDropdown = ({
View QR Code
-
-
-
-
- View on Block Explorer
-
-
-
{allowedNetworks.length > 1 ? (
{
- const networkColor = useNetworkColor();
const { targetNetwork } = useTargetNetwork();
const { address: connectedAddress } = useAccount();
const { handleRegister, isRegistering } = useUserRegister();
@@ -34,7 +31,11 @@ export const RainbowKitCustomConnectButton = () => {
if (!connected) {
return (
-
+
Connect Wallet
);
@@ -50,7 +51,11 @@ export const RainbowKitCustomConnectButton = () => {
if (!user) {
return (
-
+
{isRegistering ? : "Register"}
);
@@ -58,12 +63,6 @@ export const RainbowKitCustomConnectButton = () => {
return (
<>
-
-
-
- {chain.name}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/bgBanner_castlePlatform.svg b/packages/nextjs/public/assets/bgBanner_castlePlatform.svg
new file mode 100644
index 00000000..569cae5b
--- /dev/null
+++ b/packages/nextjs/public/assets/bgBanner_castlePlatform.svg
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/bgBanner_joinBgClouds.svg b/packages/nextjs/public/assets/bgBanner_joinBgClouds.svg
new file mode 100644
index 00000000..d2fd573e
--- /dev/null
+++ b/packages/nextjs/public/assets/bgBanner_joinBgClouds.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/dex.svg b/packages/nextjs/public/assets/challenges/dex.svg
new file mode 100644
index 00000000..b3f64b41
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/dex.svg
@@ -0,0 +1,212 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/diceGame.svg b/packages/nextjs/public/assets/challenges/diceGame.svg
new file mode 100644
index 00000000..d2c746d0
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/diceGame.svg
@@ -0,0 +1,338 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/dynamicSvgNFT.svg b/packages/nextjs/public/assets/challenges/dynamicSvgNFT.svg
new file mode 100644
index 00000000..b99b0f32
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/dynamicSvgNFT.svg
@@ -0,0 +1,217 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/multiSig.svg b/packages/nextjs/public/assets/challenges/multiSig.svg
new file mode 100644
index 00000000..c7f931f1
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/multiSig.svg
@@ -0,0 +1,378 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/simpleNFT.svg b/packages/nextjs/public/assets/challenges/simpleNFT.svg
new file mode 100644
index 00000000..2d2a773d
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/simpleNFT.svg
@@ -0,0 +1,353 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/stakingToken.svg b/packages/nextjs/public/assets/challenges/stakingToken.svg
new file mode 100644
index 00000000..fa38d775
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/stakingToken.svg
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/state.svg b/packages/nextjs/public/assets/challenges/state.svg
new file mode 100644
index 00000000..8a212db9
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/state.svg
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/challenges/tokenVendor.svg b/packages/nextjs/public/assets/challenges/tokenVendor.svg
new file mode 100644
index 00000000..183f8526
--- /dev/null
+++ b/packages/nextjs/public/assets/challenges/tokenVendor.svg
@@ -0,0 +1,593 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/header_platform.svg b/packages/nextjs/public/assets/header_platform.svg
new file mode 100644
index 00000000..15778dbc
--- /dev/null
+++ b/packages/nextjs/public/assets/header_platform.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/home_header_clouds.svg b/packages/nextjs/public/assets/home_header_clouds.svg
new file mode 100644
index 00000000..ac339df5
--- /dev/null
+++ b/packages/nextjs/public/assets/home_header_clouds.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/key_icon.svg b/packages/nextjs/public/assets/key_icon.svg
new file mode 100644
index 00000000..50f84ceb
--- /dev/null
+++ b/packages/nextjs/public/assets/key_icon.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/assets/vault_icon.svg b/packages/nextjs/public/assets/vault_icon.svg
new file mode 100644
index 00000000..4ad7cdaf
--- /dev/null
+++ b/packages/nextjs/public/assets/vault_icon.svg
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/nextjs/public/favicon.ico b/packages/nextjs/public/favicon.ico
new file mode 100644
index 00000000..e803b933
Binary files /dev/null and b/packages/nextjs/public/favicon.ico differ
diff --git a/packages/nextjs/public/favicon.png b/packages/nextjs/public/favicon.png
deleted file mode 100644
index 4bef7f2f..00000000
Binary files a/packages/nextjs/public/favicon.png and /dev/null differ
diff --git a/packages/nextjs/public/thumbnail.jpg b/packages/nextjs/public/thumbnail.jpg
deleted file mode 100644
index a3bf231f..00000000
Binary files a/packages/nextjs/public/thumbnail.jpg and /dev/null differ
diff --git a/packages/nextjs/public/thumbnail.png b/packages/nextjs/public/thumbnail.png
new file mode 100644
index 00000000..397c1dd0
Binary files /dev/null and b/packages/nextjs/public/thumbnail.png differ
diff --git a/packages/nextjs/services/database/config/schema.ts b/packages/nextjs/services/database/config/schema.ts
index b652a630..d2a24c3a 100644
--- a/packages/nextjs/services/database/config/schema.ts
+++ b/packages/nextjs/services/database/config/schema.ts
@@ -1,7 +1,9 @@
+import { EventType, ReviewAction, UserRole } from "./types";
import { SQL, relations, sql } from "drizzle-orm";
import {
AnyPgColumn,
boolean,
+ integer,
jsonb,
pgEnum,
pgTable,
@@ -17,15 +19,23 @@ export function lower(address: AnyPgColumn): SQL {
return sql`lower(${address})`;
}
-export const reviewActionEnum = pgEnum("review_action_enum", ["REJECTED", "ACCEPTED", "SUBMITTED"]);
-export const eventTypeEnum = pgEnum("event_type_enum", ["CHALLENGE_SUBMIT", "CHALLENGE_AUTOGRADE", "USER_CREATE"]);
-export const userRoleEnum = pgEnum("user_role_enum", ["USER", "ADMIN"]);
+export const reviewActionEnum = pgEnum("review_action_enum", [
+ ReviewAction.REJECTED,
+ ReviewAction.ACCEPTED,
+ ReviewAction.SUBMITTED,
+]);
+export const eventTypeEnum = pgEnum("event_type_enum", [
+ EventType.CHALLENGE_SUBMIT,
+ EventType.CHALLENGE_AUTOGRADE,
+ EventType.USER_CREATE,
+]);
+export const userRoleEnum = pgEnum("user_role_enum", [UserRole.USER, UserRole.ADMIN]);
export const users = pgTable(
"users",
{
userAddress: varchar({ length: 42 }).primaryKey(), // Ethereum wallet address
- role: userRoleEnum().default("USER"), // Using the enum and setting default
+ role: userRoleEnum().default(UserRole.USER), // Using the enum and setting default
createdAt: timestamp().defaultNow(),
socialTelegram: varchar({ length: 255 }),
socialTwitter: varchar({ length: 255 }),
@@ -40,8 +50,11 @@ export const users = pgTable(
export const challenges = pgTable("challenges", {
id: varchar({ length: 255 }).primaryKey(), // Unique identifier for the challenge
challengeName: varchar({ length: 255 }).notNull(),
+ description: text().notNull(),
+ sortOrder: integer().notNull(),
github: varchar({ length: 255 }), // Repository reference for the challenge
autograding: boolean().default(false), // Whether the challenge supports automatic grading
+ disabled: boolean().default(false),
});
export const userChallenges = pgTable(
diff --git a/packages/nextjs/services/database/config/types.ts b/packages/nextjs/services/database/config/types.ts
new file mode 100644
index 00000000..6caa9b4a
--- /dev/null
+++ b/packages/nextjs/services/database/config/types.ts
@@ -0,0 +1,17 @@
+// Types to import from both schema/db and client
+export enum ReviewAction {
+ REJECTED = "REJECTED",
+ ACCEPTED = "ACCEPTED",
+ SUBMITTED = "SUBMITTED",
+}
+
+export enum EventType {
+ CHALLENGE_SUBMIT = "CHALLENGE_SUBMIT",
+ CHALLENGE_AUTOGRADE = "CHALLENGE_AUTOGRADE",
+ USER_CREATE = "USER_CREATE",
+}
+
+export enum UserRole {
+ USER = "USER",
+ ADMIN = "ADMIN",
+}
diff --git a/packages/nextjs/services/database/repositories/challenges.ts b/packages/nextjs/services/database/repositories/challenges.ts
index 57d00c14..61b68244 100644
--- a/packages/nextjs/services/database/repositories/challenges.ts
+++ b/packages/nextjs/services/database/repositories/challenges.ts
@@ -1,7 +1,10 @@
-import { eq } from "drizzle-orm";
+import { InferInsertModel, eq } from "drizzle-orm";
import { db } from "~~/services/database/config/postgresClient";
import { challenges } from "~~/services/database/config/schema";
+export type ChallengeInsert = InferInsertModel;
+export type Challenges = Awaited>;
+
export async function getChallengeById(id: string) {
const result = await db.select().from(challenges).where(eq(challenges.id, id));
return result[0];
diff --git a/packages/nextjs/services/database/seed.data.ts b/packages/nextjs/services/database/seed.data.ts
index de9cf9ac..86d43f34 100644
--- a/packages/nextjs/services/database/seed.data.ts
+++ b/packages/nextjs/services/database/seed.data.ts
@@ -1,25 +1,26 @@
import { challenges, events, userChallenges, users } from "./config/schema";
+import { EventType, ReviewAction, UserRole } from "./config/types";
// Using Drizzle's inferred insert types to ensure seed data
// matches database schema requirements
export const seedUsers: (typeof users.$inferInsert)[] = [
{
userAddress: "0xB4F53bd85c00EF22946d24Ae26BC38Ac64F5E7B1",
- role: "USER",
+ role: UserRole.USER,
createdAt: new Date(1679063274534),
socialTwitter: "pabl0cks",
socialTelegram: "pabl0cks",
},
{
userAddress: "0x000084821704d731438d2D06f4295e1AB0ace7D8",
- role: "USER",
+ role: UserRole.USER,
createdAt: new Date(1664777161512),
socialEmail: "ryuufarhan7@gmail.com",
socialTwitter: "FarhanRyuu",
},
{
userAddress: "0x014EC6296B3493f0f59a3FE90E0FFf377fb8826a",
- role: "USER",
+ role: UserRole.USER,
createdAt: new Date(1672731143934),
socialEmail: "gokulkesavan5005@gmail.com",
socialTwitter: "meta_Goku",
@@ -27,12 +28,19 @@ export const seedUsers: (typeof users.$inferInsert)[] = [
},
{
userAddress: "0x01B2686Bd146bFc3F4B3DD6F7F86f26ac7c2f7Fd",
- role: "USER",
+ role: UserRole.USER,
createdAt: new Date(1668050419502),
socialEmail: "afo@wefa.app",
socialTwitter: "Time_Is_Oba",
socialGithub: "Oba-One",
},
+ {
+ userAddress: "0x45334F41aAA464528CD5bc0F582acadC49Eb0Cd1",
+ role: UserRole.USER,
+ createdAt: new Date(1679063274534),
+ socialTwitter: "rinat_eth",
+ socialTelegram: "rinat_eth",
+ },
];
export const seedChallenges: (typeof challenges.$inferInsert)[] = [
@@ -41,48 +49,72 @@ export const seedChallenges: (typeof challenges.$inferInsert)[] = [
challengeName: "Simple NFT Example",
github: "scaffold-eth/se-2-challenges:challenge-0-simple-nft",
autograding: true,
+ description:
+ "๐ซ Create a simple NFT to learn basics of ๐ scaffold-eth. You'll use ๐ทโโ๏ธ HardHat to compile and deploy smart contracts. Then, you'll use a template React app full of important Ethereum components and hooks. Finally, you'll deploy an NFT to a public network to share with friends! ๐",
+ sortOrder: 0,
},
{
id: "decentralized-staking",
challengeName: "Decentralized Staking App",
github: "scaffold-eth/se-2-challenges:challenge-1-decentralized-staking",
autograding: true,
+ description:
+ "๐ฆธ A superpower of Ethereum is allowing you, the builder, to create a simple set of rules that an adversarial group of players can use to work together. In this challenge, you create a decentralized application where users can coordinate a group funding effort. The users only have to trust the code.",
+ sortOrder: 1,
},
{
id: "token-vendor",
challengeName: "Token Vendor",
github: "scaffold-eth/se-2-challenges:challenge-2-token-vendor",
autograding: true,
+ description:
+ '๐ค Smart contracts are kind of like "always on" vending machines that anyone can access. Let\'s make a decentralized, digital currency (an ERC20 token). Then, let\'s build an unstoppable vending machine that will buy and sell the currency. We\'ll learn about the "approve" pattern for ERC20s and how contract to contract interactions work.',
+ sortOrder: 2,
},
{
id: "dice-game",
challengeName: "Dice Game",
github: "scaffold-eth/se-2-challenges:challenge-3-dice-game",
autograding: true,
+ description:
+ "๐ฐ Randomness is tricky on a public deterministic blockchain. The block hash is the result proof-of-work (for now) and some builders use this as a weak form of randomness. In this challenge you will take advantage of a Dice Game contract by predicting the randomness in order to only roll winning dice!",
+ sortOrder: 3,
},
{
id: "minimum-viable-exchange",
challengeName: "Build a DEX",
github: "scaffold-eth/se-2-challenges:challenge-4-dex",
autograding: true,
+ description:
+ "๐ต Build an exchange that swaps ETH to tokens and tokens to ETH. ๐ฐ This is possible because the smart contract holds reserves of both assets and has a price function based on the ratio of the reserves. Liquidity providers are issued a token that represents their share of the reserves and fees...",
+ sortOrder: 4,
},
{
id: "state-channels",
challengeName: "A State Channel Application",
github: "scaffold-eth/se-2-challenges:challenge-5-state-channels",
autograding: true,
+ description:
+ "๐ฃ๏ธ The Ethereum blockchain has great decentralization & security properties but these properties come at a price: transaction throughput is low, and transactions can be expensive. This makes many traditional web applications infeasible on a blockchain... or does it? State channels look to solve these problems by allowing participants to securely transact off-chain while keeping interaction with Ethereum Mainnet at a minimum.",
+ sortOrder: 5,
},
{
id: "multisig",
challengeName: "Multisig Wallet",
github: "scaffold-eth/se-2-challenges:challenge-6-multisig",
autograding: false,
+ description:
+ '๐ฉโ๐ฉโ๐งโ๐ง Using a smart contract as a wallet we can secure assets by requiring multiple accounts to "vote" on transactions. The contract will keep track of transactions in an array of structs and owners will confirm or reject each one. Any transaction with enough confirmations can "execute".',
+ sortOrder: 6,
},
{
id: "svg-nft",
challengeName: "SVG NFT",
github: "scaffold-eth/se-2-challenges:challenge-7-svg-nft",
autograding: false,
+ description:
+ "๐จ Create a dynamic SVG NFT using a smart contract. Your contract will generate on-chain SVG images and allow users to mint their unique NFTs. โจ Customize your SVG graphics and metadata directly within the smart contract. ๐ Share the minting URL once your project is live!",
+ sortOrder: 7,
},
];
@@ -94,7 +126,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://sepolia.etherscan.io/address/0x7f918d7b7d0fe0d3a8de3c0570ed4e154c0096e0",
reviewComment: "Dummy review, nice work",
submittedAt: new Date(1679063312936),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0xB4F53bd85c00EF22946d24Ae26BC38Ac64F5E7B1",
@@ -103,7 +135,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://sepolia.etherscan.io/address/0xd08b984c3ee4a880112d81de5a4a074c857b7f2f",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1679359244796),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0xB4F53bd85c00EF22946d24Ae26BC38Ac64F5E7B1",
@@ -112,7 +144,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://sepolia.etherscan.io/address/0xbF35fC995A2Cc4F1508B5F769922623dE7f220d6",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1679185806311),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0xB4F53bd85c00EF22946d24Ae26BC38Ac64F5E7B1",
@@ -121,7 +153,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://sepolia.etherscan.io/address/0x57D312c3E4bF22F22d6A900Be8B38b5546b47C3f",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1679317471852),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x000084821704d731438d2D06f4295e1AB0ace7D8",
@@ -130,7 +162,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0x861346d67b728949bcb69595638a78723a2adae3",
reviewComment: "Dummy review, nice work",
submittedAt: new Date(1664778633262),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x014EC6296B3493f0f59a3FE90E0FFf377fb8826a",
@@ -139,7 +171,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0xe3DF14f1482074916A8Aeb40d84898C879b2B5f6",
reviewComment: "Dummy review, nice work",
submittedAt: new Date(1672831270556),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x014EC6296B3493f0f59a3FE90E0FFf377fb8826a",
@@ -148,7 +180,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0x71F80197032c9a07b966D3f8eCAFE601Af244F35",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1673150820763),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x014EC6296B3493f0f59a3FE90E0FFf377fb8826a",
@@ -157,7 +189,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0xEd2aF24B1a657000C9b4509BC91FCfA5E76D3d33",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1672974794575),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x014EC6296B3493f0f59a3FE90E0FFf377fb8826a",
@@ -166,7 +198,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0xf3384118b56827271979A879e2d5A4d28569eb48",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1672830084301),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x014EC6296B3493f0f59a3FE90E0FFf377fb8826a",
@@ -175,7 +207,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0x0752d3847601b506cBFB10330172821B495e6bB0",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1673006384070),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x014EC6296B3493f0f59a3FE90E0FFf377fb8826a",
@@ -184,7 +216,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0x9626C68Dd8d000BBB7B06f0782266976dEB91c35",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1672819901984),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x01B2686Bd146bFc3F4B3DD6F7F86f26ac7c2f7Fd",
@@ -193,7 +225,7 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0xb9487f8d9E336a9468fcbb50dAF39587D0EBCA63",
reviewComment: "Dummy review, nice work",
submittedAt: new Date(1668196973527),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
},
{
userAddress: "0x01B2686Bd146bFc3F4B3DD6F7F86f26ac7c2f7Fd",
@@ -202,13 +234,69 @@ export const seedUserChallenges: (typeof userChallenges.$inferInsert)[] = [
contractUrl: "https://goerli.etherscan.io/address/0xd68edd04EbB81c6f187A526F3656C18dD0258cd8",
reviewComment: "Dummy review, it's working great",
submittedAt: new Date(1668830120864),
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
+ },
+ {
+ userAddress: "0x45334F41aAA464528CD5bc0F582acadC49Eb0Cd1",
+ challengeId: "token-vendor",
+ frontendUrl: "https://sepolia-optimism.etherscan.io/address/0xad5e878a62D5B77277aCDC321614a9727815B4C8#code",
+ contractUrl: "https://sepolia-optimism.etherscan.io/address/0xad5e878a62D5B77277aCDC321614a9727815B4C8#code",
+ submittedAt: new Date(1736441361988),
+ reviewComment:
+ "You have successfully passed challenge 2!
You have passed the first three challenges on SpeedRunEthereum and can now join the BuidlGuidl!
",
+ reviewAction: ReviewAction.REJECTED,
+ },
+ {
+ userAddress: "0x45334F41aAA464528CD5bc0F582acadC49Eb0Cd1",
+ challengeId: "simple-nft-example",
+ frontendUrl: "https://sepolia-optimism.etherscan.io/address/0x82b4935ebe7a5d802cf465a3495da1aff96f1153#code",
+ contractUrl: "https://sepolia-optimism.etherscan.io/address/0x82b4935ebe7a5d802cf465a3495da1aff96f1153#code",
+ submittedAt: new Date(1736437841322),
+ reviewComment: "You passed all tests on Challenge 0, keep it up!
",
+ reviewAction: ReviewAction.ACCEPTED,
+ },
+ {
+ userAddress: "0x45334F41aAA464528CD5bc0F582acadC49Eb0Cd1",
+ challengeId: "decentralized-staking",
+ frontendUrl: "https://sepolia-optimism.etherscan.io/address/0x75CCfC494667c91F4926213A04619f93812885b2#code",
+ contractUrl: "https://sepolia-optimism.etherscan.io/address/0x75CCfC494667c91F4926213A04619f93812885b2#code",
+ submittedAt: new Date(1736440199150),
+ reviewComment: "You passed all tests on Challenge 1, keep it up!
",
+ reviewAction: ReviewAction.SUBMITTED,
+ },
+ {
+ userAddress: "0x45334F41aAA464528CD5bc0F582acadC49Eb0Cd1",
+ challengeId: "dice-game",
+ frontendUrl: "https://sepolia-optimism.etherscan.io/address/0xB30b4D0AD811De445656FA49d3CbfD26f24Fa20f#code",
+ contractUrl: "https://sepolia-optimism.etherscan.io/address/0xB30b4D0AD811De445656FA49d3CbfD26f24Fa20f#code",
+ submittedAt: new Date(1736455465938),
+ reviewComment:
+ "This looks good! Demo site and contract code are solid and the dice only roll when it's a winner!
",
+ reviewAction: ReviewAction.ACCEPTED,
+ },
+ {
+ userAddress: "0x45334F41aAA464528CD5bc0F582acadC49Eb0Cd1",
+ challengeId: "minimum-viable-exchange",
+ frontendUrl: "https://sepolia-optimism.etherscan.io/address/0xFD893C93f44fdCd16D4673072D2a071578e6C7Ee#code",
+ contractUrl: "https://sepolia-optimism.etherscan.io/address/0xFD893C93f44fdCd16D4673072D2a071578e6C7Ee#code",
+ submittedAt: new Date(1736523426599),
+ reviewComment: "You have successfully passed the Dex Challenge! Great work!
",
+ reviewAction: ReviewAction.ACCEPTED,
+ },
+ {
+ userAddress: "0x45334F41aAA464528CD5bc0F582acadC49Eb0Cd1",
+ challengeId: "state-channels",
+ frontendUrl: "https://sepolia-optimism.etherscan.io/address/0x1BC437381EC1A36Eb30A0F3AfEFF1Fd5E0078256#code",
+ contractUrl: "https://sepolia-optimism.etherscan.io/address/0x1BC437381EC1A36Eb30A0F3AfEFF1Fd5E0078256#code",
+ submittedAt: new Date(1736524737219),
+ reviewComment: "You have successfully passed the State Channel Challenge! Great work!
",
+ reviewAction: ReviewAction.ACCEPTED,
},
];
export const seedEvents: (typeof events.$inferInsert)[] = [
{
- eventType: "CHALLENGE_SUBMIT",
+ eventType: EventType.CHALLENGE_SUBMIT,
eventAt: new Date(1679063312936),
signature:
"0x7a808ee181d8655f38c48e0154903bea229b91e32f6062f6c23ad9da5fa25c3008238b07e367e7904200015157a43378b39244140c43ea35e5eea11c055a170500",
@@ -220,7 +308,7 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1679063320289),
signature:
"0x7a808ee181d8655f38c48e0154903bea229b91e32f6062f6c23ad9da5fa25c3008238b07e367e7904200015157a43378b39244140c43ea35e5eea11c055a170500",
@@ -228,12 +316,12 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
payload: {
autograding: true,
challengeId: "simple-nft-example",
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
reviewMessage: "Dummy review, nice work",
},
},
{
- eventType: "USER_CREATE",
+ eventType: EventType.USER_CREATE,
eventAt: new Date(1679063274533),
signature:
"0xe6d747b3e4760aa9ceb15ae8274366ab010d057136782bdf77ea755c9f148fc85046b2ed24bcd6a39720e6e62db5cfc52b476099126ef6d90985115d494b606a01",
@@ -243,7 +331,7 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
},
},
{
- eventType: "CHALLENGE_SUBMIT",
+ eventType: EventType.CHALLENGE_SUBMIT,
eventAt: new Date(1672974794575),
signature:
"0x4eca15955283c15c1f9565945c34638c9943a313a79301d73178b814e82e3e183e3d89aebdafa10a1012912cb820950fb6eea6ba76e58bf875f226bdc8257dc700",
@@ -255,7 +343,7 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1679185806311),
signature:
"0x78e60403c8f0d5e8094de16f059d169e8e3a92e3a2df940589f2ce9c2b556d5d0dc0230c421df265fa8975f6175d0a7881a58e0e51b31107c6de1ee4a55a9eb601",
@@ -268,7 +356,7 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1679185814845),
signature:
"0x78e60403c8f0d5e8094de16f059d169e8e3a92e3a2df940589f2ce9c2b556d5d0dc0230c421df265fa8975f6175d0a7881a58e0e51b31107c6de1ee4a55a9eb601",
@@ -276,12 +364,12 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
payload: {
autograding: true,
challengeId: "decentralized-staking",
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
reviewMessage: "Dummy review, it's working great",
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1679317478533),
signature:
"0x4e681d003b6310a604944bd334966cd10348b283d1797db0f93b6ce55f9c02d149b53e59a331362b7f6468528fe4dfee1cd8d923199fc9949ac8386ec633213a01",
@@ -289,12 +377,12 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
payload: {
autograding: true,
challengeId: "token-vendor",
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
reviewMessage: "Dummy review, it's working great",
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1679359253588),
signature:
"0x4eca15955283c15c1f9565945c34638c9943a313a79301d73178b814e82e3e183e3d89aebdafa10a1012912cb820950fb6eea6ba76e58bf875f226bdc8257dc700",
@@ -302,12 +390,12 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
payload: {
autograding: true,
challengeId: "dice-game",
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
reviewMessage: "Dummy review, it's working great",
},
},
{
- eventType: "USER_CREATE",
+ eventType: EventType.USER_CREATE,
eventAt: new Date(1664777161511),
signature:
"0x8d319d59ce02619610376d6ebb2aae2581935d4176ec3ba2228d2b2cab720d260b4d4499f0000ec63d029df5271c10c92ccd68333ddedf506878511f79dd7fad1b",
@@ -317,7 +405,7 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1664778651266),
signature:
"0xc6d7cd95c4baf16851aec5817b08a07ca52f1682f37548c57173d3432df734d17d4e6d810ce55f151dcc5272ae07991305b8d40b0863b4308d5bd74788b645471c",
@@ -325,12 +413,12 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
payload: {
autograding: true,
challengeId: "simple-nft-example",
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
reviewMessage: "Dummy review, nice work",
},
},
{
- eventType: "USER_CREATE",
+ eventType: EventType.USER_CREATE,
eventAt: new Date(1668050419499),
signature:
"0x760bf0c7f94d6fdce07a72d07274e36a258ffe1c4386201b8d34ba7e14882c0432fbedfb77259cbf42599b9939db4fe4ba5a73a4deffef10aa99b757f6f5957e1c",
@@ -340,7 +428,7 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1668196991801),
signature:
"0x03699f6b44e96814d5bac9e4a8a53195216af7b3d4eb44a76206e75ccb7869594755cf526826153335a00e0b140ec3f498db454681bd34d245d801f065b4ccf91c",
@@ -348,12 +436,12 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
payload: {
autograding: true,
challengeId: "simple-nft-example",
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
reviewMessage: "Dummy review, nice work",
},
},
{
- eventType: "CHALLENGE_AUTOGRADE",
+ eventType: EventType.CHALLENGE_AUTOGRADE,
eventAt: new Date(1668830138768),
signature:
"0x40a5005851b26a87a1ed130da6b1eedcf938dda15703f6d7155db088f91cca42496cd84d53e949d0ecb89ed3ed82db3bae9705c6a91b9198693c646690d9dea01c",
@@ -361,7 +449,7 @@ export const seedEvents: (typeof events.$inferInsert)[] = [
payload: {
autograding: true,
challengeId: "decentralized-staking",
- reviewAction: "ACCEPTED",
+ reviewAction: ReviewAction.ACCEPTED,
reviewMessage: "Dummy review, it's working great",
},
},
diff --git a/packages/nextjs/tailwind.config.js b/packages/nextjs/tailwind.config.js
index 876b557c..65adee3e 100644
--- a/packages/nextjs/tailwind.config.js
+++ b/packages/nextjs/tailwind.config.js
@@ -8,19 +8,19 @@ module.exports = {
themes: [
{
light: {
- primary: "#93BBFB",
- "primary-content": "#212638",
- secondary: "#DAE8FF",
- "secondary-content": "#212638",
- accent: "#93BBFB",
- "accent-content": "#212638",
- neutral: "#212638",
+ primary: "#088484",
+ "primary-content": "#026262",
+ secondary: "#C8F5FF",
+ "secondary-content": "#026262",
+ accent: "#67DDDE",
+ "accent-content": "#026262",
+ neutral: "#026262",
"neutral-content": "#ffffff",
"base-100": "#ffffff",
- "base-200": "#f4f8ff",
- "base-300": "#DAE8FF",
- "base-content": "#212638",
- info: "#93BBFB",
+ "base-200": "#E9FBFF",
+ "base-300": "#C8F5FF",
+ "base-content": "#026262",
+ info: "#2FBABB",
success: "#34EEB6",
warning: "#FFCF72",
error: "#FF8863",
@@ -32,19 +32,19 @@ module.exports = {
},
{
dark: {
- primary: "#212638",
- "primary-content": "#F9FBFF",
- secondary: "#323f61",
- "secondary-content": "#F9FBFF",
- accent: "#4969A6",
- "accent-content": "#F9FBFF",
- neutral: "#F9FBFF",
- "neutral-content": "#385183",
- "base-100": "#385183",
- "base-200": "#2A3655",
- "base-300": "#212638",
- "base-content": "#F9FBFF",
- info: "#385183",
+ primary: "#C8F5FF",
+ "primary-content": "#C8F5FF",
+ secondary: "#026262",
+ "secondary-content": "#C8F5FF",
+ accent: "#67DDDE",
+ "accent-content": "#C8F5FF",
+ neutral: "#E9FBFF",
+ "neutral-content": "#088484",
+ "base-100": "#088484",
+ "base-200": "#026262",
+ "base-300": "#088484",
+ "base-content": "#C8F5FF",
+ info: "#67DDDE",
success: "#34EEB6",
warning: "#FFCF72",
error: "#FF8863",
diff --git a/packages/nextjs/utils/dependent-challenges.ts b/packages/nextjs/utils/dependent-challenges.ts
new file mode 100644
index 00000000..8282c35f
--- /dev/null
+++ b/packages/nextjs/utils/dependent-challenges.ts
@@ -0,0 +1,45 @@
+import { ReviewAction } from "~~/services/database/config/types";
+import { UserChallenges } from "~~/services/database/repositories/userChallenges";
+
+// TODO: update deps later
+export const getIsDependencyChallengesCompleted = (deps: { dependencies: string[] }, userChallenges: UserChallenges) =>
+ deps.dependencies?.every(name => {
+ const userChallenge = userChallenges.find(uc => uc.challengeId === name);
+ return userChallenge?.reviewAction === ReviewAction.ACCEPTED;
+ });
+
+const getLockReasonTooltip = ({
+ dependencies,
+ userChallenges,
+}: {
+ dependencies?: string[];
+ userChallenges: UserChallenges;
+}) => {
+ const pendingDependenciesChallenges = dependencies?.filter(dependency => {
+ return (
+ !userChallenges.find(userChallenge => userChallenge.challengeId === dependency)?.reviewAction ||
+ userChallenges.find(userChallenge => userChallenge.challengeId === dependency)?.reviewAction !==
+ ReviewAction.ACCEPTED
+ );
+ });
+
+ if (pendingDependenciesChallenges && pendingDependenciesChallenges.length > 0) {
+ return "The following challenges are not completed: " + pendingDependenciesChallenges?.join(", ");
+ }
+ return "";
+};
+
+export const getChallengeDependenciesInfo = ({
+ dependencies,
+ userChallenges,
+}: {
+ dependencies: string[];
+ userChallenges: UserChallenges;
+}) => {
+ const lockReasonToolTip = getLockReasonTooltip({
+ dependencies,
+ userChallenges,
+ });
+
+ return { completed: !lockReasonToolTip, lockReasonToolTip };
+};
diff --git a/packages/nextjs/utils/scaffold-eth/getMetadata.ts b/packages/nextjs/utils/scaffold-eth/getMetadata.ts
index ad531aea..1294a686 100644
--- a/packages/nextjs/utils/scaffold-eth/getMetadata.ts
+++ b/packages/nextjs/utils/scaffold-eth/getMetadata.ts
@@ -1,9 +1,10 @@
+import Favicon from "/public/favicon.ico";
import type { Metadata } from "next";
const baseUrl = process.env.VERCEL_PROJECT_PRODUCTION_URL
? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
: `http://localhost:${process.env.PORT || 3000}`;
-const titleTemplate = "%s | Scaffold-ETH 2";
+const titleTemplate = "%s | Speed Run Ethereum";
export const getMetadata = ({
title,
@@ -43,8 +44,6 @@ export const getMetadata = ({
description: description,
images: [imageUrl],
},
- icons: {
- icon: [{ url: "/favicon.png", sizes: "32x32", type: "image/png" }],
- },
+ icons: [{ rel: "icon", url: Favicon.src }],
};
};
diff --git a/yarn.lock b/yarn.lock
index 1fdad9e8..25a90e42 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2646,6 +2646,7 @@ __metadata:
next: ~14.2.11
next-mdx-remote: ^5.0.0
next-nprogress-bar: ~2.3.13
+ next-plausible: ^3.12.4
next-themes: ~0.3.0
pg: ^8.13.1
postcss: ~8.4.45
@@ -11647,6 +11648,17 @@ __metadata:
languageName: node
linkType: hard
+"next-plausible@npm:^3.12.4":
+ version: 3.12.4
+ resolution: "next-plausible@npm:3.12.4"
+ peerDependencies:
+ next: "^11.1.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 "
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ checksum: f8ae32eae3b8f4f45e1bdf1ab1aaf6be9ff184468c90253f4920a0cca01b426610c7ac85d082970e614f6f05b3679b9e68aae13ee5081a3d5bd003baaf11005a
+ languageName: node
+ linkType: hard
+
"next-themes@npm:~0.3.0":
version: 0.3.0
resolution: "next-themes@npm:0.3.0"