package app import ( "fmt" "github.com/seaweedfs/seaweedfs/weed/admin/dash" ) templ EcVolumeDetails(data dash.EcVolumeDetailsData) {

EC Volume Details

Volume Information
if !data.IsComplete { } if len(data.Generations) > 1 { }
Volume ID: {fmt.Sprintf("%d", data.VolumeID)}
Collection: if data.Collection != "" { {data.Collection} } else { default }
Status: if data.IsComplete { Complete ({data.TotalShards}/14 shards) } else { Incomplete ({data.TotalShards}/14 shards) }
Missing Shards: for i, shardID := range data.MissingShards { if i > 0 { , } {fmt.Sprintf("%02d", shardID)} }
Data Centers: for i, dc := range data.DataCenters { if i > 0 { , } {dc} }
Servers: {fmt.Sprintf("%d servers", len(data.Servers))}
Active Generation: if data.ActiveGeneration > 0 { {fmt.Sprintf("G%d", data.ActiveGeneration)} } else { G0 } if len(data.Generations) > 1 { Multi-Gen }
All Generations: for i, gen := range data.Generations { if i > 0 { } if gen == data.ActiveGeneration { G{fmt.Sprintf("%d", gen)} (Active) } else { G{fmt.Sprintf("%d", gen)} } }
Last Updated: {data.LastUpdated.Format("2006-01-02 15:04:05")}
Volume Health
if data.TotalSize > 0 || data.DeletedByteCount > 0 || data.DeleteCount > 0 {
if data.TotalSize > 0 {
{bytesToHumanReadableUint64(func() uint64 { if data.DeletedByteCount > data.TotalSize { return 0 } return data.TotalSize - data.DeletedByteCount }())}
Active Bytes
{bytesToHumanReadableUint64(data.DeletedByteCount)}
Deleted Bytes
} else {
{fmt.Sprintf("%d", data.DeleteCount)}
Deleted Needles (EC-only volume) if data.DeleteCount > 0 {
Deletion info from .ecj files
}
}
if data.TotalSize > 0 {
{fmt.Sprintf("%d", func() uint64 { if data.DeleteCount > data.FileCount { return 0 } return data.FileCount - data.DeleteCount }())}
Active Files
{fmt.Sprintf("%d", data.DeleteCount)}
Deleted Files
{fmt.Sprintf("%.1f%%", data.GarbageRatio * 100)}
Garbage Ratio if data.GarbageRatio >= 0.3 {
EC Vacuum Candidate
}
} else if data.DeleteCount > 0 {
EC Vacuum Eligible
Volume has {fmt.Sprintf("%d", data.DeleteCount)} deleted needles
} } else {
Volume health metrics not available
This may be normal for newly created EC volumes
}
Shard Distribution

{fmt.Sprintf("%d", data.TotalShards)}

Total Shards

{fmt.Sprintf("%d", len(data.DataCenters))}

Data Centers

{fmt.Sprintf("%d", len(data.Servers))}

Servers
if len(data.Generations) > 1 { for _, gen := range data.Generations {
if gen == data.ActiveGeneration { G{fmt.Sprintf("%d", gen)} (Active) } else { G{fmt.Sprintf("%d", gen)} } {fmt.Sprintf("%d/14 shards", len(data.GenerationShards[gen]))} if data.GenerationComplete[gen] { } else { }
for _, shardID := range data.GenerationShards[gen] { if gen == data.ActiveGeneration { {fmt.Sprintf("%02d", shardID)} } else { {fmt.Sprintf("%02d", shardID)} } }
} } else {
Present Shards:
for _, shard := range data.Shards { {fmt.Sprintf("%02d", shard.ShardID)} }
if len(data.MissingShards) > 0 {
Missing Shards:
for _, shardID := range data.MissingShards { {fmt.Sprintf("%02d", shardID)} }
} }
Shard Details
if len(data.Shards) > 0 {
for _, shard := range data.Shards { }
Shard ID if data.SortBy == "shard_id" { if data.SortOrder == "asc" { } else { } } else { } Server if data.SortBy == "server" { if data.SortOrder == "asc" { } else { } } else { } Data Center if data.SortBy == "data_center" { if data.SortOrder == "asc" { } else { } } else { } Rack if data.SortBy == "rack" { if data.SortOrder == "asc" { } else { } } else { } Generation Disk Type Shard Size Actions
{fmt.Sprintf("%02d", shard.ShardID)} {shard.Server} {shard.DataCenter} {shard.Rack} if shard.Generation == data.ActiveGeneration { G{fmt.Sprintf("%d", shard.Generation)} } else { G{fmt.Sprintf("%d", shard.Generation)} } {shard.DiskType} {bytesToHumanReadableUint64(shard.Size)} Volume Server
} else {
No EC shards found

This volume may not be EC encoded yet.

}
} // Helper function to convert bytes to human readable format (uint64 version) func bytesToHumanReadableUint64(bytes uint64) string { const unit = 1024 if bytes < unit { return fmt.Sprintf("%dB", bytes) } div, exp := uint64(unit), 0 for n := bytes / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.1f%cB", float64(bytes)/float64(div), "KMGTPE"[exp]) } // Helper function to get color for garbage ratio display func getGarbageRatioColor(ratio float64) string { if ratio >= 0.5 { return "#dc3545" // Red for high garbage ratio } else if ratio >= 0.3 { return "#fd7e14" // Orange for medium garbage ratio } else if ratio >= 0.1 { return "#ffc107" // Yellow for low garbage ratio } return "#28a745" // Green for very low garbage ratio }