Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2367e44
fix: update bonus pods from 10% to 5% in sow order form
github-actions[bot] Jan 22, 2026
617fae5
Merge pull request #405 from pinto-org/claude/issue-issue-404-2026012…
fr1jo Jan 22, 2026
2c18f54
feat: implement dashboard for legacy beanstalk holder obligations
feyyazcigim Feb 4, 2026
b6d1d75
chore: remove non-existent contract calls and add subgraph TODOs
feyyazcigim Feb 4, 2026
01f94e7
refactor: extract repayment field ID to constant
github-actions[bot] Feb 6, 2026
01c3a9d
refactor: replace anchor tag with Link component in Beanstalk page
github-actions[bot] Feb 6, 2026
eaf5465
refactor: extract urBDV and Sprouts to internal token definitions
github-actions[bot] Feb 6, 2026
4877ee8
feat(transfer): add consolidated pod range selection and summary view
feyyazcigim Feb 11, 2026
9368d25
feat(market): support marketplace filtering and update plot metadata
feyyazcigim Feb 11, 2026
699bb76
feat(transfer): add silo, pod, and fertilizer transfer flows
feyyazcigim Feb 13, 2026
7c5d203
Merge branch 'feature/upgrade-send-pods' into feature/beanstalk-asset…
feyyazcigim Feb 13, 2026
fa1c8d4
feat(transfer): update pod and fertilizer selection and transfer flows
feyyazcigim Feb 13, 2026
660024f
Merge branch 'feature/beanstalk-marketplace' into feature/beanstalk-a…
feyyazcigim Feb 13, 2026
d81b93d
feat: implement beanstalk obligation repayment and claim functionality
feyyazcigim Feb 13, 2026
1f8ac96
feat: enhance beanstalk repayment data fetching and transfer flows
feyyazcigim Feb 13, 2026
b57b211
fix(beanstalk): update transaction hook callback and rinse logic
feyyazcigim Feb 13, 2026
df380c4
feat: implement direct pod harvesting in obligations card
feyyazcigim Feb 13, 2026
605b9c9
refactor(beanstalk): update fertilizer count calculation and labeling
feyyazcigim Feb 13, 2026
dfe4aa4
feat(transfer): optimize log scanning and enhance batch transfer flow
feyyazcigim Feb 15, 2026
ea10151
feat(ui): improve beanstalk obligations and streamline pod transfer flow
feyyazcigim Feb 16, 2026
c4b1b6f
feat: integrate beanstalk marketplace toggle and field-specific logic
feyyazcigim Feb 16, 2026
674b768
chore: remove unecessary buttons from beanstalk pods send page
feyyazcigim Feb 16, 2026
b5bd5df
chore: update fertilizer text order
feyyazcigim Feb 16, 2026
267e9ab
fix: make 'My Pods In Line' label black in pod transfer steps
github-actions[bot] Feb 18, 2026
1a36053
fix: update text color hierarchy in Beanstalk dashboard
github-actions[bot] Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/ProtectedLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { isDev } from "@/utils/utils";
import { Navigate, Route, Routes } from "react-router-dom";
import DevPage from "./components/DevPage";
import PageMetaWrapper from "./components/PageMetaWrapper";
import Beanstalk from "./pages/Beanstalk";
import Collection from "./pages/Collection";
import Error404 from "./pages/Error404";
import Explorer from "./pages/Explorer";
Expand Down Expand Up @@ -69,6 +70,14 @@ export default function ProtectedLayout() {
</PageMetaWrapper>
}
/>
<Route
path="/beanstalk"
element={
<PageMetaWrapper metaKey="beanstalk">
<Beanstalk />
</PageMetaWrapper>
}
/>
<Route
path="/market/pods"
element={
Expand Down
24 changes: 24 additions & 0 deletions src/assets/protocol/Fertilizer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions src/components/BeanstalkStatField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import TextSkeleton from "@/components/TextSkeleton";
import { Button } from "@/components/ui/Button";
import { ReactNode } from "react";

interface BeanstalkStatFieldAction {
label: string;
onClick?: () => void;
disabled?: boolean;
}

interface BeanstalkStatFieldProps {
title: string;
value: ReactNode;
isLoading?: boolean;
disabled?: boolean;
actions?: BeanstalkStatFieldAction[];
children?: ReactNode;
}

/**
* Reusable stat field component with title, value, and optional action buttons
* Used in Beanstalk obligations and global stats cards
*/
const BeanstalkStatField: React.FC<BeanstalkStatFieldProps> = ({
title,
value,
isLoading = false,
disabled = false,
actions,
children,
}) => {
return (
<div className={`flex flex-col gap-1 ${disabled ? "opacity-60" : ""}`}>
<div className="flex items-center justify-between">
<div className="pinto-sm sm:pinto-body-light text-pinto-gray-6 sm:text-pinto-gray-6">{title}</div>
{actions && actions.length > 0 && (
<div className="flex items-center gap-3">
{actions.map((action) => (
<Button
key={action.label}
variant="hoverTextPrimary"
size="sm"
noPadding
onClick={action.onClick}
disabled={disabled || action.disabled}
className="pinto-sm sm:pinto-body-light"
>
{action.label}
</Button>
))}
</div>
)}
</div>
{children ? (
children
) : (
<TextSkeleton loading={isLoading} height="body" className="w-24">
<div className="pinto-sm sm:pinto-body-light">
{disabled ? <span className="text-pinto-light">N/A</span> : value}
</div>
</TextSkeleton>
)}
</div>
);
};

export default BeanstalkStatField;
14 changes: 11 additions & 3 deletions src/components/ComboInputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export interface ComboInputProps extends InputHTMLAttributes<HTMLInputElement> {

// Additional info display
showAdditionalInfo?: boolean;
hideUsdValue?: boolean;
}

function ComboInputField({
Expand Down Expand Up @@ -146,6 +147,7 @@ function ComboInputField({
enableSlider,
sliderMarkers,
showAdditionalInfo = true,
hideUsdValue = false,
}: ComboInputProps) {
const tokenData = useTokenData();
const { balances } = useFarmerBalances();
Expand Down Expand Up @@ -230,8 +232,9 @@ function ComboInputField({
}

// If customMaxAmount is provided and greater than 0, use the minimum of base balance and customMaxAmount
// If base balance is 0 (no token selected or no farmer balance), use customMaxAmount directly
if (customMaxAmount?.gt(0)) {
return TokenValue.min(baseBalance, customMaxAmount);
return baseBalance.gt(0) ? TokenValue.min(baseBalance, customMaxAmount) : customMaxAmount;
}

// Otherwise use base balance
Expand All @@ -256,7 +259,12 @@ function ComboInputField({
return tokenAndBalanceMap.get(selectedToken) ?? TokenValue.ZERO;
}
// Always use farmerTokenBalance for display, not maxAmount (which may be limited by customMaxAmount)
return getFarmerBalanceByMode(farmerTokenBalance, balanceFrom);
const farmerBalance = getFarmerBalanceByMode(farmerTokenBalance, balanceFrom);
// If farmer balance is 0 and customMaxAmount is provided, show customMaxAmount as the balance
if (farmerBalance.eq(0) && customMaxAmount?.gt(0)) {
return customMaxAmount;
}
return farmerBalance;
}, [mode, selectedPlots, tokenAndBalanceMap, selectedToken, farmerTokenBalance, balanceFrom, getFarmerBalanceByMode]);

/**
Expand Down Expand Up @@ -630,7 +638,7 @@ function ComboInputField({
{!disableInlineBalance && (
<div className="flex flex-row gap-2 justify-between items-center">
<div className="font-[340] text-[1rem] text-pinto-gray-4 flex flex-row gap-2 items-center">
{shouldShowAdditionalInfo() && mode !== "plots" ? (
{shouldShowAdditionalInfo() && mode !== "plots" && !hideUsdValue ? (
<TextSkeleton loading={isLoading} className="flex w-8 h-4 rounded-lg">
{formatter.usd(inputValue)}
</TextSkeleton>
Expand Down
67 changes: 67 additions & 0 deletions src/components/FertilizerCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import fertilizerIcon from "@/assets/protocol/Fertilizer.svg";
import CheckmarkCircle from "@/components/CheckmarkCircle";
import IconImage from "@/components/ui/IconImage";
import { Input } from "@/components/ui/Input";
import { formatter } from "@/utils/format";

interface FertilizerCardProps {
fertId: bigint;
amount: string;
isSelected: boolean;
maxBalance: bigint;
sprouts: string;
humidity: string;
onToggleSelection: (fertId: bigint) => void;
onAmountChange: (fertId: bigint, value: string, maxBalance: bigint) => void;
}

export default function FertilizerCard({
fertId,
amount,
isSelected,
maxBalance,
sprouts,
humidity,
onToggleSelection,
onAmountChange,
}: FertilizerCardProps) {
return (
<div className="flex flex-row items-center gap-3 p-4 rounded-lg bg-white hover:bg-pinto-green-1/30 transition-colors">
{/* Checkbox */}
<div className="flex-shrink-0 cursor-pointer" onClick={() => onToggleSelection(fertId)}>
<CheckmarkCircle isSelected={isSelected} />
</div>

{/* Fertilizer icon */}
<div className="flex-shrink-0">
<IconImage src={fertilizerIcon} size={12} mobileSize={12} />
</div>

{/* Fertilizer info */}
<div className="flex-1 flex flex-col gap-1">
<div className="pinto-body font-[500] text-black">
{sprouts} Sprouts
<span className="pinto-sm text-pinto-gray-3 ml-1.5">Humidity: {humidity}</span>
</div>
<div className="pinto-sm text-pinto-gray-4">
{formatter.number(Number(maxBalance))} bsFERT - ID {formatter.number(Number(fertId))}
</div>
</div>

{/* Amount input */}
<div className="flex-shrink-0 w-52">
<Input
type="number"
placeholder="Amount to Transfer"
value={amount}
onChange={(e) => onAmountChange(fertId, e.target.value, maxBalance)}
outlined={true}
containerClassName="border border-pinto-green-4 focus-within:border-pinto-green-4"
min="0"
max={maxBalance.toString()}
step="1"
/>
</div>
</div>
);
}
21 changes: 19 additions & 2 deletions src/components/PintoAssetTransferNotice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,26 @@ export default function PintoAssetTransferNotice({
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
className="text-2xl"
className="flex flex-col gap-8"
>
Note: be sure recipient address is intended.
<p className="text-2xl">Note: be sure recipient address is intended.</p>
<div className="flex flex-row gap-3 items-center">
<Checkbox
id="wallet-balance-notice"
className="bg-white text-pinto-green-4"
checked={transferNotice}
onCheckedChange={(checked) => {
if (checked !== "indeterminate") {
setTransferNotice(checked);
} else {
setTransferNotice(false);
}
}}
/>
<Label htmlFor="wallet-balance-notice" className="text-sm font-medium cursor-pointer text-yellow-900">
I acknowledge the recipient address is correct
</Label>
</div>
</motion.div>
)}
</AnimatePresence>
Expand Down
Loading