@@ -5,6 +5,7 @@ import useEnvResources from '@/components/hooks/use-env-resources';
55import Input from '@/components/input/input' ;
66import Select from '@/components/input/select' ;
77import Slider from '@/components/slider/slider' ;
8+ import config from '@/config' ;
89import { SelectedToken , useRunJobContext } from '@/context/run-job-context' ;
910import { useP2P } from '@/contexts/P2PContext' ;
1011import { useOceanAccount } from '@/lib/use-ocean-account' ;
@@ -41,6 +42,8 @@ const SelectResources = ({ environment, freeCompute, token }: SelectResourcesPro
4142 const { closeAuthModal, isOpen : isAuthModalOpen , openAuthModal } = useAuthModal ( ) ;
4243 const router = useRouter ( ) ;
4344
45+ const { isReady : p2pReady } = useP2P ( ) ;
46+
4447 const { account } = useOceanAccount ( ) ;
4548
4649 const {
@@ -52,8 +55,6 @@ const SelectResources = ({ environment, freeCompute, token }: SelectResourcesPro
5255 setSelectedResources,
5356 } = useRunJobContext ( ) ;
5457
55- const { isReady : p2pReady } = useP2P ( ) ;
56-
5758 const [ initComputeError , setInitComputeError ] = useState < unknown | null > ( null ) ;
5859 const [ isLoadingCost , setIsLoadingCost ] = useState ( false ) ;
5960
@@ -275,45 +276,83 @@ const SelectResources = ({ environment, freeCompute, token }: SelectResourcesPro
275276 formik . setFieldValue ( 'maxJobDurationValue' , Math . max ( 0 , num ) ) ;
276277 } ;
277278
278- const renderCostEstimation = ( ) => {
279- if ( isLoadingCost ) {
280- return (
281- < h3 className = { styles . estimationMessage } >
282- < CircularProgress size = { 24 } />
283- Estimating cost...
284- </ h3 >
285- ) ;
286- }
287- if ( ! ! initComputeError || ! token ) {
288- let errorText ;
289- if ( initComputeError instanceof Error ) {
290- errorText = initComputeError . message ;
279+ const renderCostCard = ( ) => {
280+ const renderCostEstimation = ( ) => {
281+ if ( isLoadingCost ) {
282+ return (
283+ < h3 className = { styles . estimationMessage } >
284+ < CircularProgress size = { 24 } />
285+ Estimating cost...
286+ </ h3 >
287+ ) ;
288+ }
289+ if ( ! p2pReady || ( ! estimatedTotalCost && estimatedTotalCost !== 0 ) ) {
290+ return (
291+ < h3 className = { styles . estimationMessage } >
292+ < CircularProgress size = { 24 } />
293+ Connecting to node...
294+ </ h3 >
295+ ) ;
291296 }
292297 return (
293- < h3 className = { styles . estimationMessage } >
294- Cost estimation failed{ ' ' }
295- { errorText ? (
296- < Tooltip className = "textAccent1" title = { errorText } >
297- < InfoOutlinedIcon className = { styles . accessInfoIcon } />
298- </ Tooltip >
299- ) : null }
300- </ h3 >
298+ < div >
299+ < span className = { styles . token } > { token ?. symbol } </ span >
300+
301+ < span className = { styles . amount } > { token ? formatTokenAmount ( estimatedTotalCost , token . address ) : null } </ span >
302+ </ div >
301303 ) ;
304+ } ;
305+
306+ return (
307+ < Card
308+ className = { styles . costCard }
309+ direction = "column"
310+ innerShadow = "black"
311+ paddingX = "md"
312+ paddingY = "sm"
313+ radius = "md"
314+ spacing = "sm"
315+ variant = "glass"
316+ >
317+ < div className = { styles . costEstimation } >
318+ < h3 > Estimated total cost</ h3 >
319+ { renderCostEstimation ( ) }
320+ </ div >
321+ < div className = "alignSelfEnd textSuccessDarker" >
322+ If your job finishes earlier than estimated, the unconsumed tokens remain in your escrow
323+ </ div >
324+ </ Card >
325+ ) ;
326+ } ;
327+
328+ const renderConnectionErrorCard = ( ) => {
329+ if ( ! initComputeError ) {
330+ return null ;
302331 }
303- if ( ! p2pReady || ( ! estimatedTotalCost && estimatedTotalCost !== 0 ) ) {
304- return (
305- < h3 className = { styles . estimationMessage } >
306- < CircularProgress size = { 24 } />
307- Connecting to node...
308- </ h3 >
309- ) ;
332+ let errorText ;
333+ if ( initComputeError instanceof Error ) {
334+ errorText = initComputeError . message ;
310335 }
311336 return (
312- < div >
313- < span className = { styles . token } > { token ?. symbol } </ span >
314-
315- < span className = { styles . amount } > { formatTokenAmount ( estimatedTotalCost , token . address ) } </ span >
316- </ div >
337+ < Card direction = "column" paddingX = "md" paddingY = "sm" radius = "md" spacing = "sm" variant = "error" >
338+ < h3 > Could not reach this node</ h3 >
339+ { errorText ? < p > { errorText } </ p > : null }
340+ < p >
341+ This may be due to missing WSS, TLS, or P2P circuit relay configuration on the node.
342+ < br />
343+ If you are the node operator, please check your node's network setup.
344+ </ p >
345+ < Button
346+ className = "alignSelfStart"
347+ color = "accent2"
348+ href = { config . links . docs }
349+ size = "sm"
350+ target = "_blank"
351+ variant = "filled"
352+ >
353+ Visit docs
354+ </ Button >
355+ </ Card >
317356 ) ;
318357 } ;
319358
@@ -439,21 +478,12 @@ const SelectResources = ({ environment, freeCompute, token }: SelectResourcesPro
439478 value = { formik . values . maxJobDurationValue }
440479 />
441480 </ div >
442- < TransitionGroup >
443- { formik . isValid && ! freeCompute ? (
444- < Collapse >
445- < Card className = { styles . costCard } innerShadow = "black" radius = "md" variant = "glass" >
446- < div className = { styles . costEstimation } >
447- < h3 > Estimated total cost</ h3 >
448- { renderCostEstimation ( ) }
449- </ div >
450- < div className = "alignSelfEnd textSuccessDarker" >
451- If your job finishes earlier than estimated, the unconsumed tokens remain in your escrow
452- </ div >
453- </ Card >
454- </ Collapse >
455- ) : null }
456- </ TransitionGroup >
481+ { freeCompute ? null : (
482+ < TransitionGroup >
483+ { initComputeError ? < Collapse > { renderConnectionErrorCard ( ) } </ Collapse > : null }
484+ { ! initComputeError && formik . isValid ? < Collapse > { renderCostCard ( ) } </ Collapse > : null }
485+ </ TransitionGroup >
486+ ) }
457487 < div className = "actionsGroupLgBetween" >
458488 < Button
459489 color = "accent1"
0 commit comments