@@ -233,7 +252,10 @@ export function CreateGardenModal({ isOpen, onClose }: CreateGardenModalProps) {
{gardeners.map((gardener, index) => (
-
+
{gardener}
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/components/Layout/Header.tsx b/packages/admin/src/components/Layout/Header.tsx
index 80cb5e6..7f26200 100644
--- a/packages/admin/src/components/Layout/Header.tsx
+++ b/packages/admin/src/components/Layout/Header.tsx
@@ -8,8 +8,9 @@ export function Header() {
const { switchChain, isPending: isSwitching } = useSwitchChain();
const chainId = useChainId();
const { setSidebarOpen } = useAdminStore();
-
- const currentChain = SUPPORTED_CHAINS.find(chain => chain.id === chainId) || SUPPORTED_CHAINS[0];
+
+ const currentChain =
+ SUPPORTED_CHAINS.find((chain) => chain.id === chainId) || SUPPORTED_CHAINS[0];
return (
@@ -49,4 +50,4 @@ export function Header() {
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/components/Layout/Sidebar.tsx b/packages/admin/src/components/Layout/Sidebar.tsx
index cfa8593..af58acc 100644
--- a/packages/admin/src/components/Layout/Sidebar.tsx
+++ b/packages/admin/src/components/Layout/Sidebar.tsx
@@ -6,7 +6,12 @@ import { useAdminStore } from "@/stores/admin";
import { cn } from "@/utils/cn";
const navigation = [
- { name: "Dashboard", href: "/dashboard", icon: RiDashboardLine, roles: ["deployer", "operator", "user"] },
+ {
+ name: "Dashboard",
+ href: "/dashboard",
+ icon: RiDashboardLine,
+ roles: ["deployer", "operator", "user"],
+ },
{ name: "Gardens", href: "/gardens", icon: RiPlantLine, roles: ["deployer", "operator", "user"] },
{ name: "Contracts", href: "/contracts", icon: RiSettings3Line, roles: ["deployer"] },
];
@@ -17,16 +22,16 @@ export function Sidebar() {
const { role, isDeployer } = useRole();
const { sidebarOpen, setSidebarOpen } = useAdminStore();
- const filteredNavigation = navigation.filter(item =>
- item.roles.includes(role)
- );
+ const filteredNavigation = navigation.filter((item) => item.roles.includes(role));
return (
-
+
{/* Header */}
@@ -36,7 +41,12 @@ export function Sidebar() {
className="lg:hidden p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300"
>
@@ -44,20 +54,25 @@ export function Sidebar() {
{/* Role indicator */}
-
-
{role}
+
+
+ {role}
+
{/* Navigation */}
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/components/Layout/UserProfile.tsx b/packages/admin/src/components/Layout/UserProfile.tsx
index e7a2e24..3b2b01c 100644
--- a/packages/admin/src/components/Layout/UserProfile.tsx
+++ b/packages/admin/src/components/Layout/UserProfile.tsx
@@ -1,5 +1,12 @@
import { useState, useRef, useEffect } from "react";
-import { RiLogoutBoxLine, RiMoonLine, RiSunLine, RiUserLine, RiArrowDownSLine, RiComputerLine } from "@remixicon/react";
+import {
+ RiLogoutBoxLine,
+ RiMoonLine,
+ RiSunLine,
+ RiUserLine,
+ RiArrowDownSLine,
+ RiComputerLine,
+} from "@remixicon/react";
import { useAuth } from "@/providers/AuthProvider";
import { useRole } from "@/hooks/useRole";
import { useDarkMode } from "@/hooks/useDarkMode";
@@ -10,7 +17,7 @@ export function UserProfile() {
const dropdownRef = useRef
(null);
const { disconnect, address } = useAuth();
const { role } = useRole();
- const { isDark, themeMode, setThemeMode } = useDarkMode();
+ const { themeMode, setThemeMode } = useDarkMode();
// Close dropdown when clicking outside
useEffect(() => {
@@ -33,11 +40,11 @@ export function UserProfile() {
const getThemeIcon = (mode: string) => {
switch (mode) {
- case 'light':
+ case "light":
return ;
- case 'dark':
+ case "dark":
return ;
- case 'system':
+ case "system":
return ;
default:
return ;
@@ -46,14 +53,14 @@ export function UserProfile() {
const getThemeLabel = (mode: string) => {
switch (mode) {
- case 'light':
- return 'Light Mode';
- case 'dark':
- return 'Dark Mode';
- case 'system':
- return 'System';
+ case "light":
+ return "Light Mode";
+ case "dark":
+ return "Dark Mode";
+ case "system":
+ return "System";
default:
- return 'System';
+ return "System";
}
};
@@ -64,7 +71,9 @@ export function UserProfile() {
className="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
-
{role}
+
+ {role}
+
{address ? `${address.slice(0, 6)}...${address.slice(-4)}` : ""}
@@ -75,11 +84,11 @@ export function UserProfile() {
{role === "deployer" ? "D" : role === "operator" ? "O" : "U"}
-
@@ -111,7 +120,7 @@ export function UserProfile() {
Theme
- {(['light', 'dark', 'system'] as const).map((mode) => (
+ {(["light", "dark", "system"] as const).map((mode) => (
setThemeMode(mode)}
@@ -146,4 +155,4 @@ export function UserProfile() {
)}
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/components/RequireAuth.tsx b/packages/admin/src/components/RequireAuth.tsx
index 7fd8238..cb689a6 100644
--- a/packages/admin/src/components/RequireAuth.tsx
+++ b/packages/admin/src/components/RequireAuth.tsx
@@ -8,7 +8,10 @@ export function RequireAuth() {
if (!ready) {
return (
);
}
@@ -18,6 +21,6 @@ export function RequireAuth() {
const redirectTo = encodeURIComponent(location.pathname + location.search + location.hash);
return
;
}
-
+
return
;
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/components/RequireRole.tsx b/packages/admin/src/components/RequireRole.tsx
index 9f6e192..94178bf 100644
--- a/packages/admin/src/components/RequireRole.tsx
+++ b/packages/admin/src/components/RequireRole.tsx
@@ -11,7 +11,10 @@ export function RequireRole({ allowedRoles }: RequireRoleProps) {
if (loading) {
return (
);
}
@@ -21,9 +24,7 @@ export function RequireRole({ allowedRoles }: RequireRoleProps) {
Unauthorized
-
- You don't have permission to access this area.
-
+
You don't have permission to access this area.
{role === "user" && (
To access this area, you need to be:
@@ -43,4 +44,4 @@ export function RequireRole({ allowedRoles }: RequireRoleProps) {
}
return
;
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/components/ui/AddressDisplay.tsx b/packages/admin/src/components/ui/AddressDisplay.tsx
index 81a52b2..c6bbea1 100644
--- a/packages/admin/src/components/ui/AddressDisplay.tsx
+++ b/packages/admin/src/components/ui/AddressDisplay.tsx
@@ -9,11 +9,11 @@ interface AddressDisplayProps {
truncateLength?: number;
}
-export function AddressDisplay({
- address,
- className,
- showCopyButton = true,
- truncateLength = 6
+export function AddressDisplay({
+ address,
+ className,
+ showCopyButton = true,
+ truncateLength = 6,
}: AddressDisplayProps) {
const [copied, setCopied] = useState(false);
const [showTooltip, setShowTooltip] = useState(false);
@@ -33,15 +33,13 @@ export function AddressDisplay({
return (
-
setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
>
-
- {truncatedAddress}
-
-
+
{truncatedAddress}
+
{/* Tooltip */}
{showTooltip && (
@@ -50,7 +48,7 @@ export function AddressDisplay({
)}
-
+
{showCopyButton && (
(() => {
- if (typeof window === 'undefined') return 'system'
- const stored = localStorage.getItem('themeMode') as ThemeMode | null
- return stored || 'system'
- })
+ if (typeof window === "undefined") return "system";
+ const stored = localStorage.getItem("themeMode") as ThemeMode | null;
+ return stored || "system";
+ });
const [isDark, setIsDark] = useState(() => {
- if (typeof window === 'undefined') return false
- const stored = localStorage.getItem('themeMode')
- if (stored === 'dark') return true
- if (stored === 'light') return false
+ if (typeof window === "undefined") return false;
+ const stored = localStorage.getItem("themeMode");
+ if (stored === "dark") return true;
+ if (stored === "light") return false;
// System preference
- return window.matchMedia('(prefers-color-scheme: dark)').matches
- })
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
+ });
useEffect(() => {
const updateTheme = () => {
- const root = document.documentElement
- let shouldBeDark = false
+ const root = document.documentElement;
+ let shouldBeDark = false;
- if (themeMode === 'dark') {
- shouldBeDark = true
- } else if (themeMode === 'light') {
- shouldBeDark = false
+ if (themeMode === "dark") {
+ shouldBeDark = true;
+ } else if (themeMode === "light") {
+ shouldBeDark = false;
} else {
// System mode
- shouldBeDark = window.matchMedia('(prefers-color-scheme: dark)').matches
+ shouldBeDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
}
if (shouldBeDark) {
- root.classList.add('dark')
+ root.classList.add("dark");
} else {
- root.classList.remove('dark')
+ root.classList.remove("dark");
}
- setIsDark(shouldBeDark)
- localStorage.setItem('themeMode', themeMode)
- }
+ setIsDark(shouldBeDark);
+ localStorage.setItem("themeMode", themeMode);
+ };
- updateTheme()
+ updateTheme();
// Listen for system theme changes when in system mode
- if (themeMode === 'system') {
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
- const handleChange = () => updateTheme()
- mediaQuery.addEventListener('change', handleChange)
- return () => mediaQuery.removeEventListener('change', handleChange)
+ if (themeMode === "system") {
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
+ const handleChange = () => updateTheme();
+ mediaQuery.addEventListener("change", handleChange);
+ return () => mediaQuery.removeEventListener("change", handleChange);
}
- }, [themeMode])
+ }, [themeMode]);
const toggleTheme = () => {
- if (themeMode === 'light') {
- setThemeMode('dark')
- } else if (themeMode === 'dark') {
- setThemeMode('system')
+ if (themeMode === "light") {
+ setThemeMode("dark");
+ } else if (themeMode === "dark") {
+ setThemeMode("system");
} else {
- setThemeMode('light')
+ setThemeMode("light");
}
- }
+ };
return {
isDark,
themeMode,
setThemeMode,
- toggleTheme
- } as const
+ toggleTheme,
+ } as const;
}
diff --git a/packages/admin/src/hooks/useDeploymentRegistry.ts b/packages/admin/src/hooks/useDeploymentRegistry.ts
index c8abae8..dd0e407 100644
--- a/packages/admin/src/hooks/useDeploymentRegistry.ts
+++ b/packages/admin/src/hooks/useDeploymentRegistry.ts
@@ -52,12 +52,12 @@ export function useDeploymentRegistry(): DeploymentRegistryPermissions {
return;
}
- setPermissions(prev => ({ ...prev, loading: true, error: undefined }));
+ setPermissions((prev) => ({ ...prev, loading: true, error: undefined }));
try {
const contracts = getNetworkContracts(selectedChainId);
const chain = getChainById(selectedChainId);
-
+
// If deployment registry is not configured, return false
if (contracts.deploymentRegistry === "0x0000000000000000000000000000000000000000") {
setPermissions({
@@ -70,7 +70,7 @@ export function useDeploymentRegistry(): DeploymentRegistryPermissions {
}
const alchemyKey = import.meta.env.VITE_ALCHEMY_KEY || "demo";
-
+
let rpcUrl = "";
switch (selectedChainId) {
case 42161: // Arbitrum
@@ -131,4 +131,4 @@ export function useDeploymentRegistry(): DeploymentRegistryPermissions {
}, [address, ready, selectedChainId]);
return permissions;
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/hooks/useGardenOperations.ts b/packages/admin/src/hooks/useGardenOperations.ts
index 9e8f3e5..70e39e7 100644
--- a/packages/admin/src/hooks/useGardenOperations.ts
+++ b/packages/admin/src/hooks/useGardenOperations.ts
@@ -15,7 +15,7 @@ export function useGardenOperations(gardenId: string) {
}
setIsLoading(true);
-
+
try {
const result = await executeWithToast(
async () => {
@@ -48,7 +48,7 @@ export function useGardenOperations(gardenId: string) {
}
setIsLoading(true);
-
+
try {
const result = await executeWithToast(
async () => {
@@ -81,7 +81,7 @@ export function useGardenOperations(gardenId: string) {
}
setIsLoading(true);
-
+
try {
const result = await executeWithToast(
async () => {
@@ -114,7 +114,7 @@ export function useGardenOperations(gardenId: string) {
}
setIsLoading(true);
-
+
try {
const result = await executeWithToast(
async () => {
@@ -148,4 +148,4 @@ export function useGardenOperations(gardenId: string) {
removeOperator,
isLoading,
};
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/hooks/useGardenPermissions.ts b/packages/admin/src/hooks/useGardenPermissions.ts
index ac42d7a..becac1e 100644
--- a/packages/admin/src/hooks/useGardenPermissions.ts
+++ b/packages/admin/src/hooks/useGardenPermissions.ts
@@ -16,9 +16,7 @@ export function useGardenPermissions(): GardenPermissions {
const permissions = useMemo(() => {
const isOperatorOfGarden = (garden: Garden): boolean => {
if (!address || !garden.operators) return false;
- return garden.operators
- .map(op => op.toLowerCase())
- .includes(address.toLowerCase());
+ return garden.operators.map((op) => op.toLowerCase()).includes(address.toLowerCase());
};
const canViewGarden = (_garden: Garden): boolean => {
@@ -51,4 +49,4 @@ export function useGardenPermissions(): GardenPermissions {
}, [address]);
return permissions;
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/hooks/useRole.ts b/packages/admin/src/hooks/useRole.ts
index 1e2e29e..20bf13a 100644
--- a/packages/admin/src/hooks/useRole.ts
+++ b/packages/admin/src/hooks/useRole.ts
@@ -30,7 +30,7 @@ export interface RoleInfo {
export function useRole(): RoleInfo {
const { address, ready } = useAuth();
const deploymentRegistry = useDeploymentRegistry();
-
+
const [{ data: operatorData, fetching }] = useQuery({
query: GET_OPERATOR_GARDENS,
variables: { operator: [address || ""] },
@@ -40,7 +40,7 @@ export function useRole(): RoleInfo {
const operatorGardens = operatorData?.Garden || [];
const isOperator = operatorGardens.length > 0;
const isDeployer = deploymentRegistry.canDeploy;
-
+
// Determine primary role based on capabilities
let role: UserRole = "user";
if (isDeployer) {
@@ -61,4 +61,4 @@ export function useRole(): RoleInfo {
isInAllowlist: deploymentRegistry.isInAllowlist,
},
};
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/hooks/useToastAction.ts b/packages/admin/src/hooks/useToastAction.ts
index af1a3eb..c8514f0 100644
--- a/packages/admin/src/hooks/useToastAction.ts
+++ b/packages/admin/src/hooks/useToastAction.ts
@@ -10,10 +10,7 @@ export interface ToastActionOptions {
export function useToastAction() {
const executeWithToast = useCallback(
- async (
- action: () => Promise,
- options: ToastActionOptions = {}
- ): Promise => {
+ async (action: () => Promise, options: ToastActionOptions = {}): Promise => {
const {
loadingMessage = "Processing...",
successMessage = "Action completed successfully",
@@ -38,4 +35,4 @@ export function useToastAction() {
);
return { executeWithToast };
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/main.tsx b/packages/admin/src/main.tsx
index 7c0cab8..b7eeeca 100644
--- a/packages/admin/src/main.tsx
+++ b/packages/admin/src/main.tsx
@@ -1,7 +1,7 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
-import { WagmiProvider } from 'wagmi'
-import { QueryClientProvider } from '@tanstack/react-query'
+import { WagmiProvider } from "wagmi";
+import { QueryClientProvider } from "@tanstack/react-query";
import App from "@/App.tsx";
import { AuthProvider } from "@/providers/AuthProvider";
@@ -11,22 +11,22 @@ import "@/index.css";
// Initialize theme on app start
function initializeTheme() {
- const themeMode = localStorage.getItem("themeMode") || 'system';
+ const themeMode = localStorage.getItem("themeMode") || "system";
let shouldBeDark = false;
- if (themeMode === 'dark') {
+ if (themeMode === "dark") {
shouldBeDark = true;
- } else if (themeMode === 'light') {
+ } else if (themeMode === "light") {
shouldBeDark = false;
} else {
// System mode
- shouldBeDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ shouldBeDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
}
if (shouldBeDark) {
- document.documentElement.classList.add('dark');
+ document.documentElement.classList.add("dark");
} else {
- document.documentElement.classList.remove('dark');
+ document.documentElement.classList.remove("dark");
}
}
@@ -50,4 +50,4 @@ root.render(
-);
\ No newline at end of file
+);
diff --git a/packages/admin/src/providers/AuthProvider.tsx b/packages/admin/src/providers/AuthProvider.tsx
index ca18e69..4c643ef 100644
--- a/packages/admin/src/providers/AuthProvider.tsx
+++ b/packages/admin/src/providers/AuthProvider.tsx
@@ -1,53 +1,55 @@
-import React, { createContext, useContext } from 'react'
-import { useAccount, useDisconnect } from 'wagmi'
-import { useAppKit } from '@reown/appkit/react'
+import React, { createContext, useContext } from "react";
+import { useAccount, useDisconnect } from "wagmi";
+import { useAppKit } from "@reown/appkit/react";
interface AuthContextType {
// Wallet connection state
- address?: `0x${string}`
- isConnected: boolean
- isConnecting: boolean
-
+ address?: `0x${string}`;
+ isConnected: boolean;
+ isConnecting: boolean;
+
// Actions
- connect: () => void
- disconnect: () => void
-
+ connect: () => void;
+ disconnect: () => void;
+
// Legacy compatibility (will be replaced with onchain roles)
- ready: boolean
- user: { id: string; wallet: { address: string } } | null
+ ready: boolean;
+ user: { id: string; wallet: { address: string } } | null;
}
-const AuthContext = createContext(undefined)
+const AuthContext = createContext(undefined);
export function useAuth(): AuthContextType {
- const context = useContext(AuthContext)
+ const context = useContext(AuthContext);
if (!context) {
- throw new Error('useAuth must be used within AuthProvider')
+ throw new Error("useAuth must be used within AuthProvider");
}
- return context
+ return context;
}
// Legacy hook for compatibility during migration
export function useUser() {
- const { address, ready, user } = useAuth()
+ const { address, ready, user } = useAuth();
return {
address,
ready,
user,
- eoa: user ? { address: address as string } : null
- }
+ eoa: user ? { address: address as string } : null,
+ };
}
export function AuthProvider({ children }: { children: React.ReactNode }) {
- const { address, isConnected, isConnecting } = useAccount()
- const { disconnect } = useDisconnect()
- const { open } = useAppKit()
+ const { address, isConnected, isConnecting } = useAccount();
+ const { disconnect } = useDisconnect();
+ const { open } = useAppKit();
// Create legacy-compatible user object
- const user = address ? {
- id: `user-${address}`,
- wallet: { address: address as string }
- } : null
+ const user = address
+ ? {
+ id: `user-${address}`,
+ wallet: { address: address as string },
+ }
+ : null;
const contextValue: AuthContextType = {
address,
@@ -56,12 +58,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
connect: open,
disconnect,
ready: !isConnecting && isConnected, // Ready when connected and not connecting
- user
- }
+ user,
+ };
- return (
-
- {children}
-
- )
-}
\ No newline at end of file
+ return {children};
+}
diff --git a/packages/admin/src/stores/admin.ts b/packages/admin/src/stores/admin.ts
index a8c7d6e..4d2ab85 100644
--- a/packages/admin/src/stores/admin.ts
+++ b/packages/admin/src/stores/admin.ts
@@ -68,4 +68,4 @@ export const useAdminStore = create()(
sidebarOpen: false,
setSidebarOpen: (open) => set({ sidebarOpen: open }),
}))
-);
\ No newline at end of file
+);
diff --git a/packages/admin/src/types/contracts.ts b/packages/admin/src/types/contracts.ts
index 2e54fa1..adf3a13 100644
--- a/packages/admin/src/types/contracts.ts
+++ b/packages/admin/src/types/contracts.ts
@@ -28,4 +28,4 @@ export interface DeploymentParams {
contractType: string;
chainId: number;
initParams?: unknown[];
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/types/garden.ts b/packages/admin/src/types/garden.ts
index 1a1dc7c..13adec1 100644
--- a/packages/admin/src/types/garden.ts
+++ b/packages/admin/src/types/garden.ts
@@ -10,4 +10,4 @@ export interface Garden {
createdAt: number;
gardeners: string[];
operators: string[];
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/types/global.d.ts b/packages/admin/src/types/global.d.ts
index 0b0a68c..d85f4fc 100644
--- a/packages/admin/src/types/global.d.ts
+++ b/packages/admin/src/types/global.d.ts
@@ -1,4 +1,4 @@
-import 'react';
+import "react";
declare global {
namespace JSX {
@@ -6,7 +6,7 @@ declare global {
/**
* The AppKit button web component. Registered globally by AppKit.
*/
- 'appkit-button': React.DetailedHTMLProps, HTMLElement>;
+ "appkit-button": React.DetailedHTMLProps, HTMLElement>;
}
}
}
diff --git a/packages/admin/src/types/vite-env.d.ts b/packages/admin/src/types/vite-env.d.ts
index 9ebd9a4..240779f 100644
--- a/packages/admin/src/types/vite-env.d.ts
+++ b/packages/admin/src/types/vite-env.d.ts
@@ -11,4 +11,4 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv;
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/utils/cn.ts b/packages/admin/src/utils/cn.ts
index daab5de..a5ef193 100644
--- a/packages/admin/src/utils/cn.ts
+++ b/packages/admin/src/utils/cn.ts
@@ -3,4 +3,4 @@ import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/utils/contracts.ts b/packages/admin/src/utils/contracts.ts
index 2814bd8..3c632cf 100644
--- a/packages/admin/src/utils/contracts.ts
+++ b/packages/admin/src/utils/contracts.ts
@@ -17,7 +17,7 @@ export { GardenTokenABI, GardenAccountABI, ActionRegistryABI };
function getNetworkConfigFromNetworksJson(chainId: number) {
const networks = networksConfig as Record;
-
+
if (networks[chainId]) {
return networks[chainId];
}
@@ -52,13 +52,19 @@ export function getNetworkContracts(chainId: number): NetworkContracts {
gardenToken: deployment.gardenToken || "0x0000000000000000000000000000000000000000",
actionRegistry: deployment.actionRegistry || "0x0000000000000000000000000000000000000000",
workResolver: deployment.workResolver || "0x0000000000000000000000000000000000000000",
- workApprovalResolver: deployment.workApprovalResolver || "0x0000000000000000000000000000000000000000",
- deploymentRegistry: deployment.deploymentRegistry || "0x0000000000000000000000000000000000000000",
+ workApprovalResolver:
+ deployment.workApprovalResolver || "0x0000000000000000000000000000000000000000",
+ deploymentRegistry:
+ deployment.deploymentRegistry || "0x0000000000000000000000000000000000000000",
eas: networkConfig.contracts?.eas || "0x0000000000000000000000000000000000000000",
- easSchemaRegistry: networkConfig.contracts?.easSchemaRegistry || "0x0000000000000000000000000000000000000000",
- communityToken: networkConfig.contracts?.communityToken || "0x0000000000000000000000000000000000000000",
- erc4337EntryPoint: networkConfig.contracts?.erc4337EntryPoint || "0x0000000000000000000000000000000000000000",
- multicallForwarder: networkConfig.contracts?.multicallForwarder || "0x0000000000000000000000000000000000000000",
+ easSchemaRegistry:
+ networkConfig.contracts?.easSchemaRegistry || "0x0000000000000000000000000000000000000000",
+ communityToken:
+ networkConfig.contracts?.communityToken || "0x0000000000000000000000000000000000000000",
+ erc4337EntryPoint:
+ networkConfig.contracts?.erc4337EntryPoint || "0x0000000000000000000000000000000000000000",
+ multicallForwarder:
+ networkConfig.contracts?.multicallForwarder || "0x0000000000000000000000000000000000000000",
};
}
@@ -78,7 +84,7 @@ export function getChainById(chainId: number) {
export function createClients(chainId: number) {
const chain = getChainById(chainId);
const alchemyKey = import.meta.env.VITE_ALCHEMY_KEY || "demo";
-
+
let rpcUrl = "";
switch (chainId) {
case arbitrum.id:
@@ -100,4 +106,4 @@ export function createClients(chainId: number) {
});
return { publicClient };
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/utils/pinata.ts b/packages/admin/src/utils/pinata.ts
index 081034a..d116c62 100644
--- a/packages/admin/src/utils/pinata.ts
+++ b/packages/admin/src/utils/pinata.ts
@@ -23,29 +23,29 @@ export async function getFileByHash(hash: string) {
*/
export function resolveIPFSUrl(url: string): string {
if (!url) return "";
-
+
// If it's already a proper gateway URL, return as is
if (url.startsWith("https://greengoods.mypinata.cloud/")) {
return url;
}
-
+
// Handle ipfs:// protocol
if (url.startsWith("ipfs://")) {
const hash = url.replace("ipfs://", "");
return `https://greengoods.mypinata.cloud/ipfs/${hash}`;
}
-
+
// Handle https://ipfs.io/ URLs
if (url.includes("ipfs.io/ipfs/")) {
const hash = url.split("ipfs.io/ipfs/")[1];
return `https://greengoods.mypinata.cloud/ipfs/${hash}`;
}
-
+
// Handle direct hash
if (url.startsWith("Qm") || url.startsWith("baf")) {
return `https://greengoods.mypinata.cloud/ipfs/${url}`;
}
-
+
// Return original URL if no IPFS pattern matched
return url;
}
diff --git a/packages/admin/src/utils/urql.ts b/packages/admin/src/utils/urql.ts
index c44eaa1..1278f3b 100644
--- a/packages/admin/src/utils/urql.ts
+++ b/packages/admin/src/utils/urql.ts
@@ -16,8 +16,8 @@ export const urqlClient = createClient({
fetchOptions: () => {
return {
headers: {
- 'Content-Type': 'application/json',
+ "Content-Type": "application/json",
},
};
},
-});
\ No newline at end of file
+});
diff --git a/packages/admin/src/views/Contracts/index.tsx b/packages/admin/src/views/Contracts/index.tsx
index c288156..df48dd0 100644
--- a/packages/admin/src/views/Contracts/index.tsx
+++ b/packages/admin/src/views/Contracts/index.tsx
@@ -9,7 +9,7 @@ export default function Contracts() {
const [activeTab, setActiveTab] = useState<"deployed" | "deploy" | "upgrade">("deployed");
const contracts = getNetworkContracts(selectedChainId);
- const currentChain = SUPPORTED_CHAINS.find(c => c.id === selectedChainId);
+ const currentChain = SUPPORTED_CHAINS.find((c) => c.id === selectedChainId);
const contractList = [
{ name: "Garden Token", address: contracts.gardenToken, type: "core" },
@@ -111,7 +111,9 @@ export default function Contracts() {
{contract.address}
-
+
{getStatusText(contract.address)}
|
@@ -164,4 +166,4 @@ export default function Contracts() {
)}
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/views/Dashboard/index.tsx b/packages/admin/src/views/Dashboard/index.tsx
index 609b2ea..e1f2baa 100644
--- a/packages/admin/src/views/Dashboard/index.tsx
+++ b/packages/admin/src/views/Dashboard/index.tsx
@@ -19,16 +19,16 @@ const GET_DASHBOARD_STATS = graphql(`
export default function Dashboard() {
const { role, operatorGardens } = useRole();
const chainId = useChainId();
- const [{ data, fetching, error }] = useQuery({
+ const [{ data, fetching, error }] = useQuery({
query: GET_DASHBOARD_STATS,
- variables: { chainId }
+ variables: { chainId },
});
const gardens = data?.Garden || [];
const totalGardens = gardens.length;
const userOperatorGardens = operatorGardens.length;
- const totalOperators = new Set((gardens as Garden[]).flatMap(g => g.operators)).size;
- const totalGardeners = new Set((gardens as Garden[]).flatMap(g => g.gardeners)).size;
+ const totalOperators = new Set((gardens as Garden[]).flatMap((g) => g.operators)).size;
+ const totalGardeners = new Set((gardens as Garden[]).flatMap((g) => g.gardeners)).size;
if (fetching) {
return (
@@ -36,7 +36,7 @@ export default function Dashboard() {
- {[1, 2, 3].map(i => (
+ {[1, 2, 3].map((i) => (
))}
@@ -52,35 +52,42 @@ export default function Dashboard() {
Indexer Connection Issue
Unable to connect to the indexer: {error.message}
-
The dashboard will work with limited functionality. Garden operations are still available.
+
+ The dashboard will work with limited functionality. Garden operations are still
+ available.
+
-
+
{/* Fallback dashboard without stats */}
- Welcome back, {role === "deployer" ? "Deployer" : role === "operator" ? "Operator" : "User"}
+ Welcome back,{" "}
+ {role === "deployer" ? "Deployer" : role === "operator" ? "Operator" : "User"}
- {role === "deployer"
+ {role === "deployer"
? "Manage gardens, deploy contracts, and oversee platform operations"
- : role === "operator"
- ? `Manage your ${operatorGardens.length} garden${operatorGardens.length !== 1 ? 's' : ''}`
- : "View gardens and explore the Green Goods ecosystem"
- }
+ : role === "operator"
+ ? `Manage your ${operatorGardens.length} garden${operatorGardens.length !== 1 ? "s" : ""}`
+ : "View gardens and explore the Green Goods ecosystem"}
-
+
Quick Actions
@@ -111,15 +118,15 @@ export default function Dashboard() {
- Welcome back, {role === "deployer" ? "Deployer" : role === "operator" ? "Operator" : "User"}
+ Welcome back,{" "}
+ {role === "deployer" ? "Deployer" : role === "operator" ? "Operator" : "User"}
- {role === "deployer"
+ {role === "deployer"
? "Manage gardens, deploy contracts, and oversee platform operations"
- : role === "operator"
- ? `Manage your ${operatorGardens.length} garden${operatorGardens.length !== 1 ? 's' : ''}`
- : "View gardens and explore the Green Goods ecosystem"
- }
+ : role === "operator"
+ ? `Manage your ${operatorGardens.length} garden${operatorGardens.length !== 1 ? "s" : ""}`
+ : "View gardens and explore the Green Goods ecosystem"}
@@ -149,8 +156,12 @@ export default function Dashboard() {
-
Total Operators
-
{totalOperators}
+
+ Total Operators
+
+
+ {totalOperators}
+
@@ -161,8 +172,12 @@ export default function Dashboard() {
-
Total Gardeners
-
{totalGardeners}
+
+ Total Gardeners
+
+
+ {totalGardeners}
+
@@ -175,40 +190,44 @@ export default function Dashboard() {
Recent Gardens
-
- {role === "operator"
- ? operatorGardens.slice(0, 5).map((garden) => (
-
-
-
{garden.name}
-
Operator Garden
-
-
+
+ {role === "operator"
+ ? operatorGardens.slice(0, 5).map((garden) => (
+
+
+
{garden.name}
+
Operator Garden
+
+
- ))
- : (gardens as Garden[]).slice(0, 5).map((garden: Garden) => (
-
-
-
{garden.name}
-
{garden.location || "No location"}
-
-
-
- {garden.operators?.length || 0} operators, {garden.gardeners?.length || 0} gardeners
-
-
+
+ ))
+ : (gardens as Garden[]).slice(0, 5).map((garden: Garden) => (
+
+
+
{garden.name}
+
{garden.location || "No location"}
- ))
- }
- {(role === "operator" ? operatorGardens : gardens).length === 0 && (
-
No gardens found
- )}
+
+
+ {garden.operators?.length || 0} operators, {garden.gardeners?.length || 0}{" "}
+ gardeners
+
+
+
+ ))}
+ {(role === "operator" ? operatorGardens : gardens).length === 0 && (
+
No gardens found
+ )}
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/views/Gardens/Detail.tsx b/packages/admin/src/views/Gardens/Detail.tsx
index be33b00..60bba95 100644
--- a/packages/admin/src/views/Gardens/Detail.tsx
+++ b/packages/admin/src/views/Gardens/Detail.tsx
@@ -39,14 +39,15 @@ export default function GardenDetail() {
setAddMemberModalOpen(true);
console.log("🟢 Modal state set:", { type, open: true });
};
-
+
const [{ data, fetching, error }] = useQuery({
query: GET_GARDEN_DETAIL,
variables: { id: id! },
pause: !id,
});
- const { addGardener, removeGardener, addOperator, removeOperator, isLoading } = useGardenOperations(id!);
+ const { addGardener, removeGardener, addOperator, removeOperator, isLoading } =
+ useGardenOperations(id!);
const garden = data?.Garden?.[0];
@@ -68,9 +69,7 @@ export default function GardenDetail() {
return (
-
- {error?.message || "Garden not found"}
-
+
{error?.message || "Garden not found"}
);
@@ -89,7 +88,9 @@ export default function GardenDetail() {
-
{garden.name}
+
+ {garden.name}
+
{garden.location}
•
@@ -103,150 +104,155 @@ export default function GardenDetail() {
-
- {/* Garden Info */}
-
-
- {garden.bannerImage ? (
-
})
{
- const placeholder = e.currentTarget.nextElementSibling as HTMLElement;
- if (placeholder) {
- placeholder.style.display = 'flex';
- }
- e.currentTarget.style.display = 'none';
- }}
- loading="lazy"
- />
- ) : null}
- {/* Gradient placeholder */}
-
-
-
{garden.name.charAt(0)}
-
{garden.name}
+ {/* Garden Info */}
+
+
+ {garden.bannerImage ? (
+
})
{
+ const placeholder = e.currentTarget.nextElementSibling as HTMLElement;
+ if (placeholder) {
+ placeholder.style.display = "flex";
+ }
+ e.currentTarget.style.display = "none";
+ }}
+ loading="lazy"
+ />
+ ) : null}
+ {/* Gradient placeholder */}
+
+
+
{garden.name.charAt(0)}
+
{garden.name}
+
+
+
Description
+
{garden.description}
+
-
-
Description
-
{garden.description}
-
-
- {/* Members Management */}
-
- {/* Operators */}
-
-
-
-
Operators
- {canManage && (
-
openAddMemberModal("operator")}
- className="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-green-700 bg-green-100 hover:bg-green-200"
- >
-
- Add
-
- )}
+ {/* Members Management */}
+
+ {/* Operators */}
+
+
+
+
Operators
+ {canManage && (
+ openAddMemberModal("operator")}
+ className="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-green-700 bg-green-100 hover:bg-green-200"
+ >
+
+ Add
+
+ )}
+
-
-
- {garden.operators.length === 0 ? (
-
No operators assigned
- ) : (
-
- {garden.operators.map((operator: string, index: number) => (
-
-
-
-
+
+ {garden.operators.length === 0 ? (
+
No operators assigned
+ ) : (
+
+ {garden.operators.map((operator: string, index: number) => (
+
+
-
+ {canManage && (
+
removeOperator(operator)}
+ disabled={isLoading}
+ className="text-red-600 hover:text-red-800 disabled:opacity-50"
+ >
+
+
+ )}
- {canManage && (
-
removeOperator(operator)}
- disabled={isLoading}
- className="text-red-600 hover:text-red-800 disabled:opacity-50"
- >
-
-
- )}
-
- ))}
-
- )}
-
-
-
- {/* Gardeners */}
-
-
-
-
Gardeners
- {canManage && (
- openAddMemberModal("gardener")}
- className="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-green-700 bg-green-100 hover:bg-green-200"
- >
-
- Add
-
+ ))}
+
)}
-
- {garden.gardeners.length === 0 ? (
-
No gardeners assigned
- ) : (
-
- {garden.gardeners.map((gardener: string, index: number) => (
-
-
-
-
+
+ {/* Gardeners */}
+
+
+
+
Gardeners
+ {canManage && (
+ openAddMemberModal("gardener")}
+ className="inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md text-green-700 bg-green-100 hover:bg-green-200"
+ >
+
+ Add
+
+ )}
+
+
+
+ {garden.gardeners.length === 0 ? (
+
No gardeners assigned
+ ) : (
+
+ {garden.gardeners.map((gardener: string, index: number) => (
+
+
-
+ {canManage && (
+
removeGardener(gardener)}
+ disabled={isLoading}
+ className="text-red-600 hover:text-red-800 disabled:opacity-50"
+ >
+
+
+ )}
- {canManage && (
-
removeGardener(gardener)}
- disabled={isLoading}
- className="text-red-600 hover:text-red-800 disabled:opacity-50"
- >
-
-
- )}
-
- ))}
-
- )}
+ ))}
+
+ )}
+
-
- {/* Add Member Modal */}
-
setAddMemberModalOpen(false)}
- memberType={memberType}
- onAdd={async (address: string) => {
- if (memberType === "gardener") {
- await addGardener(address);
- } else {
- await addOperator(address);
- }
- }}
- isLoading={isLoading}
- />
+ {/* Add Member Modal */}
+ setAddMemberModalOpen(false)}
+ memberType={memberType}
+ onAdd={async (address: string) => {
+ if (memberType === "gardener") {
+ await addGardener(address);
+ } else {
+ await addOperator(address);
+ }
+ }}
+ isLoading={isLoading}
+ />
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/views/Gardens/index.tsx b/packages/admin/src/views/Gardens/index.tsx
index 5bc9d9e..e86be08 100644
--- a/packages/admin/src/views/Gardens/index.tsx
+++ b/packages/admin/src/views/Gardens/index.tsx
@@ -33,9 +33,9 @@ export default function Gardens() {
const gardenPermissions = useGardenPermissions();
const [createModalOpen, setCreateModalOpen] = useState(false);
const chainId = useChainId();
- const [{ data, fetching, error }] = useQuery({
+ const [{ data, fetching, error }] = useQuery({
query: GET_GARDENS,
- variables: { chainId }
+ variables: { chainId },
});
// Load all gardens - permissions are checked at garden level
@@ -47,13 +47,18 @@ export default function Gardens() {
Gardens
-
Manage your gardens and view garden details
+
+ Manage your gardens and view garden details
+
-
+
{[...Array(8)].map((_, i) => (
-
+
@@ -64,7 +69,7 @@ export default function Gardens() {
-
+
@@ -90,19 +95,26 @@ export default function Gardens() {
Indexer Connection Issue
Unable to load gardens from indexer: {error.message}
-
Garden management features are still available if you have direct garden addresses.
+
+ Garden management features are still available if you have direct garden
+ addresses.
+
-
+
{/* Fallback content */}
@@ -122,7 +134,7 @@ export default function Gardens() {
)}
-
+
@@ -133,12 +145,9 @@ export default function Gardens() {
-
+
{isDeployer && (
-
setCreateModalOpen(false)}
- />
+ setCreateModalOpen(false)} />
)}
);
@@ -169,56 +178,60 @@ export default function Gardens() {
No gardens
- {isDeployer
- ? "Get started by creating your first garden."
- : "No gardens created yet."
- }
+ {isDeployer ? "Get started by creating your first garden." : "No gardens created yet."}
) : (
- {(gardens as Garden[]).map((garden: Garden) => {
- const canManage = gardenPermissions.canManageGarden(garden);
- const resolvedBannerImage = garden.bannerImage ? resolveIPFSUrl(garden.bannerImage) : null;
-
- return (
-
-
- {resolvedBannerImage ? (
-

{
- const placeholder = e.currentTarget.nextElementSibling as HTMLElement;
- if (placeholder) {
- placeholder.style.display = 'flex';
- }
- e.currentTarget.style.display = 'none';
- }}
- loading="lazy"
- />
- ) : null}
- {/* Gradient placeholder */}
-
-
-
{garden.name.charAt(0)}
+ {(gardens as Garden[]).map((garden: Garden) => {
+ const canManage = gardenPermissions.canManageGarden(garden);
+ const resolvedBannerImage = garden.bannerImage
+ ? resolveIPFSUrl(garden.bannerImage)
+ : null;
+
+ return (
+
+
+ {resolvedBannerImage ? (
+

{
+ const placeholder = e.currentTarget.nextElementSibling as HTMLElement;
+ if (placeholder) {
+ placeholder.style.display = "flex";
+ }
+ e.currentTarget.style.display = "none";
+ }}
+ loading="lazy"
+ />
+ ) : null}
+ {/* Gradient placeholder */}
+
+
+
{garden.name.charAt(0)}
+
+ {canManage && (
+
+
+ Operator
+
+ )}
- {canManage && (
-
-
- Operator
-
- )}
-
-
{garden.name}
+
+ {garden.name}
+
{garden.location}
{!resolvedBannerImage && canManage && (
@@ -228,8 +241,10 @@ export default function Gardens() {
)}
-
{garden.description}
-
+
+ {garden.description}
+
+
@@ -260,11 +275,8 @@ export default function Gardens() {
)}
{isDeployer && (
- setCreateModalOpen(false)}
- />
+ setCreateModalOpen(false)} />
)}
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/views/Login/index.tsx b/packages/admin/src/views/Login/index.tsx
index 0471667..67488ab 100644
--- a/packages/admin/src/views/Login/index.tsx
+++ b/packages/admin/src/views/Login/index.tsx
@@ -22,8 +22,16 @@ export default function Login() {
Green Goods
@@ -31,14 +39,11 @@ export default function Login() {
Garden management dashboard for the Green Goods protocol
-
+
{/* Custom styled connect button */}
-
-
+
+
Connect your wallet to access garden management features
@@ -47,4 +52,4 @@ export default function Login() {
);
-}
\ No newline at end of file
+}
diff --git a/packages/admin/src/workflows/createGarden.ts b/packages/admin/src/workflows/createGarden.ts
index 3ef3a35..25d0322 100644
--- a/packages/admin/src/workflows/createGarden.ts
+++ b/packages/admin/src/workflows/createGarden.ts
@@ -150,4 +150,4 @@ export const createGardenMachine = createMachine({
},
},
},
-});
\ No newline at end of file
+});