77 "github.com/chasefleming/elem-go"
88 "github.com/chasefleming/elem-go/attrs"
99 "github.com/microcosm-cc/bluemonday"
10- "github.com/mudler/LocalAI/core/p2p "
10+ "github.com/mudler/LocalAI/core/schema "
1111)
1212
1313func renderElements (n []elem.Node ) string {
@@ -18,23 +18,25 @@ func renderElements(n []elem.Node) string {
1818 return render
1919}
2020
21- func P2PNodeStats (nodes []p2p .NodeData ) string {
21+ func P2PNodeStats (nodes []schema .NodeData ) string {
2222 online := 0
2323 for _ , n := range nodes {
2424 if n .IsOnline () {
2525 online ++
2626 }
2727 }
2828
29- class := "text-blue -400"
29+ class := "text-green -400"
3030 if online == 0 {
3131 class = "text-red-400"
32+ } else if online < len (nodes ) {
33+ class = "text-yellow-400"
3234 }
3335
3436 nodesElements := []elem.Node {
3537 elem .Span (
3638 attrs.Props {
37- "class" : class + " font-bold text-xl " ,
39+ "class" : class + " font-bold text-2xl " ,
3840 },
3941 elem .Text (fmt .Sprintf ("%d" , online )),
4042 ),
@@ -49,77 +51,106 @@ func P2PNodeStats(nodes []p2p.NodeData) string {
4951 return renderElements (nodesElements )
5052}
5153
52- func P2PNodeBoxes (nodes []p2p.NodeData ) string {
53- nodesElements := []elem.Node {}
54+ func P2PNodeBoxes (nodes []schema.NodeData ) string {
55+ if len (nodes ) == 0 {
56+ return `<div class="col-span-full flex flex-col items-center justify-center py-12 text-center bg-gray-800/50 border border-gray-700/50 rounded-xl">
57+ <i class="fas fa-server text-gray-500 text-4xl mb-4"></i>
58+ <p class="text-gray-400 text-lg font-medium">No nodes available</p>
59+ <p class="text-gray-500 text-sm mt-2">Start some workers to see them here</p>
60+ </div>`
61+ }
5462
63+ render := ""
5564 for _ , n := range nodes {
5665 nodeID := bluemonday .StrictPolicy ().Sanitize (n .ID )
5766
5867 // Define status-specific classes
5968 statusIconClass := "text-green-400"
6069 statusText := "Online"
6170 statusTextClass := "text-green-400"
71+ cardHoverClass := "hover:shadow-green-500/20 hover:border-green-400/50"
6272
6373 if ! n .IsOnline () {
6474 statusIconClass = "text-red-400"
6575 statusText = "Offline"
6676 statusTextClass = "text-red-400"
77+ cardHoverClass = "hover:shadow-red-500/20 hover:border-red-400/50"
6778 }
6879
69- nodesElements = append (nodesElements ,
80+ nodeCard := elem .Div (
81+ attrs.Props {
82+ "class" : "bg-gradient-to-br from-gray-800/90 to-gray-900/80 border border-gray-700/50 rounded-xl p-5 shadow-xl transition-all duration-300 " + cardHoverClass + " backdrop-blur-sm" ,
83+ },
84+ // Header with node icon and status
7085 elem .Div (
7186 attrs.Props {
72- "class" : "bg-gray-800/80 border border-gray-700/50 rounded-xl p-4 shadow-lg transition-all duration-300 hover:shadow-blue-900/20 hover:border-blue-700/50 " ,
87+ "class" : "flex items-center justify-between mb-4 " ,
7388 },
74- // Node ID and status indicator in top row
89+ // Node info
7590 elem .Div (
7691 attrs.Props {
77- "class" : "flex items-center justify-between mb-3 " ,
92+ "class" : "flex items-center" ,
7893 },
79- // Node ID with icon
8094 elem .Div (
8195 attrs.Props {
82- "class" : "flex items-center" ,
96+ "class" : "w-10 h-10 bg-blue-500/20 rounded-lg flex items-center justify-center mr-3 " ,
8397 },
8498 elem .I (
8599 attrs.Props {
86- "class" : "fas fa-server text-blue-400 mr-2" ,
87- },
88- ),
89- elem .Span (
90- attrs.Props {
91- "class" : "text-white font-medium" ,
100+ "class" : "fas fa-server text-blue-400 text-lg" ,
92101 },
93- elem .Text (nodeID ),
94102 ),
95103 ),
96- // Status indicator
97104 elem .Div (
98- attrs.Props {
99- "class" : "flex items-center" ,
100- },
101- elem .I (
105+ attrs.Props {},
106+ elem .H4 (
102107 attrs.Props {
103- "class" : "fas fa-circle animate-pulse " + statusIconClass + " mr-1.5 " ,
108+ "class" : "text-white font-semibold text-sm " ,
104109 },
110+ elem .Text ("Node" ),
105111 ),
106- elem .Span (
112+ elem .P (
107113 attrs.Props {
108- "class" : statusTextClass ,
114+ "class" : "text-gray-400 text-xs font-mono break-all" ,
109115 },
110- elem .Text (statusText ),
116+ elem .Text (nodeID ),
111117 ),
112118 ),
113119 ),
114- // Bottom section with timestamp
120+ // Status badge
115121 elem .Div (
116122 attrs.Props {
117- "class" : "text-xs text -gray-400 pt-1 border-t border-gray-700/30 " ,
123+ "class" : "flex items-center bg -gray-900/50 rounded-full px-3 py-1.5 border border-gray-700/50 " ,
118124 },
119- elem .Text ("Last updated: " + time .Now ().UTC ().Format ("2006-01-02 15:04:05" )),
125+ elem .I (
126+ attrs.Props {
127+ "class" : "fas fa-circle animate-pulse " + statusIconClass + " mr-2 text-xs" ,
128+ },
129+ ),
130+ elem .Span (
131+ attrs.Props {
132+ "class" : statusTextClass + " text-xs font-medium" ,
133+ },
134+ elem .Text (statusText ),
135+ ),
120136 ),
121- ))
137+ ),
138+ // Footer with timestamp
139+ elem .Div (
140+ attrs.Props {
141+ "class" : "text-xs text-gray-500 pt-3 border-t border-gray-700/30 flex items-center" ,
142+ },
143+ elem .I (
144+ attrs.Props {
145+ "class" : "fas fa-clock mr-2" ,
146+ },
147+ ),
148+ elem .Text ("Updated: " + time .Now ().UTC ().Format ("15:04:05" )),
149+ ),
150+ )
151+
152+ render += nodeCard .Render ()
122153 }
123154
124- return renderElements ( nodesElements )
155+ return render
125156}
0 commit comments