Skip to content

Commit

Permalink
fix: type errors in settings page and RadioButton component
Browse files Browse the repository at this point in the history
- Fix type error in RadioButton component - Add proper id handling in RadioButton - Remove invalid page prop in settings page - Update type definitions for better type safety
  • Loading branch information
brolag committed Feb 11, 2025
1 parent b4cee6d commit a8af54c
Show file tree
Hide file tree
Showing 29 changed files with 661 additions and 348 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ yarn-error.log*

# Storybook
storybook-static

# Instructions folder
instructions/
179 changes: 92 additions & 87 deletions apps/snfoundry/scripts-ts/helpers/error-handler.ts
Original file line number Diff line number Diff line change
@@ -1,108 +1,113 @@
import { Provider } from "starknet";
import { DeploymentError, DeploymentErrorType, RetryConfig, Networks } from "../types";
import type { Provider } from "starknet";
import {
DeploymentError,
DeploymentErrorType,
Networks,
type RetryConfig,
} from "../types";

class Logger {
private formatMessage(level: string, message: any): string {
const timestamp = new Date().toISOString();
return `[${timestamp}] ${level}: ${typeof message === 'string' ? message : JSON.stringify(message)}`;
}

info(message: any): void {
console.log(this.formatMessage('INFO', message));
}

warn(message: any): void {
console.warn(this.formatMessage('WARN', message));
}

error(message: any): void {
console.error(this.formatMessage('ERROR', message));
}
private formatMessage(level: string, message: unknown): string {
const timestamp = new Date().toISOString();
return `[${timestamp}] ${level}: ${typeof message === "string" ? message : JSON.stringify(message)}`;
}

info(message: unknown): void {
console.log(this.formatMessage("INFO", message));
}

warn(message: unknown): void {
console.warn(this.formatMessage("WARN", message));
}

error(message: unknown): void {
console.error(this.formatMessage("ERROR", message));
}
}

export const logger = new Logger();

const defaultRetryConfig: RetryConfig = {
maxAttempts: 3,
initialDelay: 1000,
maxDelay: 10000,
factor: 2
maxAttempts: 3,
initialDelay: 1000,
maxDelay: 10000,
factor: 2,
};

export async function withRetry<T>(
operation: () => Promise<T>,
config: RetryConfig = defaultRetryConfig,
context: string
operation: () => Promise<T>,
context: string,
config: RetryConfig = defaultRetryConfig,
): Promise<T> {
let delay = config.initialDelay;
let attempt = 0;

while (attempt < config.maxAttempts) {
try {
return await operation();
} catch (error: any) {
attempt++;

const errorType = classifyStarknetError(error);
logger.warn({
message: `Retry attempt ${attempt}/${config.maxAttempts} for ${context}`,
error: error.message,
type: errorType
});

if (attempt === config.maxAttempts || !isRetryableError(errorType)) {
throw new DeploymentError(errorType, error.message);
}

await sleep(delay);
delay = Math.min(delay * config.factor, config.maxDelay);
}
}

throw new DeploymentError(
DeploymentErrorType.UNKNOWN_ERROR,
`Max retry attempts (${config.maxAttempts}) reached for ${context}`
);
let delay = config.initialDelay;
let attempt = 1;

while (attempt <= config.maxAttempts) {
try {
return await operation();
} catch (error: unknown) {
attempt++;

const errorType = classifyStarknetError(error as Error);
logger.warn({
message: `Retry attempt ${attempt}/${config.maxAttempts} for ${context}`,
error: (error as Error).message,
type: errorType,
});

if (attempt === config.maxAttempts || !isRetryableError(errorType)) {
throw new DeploymentError(errorType, (error as Error).message);
}

await sleep(delay);
delay = Math.min(delay * config.factor, config.maxDelay);
}
}

throw new DeploymentError(
DeploymentErrorType.UNKNOWN_ERROR,
`Max retry attempts (${config.maxAttempts}) reached for ${context}`,
);
}

function classifyStarknetError(error: any): DeploymentErrorType {
const errorMsg = error.message.toLowerCase();

if (errorMsg.includes("insufficient max fee")) {
return DeploymentErrorType.GAS_ERROR;
}
if (errorMsg.includes("invalid transaction nonce")) {
return DeploymentErrorType.NONCE_ERROR;
}
if (errorMsg.includes("network") || errorMsg.includes("timeout")) {
return DeploymentErrorType.NETWORK_ERROR;
}
if (errorMsg.includes("contract") || errorMsg.includes("class hash")) {
return DeploymentErrorType.CONTRACT_ERROR;
}
if (errorMsg.includes("invalid") || errorMsg.includes("validation")) {
return DeploymentErrorType.VALIDATION_ERROR;
}
return DeploymentErrorType.UNKNOWN_ERROR;
function classifyStarknetError(error: Error): DeploymentErrorType {
const errorMsg = error.message.toLowerCase();

if (errorMsg.includes("insufficient max fee")) {
return DeploymentErrorType.GAS_ERROR;
}
if (errorMsg.includes("invalid transaction nonce")) {
return DeploymentErrorType.NONCE_ERROR;
}
if (errorMsg.includes("network") || errorMsg.includes("timeout")) {
return DeploymentErrorType.NETWORK_ERROR;
}
if (errorMsg.includes("contract") || errorMsg.includes("class hash")) {
return DeploymentErrorType.CONTRACT_ERROR;
}
if (errorMsg.includes("invalid") || errorMsg.includes("validation")) {
return DeploymentErrorType.VALIDATION_ERROR;
}
return DeploymentErrorType.UNKNOWN_ERROR;
}

function isRetryableError(errorType: DeploymentErrorType): boolean {
return [
DeploymentErrorType.NETWORK_ERROR,
DeploymentErrorType.GAS_ERROR,
DeploymentErrorType.NONCE_ERROR
].includes(errorType);
return [
DeploymentErrorType.NETWORK_ERROR,
DeploymentErrorType.GAS_ERROR,
DeploymentErrorType.NONCE_ERROR,
].includes(errorType);
}

export async function validateNetwork(provider: Provider): Promise<void> {
try {
await provider.getChainId();
} catch (error) {
throw new DeploymentError(
DeploymentErrorType.NETWORK_ERROR,
"Failed to validate network connection"
);
}
try {
await provider.getChainId();
} catch (error) {
throw new DeploymentError(
DeploymentErrorType.NETWORK_ERROR,
"Failed to validate network connection",
);
}
}

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
4 changes: 2 additions & 2 deletions apps/snfoundry/scripts-ts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class DeploymentError extends Error {
public type: DeploymentErrorType,
message: string,
public txHash?: string,
public contractAddress?: string
public contractAddress?: string,
) {
super(message);
this.name = "DeploymentError";
Expand All @@ -45,7 +45,7 @@ export interface RetryConfig {

export interface TransactionQueueItem {
id: string;
execute: () => Promise<any>;
execute: () => Promise<unknown>;
priority: number;
network: keyof Networks;
dependencies?: string[];
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"dependencies": {
"@auth/prisma-adapter": "^1.6.0",
"@prisma/client": "^6.2.1",
"@prisma/client": "5.22.0",
"@repo/ui": "*",
"@starknet-react/chains": "^0.1.7",
"@starknet-react/core": "^2.9.0",
Expand Down
15 changes: 7 additions & 8 deletions apps/web/src/app/_components/features/OrderListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@ export default function OrderListItem({
const { t } = useTranslation();

return (
<Link href="#" onClick={onClick} className="block">
<div
className="flex items-center justify-between py-4"
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
<Link href="#" className="block">
<button
type="button"
onClick={() => {
if (onClick) {
onClick();
}
}}
role="button"
tabIndex={0}
className="flex w-full cursor-pointer items-center justify-between p-4 hover:bg-gray-50"
>
<div className="flex items-center space-x-4">
<Image
Expand All @@ -57,7 +56,7 @@ export default function OrderListItem({
</span>
<ChevronRightIcon className="text-content-body-default w-5 h-5" />
</div>
</div>
</button>
</Link>
);
}
2 changes: 1 addition & 1 deletion apps/web/src/app/_components/features/ProductCatalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default function ProductCatalog() {
farmName={metadata?.farmName ?? ""}
variety={t(product.name)}
price={product.price}
badgeText={t(`strength.${metadata?.strength.toLowerCase()}`)}
badgeText={t(`strength.${metadata?.strength?.toLowerCase()}`)}
onClick={() => accessProductDetails(product.id)}
/>
);
Expand Down
39 changes: 24 additions & 15 deletions apps/web/src/app/_components/features/ProductDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { addItemAtom, cartItemsAtom } from "~/store/cartAtom";
import { api } from "~/trpc/react";
import { ProducerInfo } from "./ProducerInfo";
import { SelectionTypeCard } from "./SelectionTypeCard";

Expand Down Expand Up @@ -45,27 +46,35 @@ export default function ProductDetails({ product }: ProductDetailsProps) {
const [isLiked, setIsLiked] = useState(false);
const router = useRouter();
const [isAddingToCart, setIsAddingToCart] = useState(false);
const [, addItem] = useAtom(addItemAtom);
const items = useAtomValue(cartItemsAtom);
const cartItemsCount = items.reduce(
(total, item) => total + item.quantity,
0,
);
const { data: cart, refetch: refetchCart } = api.cart.getUserCart.useQuery();
const { mutate: addToCart } = api.cart.addToCart.useMutation({
onSuccess: () => {
void refetchCart();
},
});
const cartItemsCount =
cart?.items?.reduce((total, item) => total + item.quantity, 0) ?? 0;

const isSoldOut = type === "SoldOut";
const isFarmer = type === "Farmer";

const handleAddToCart = () => {
setIsAddingToCart(true);
addItem({
id: String(product.id),
tokenId: product.tokenId,
name: product.name,
quantity: quantity,
price: product.price,
imageUrl: product.image,
});
setIsAddingToCart(false);
addToCart(
{
productId: product.id,
quantity: quantity,
},
{
onSuccess: () => {
setIsAddingToCart(false);
},
onError: () => {
setIsAddingToCart(false);
// TODO: Show error toast
},
},
);
};

return (
Expand Down
Loading

0 comments on commit a8af54c

Please sign in to comment.