11"use client" ;
2-
32import { useContext , useState } from "react" ;
43import * as s from "./style" ;
54import Navbar from "../../Components/Navbar" ;
@@ -172,11 +171,161 @@ const logos = imageNames.reduce((acc, name) => {
172171 return acc ;
173172} , { } ) ;
174173
175- // 드롭다운 서버 카드 컴포넌트
174+ const calculateServerStats = ( ) => {
175+ const totalServers = serverData . length ;
176+
177+ const memoryStats = {
178+ DDR3 : 0 ,
179+ DDR4 : 0 ,
180+ DDR5 : 0 ,
181+ LPDDR4X : 0 ,
182+ total : 0 ,
183+ } ;
184+
185+ const storageStats = {
186+ HDD : 0 ,
187+ "SATA SSD" : 0 ,
188+ "NVMe SSD" : 0 ,
189+ total : 0 ,
190+ } ;
191+
192+ serverData . forEach ( ( server ) => {
193+ const memoryMatch = server . memory . match ( / ( \d + ) G B / ) ;
194+ const memorySize = memoryMatch ? Number . parseInt ( memoryMatch [ 1 ] ) : 0 ;
195+
196+ if ( server . memory . includes ( "DDR5" ) ) {
197+ memoryStats . DDR5 += memorySize ;
198+ } else if ( server . memory . includes ( "DDR4" ) ) {
199+ memoryStats . DDR4 += memorySize ;
200+ } else if ( server . memory . includes ( "DDR3" ) ) {
201+ memoryStats . DDR3 += memorySize ;
202+ } else if ( server . memory . includes ( "LPDDR4X" ) ) {
203+ memoryStats . LPDDR4X += memorySize ;
204+ }
205+ memoryStats . total += memorySize ;
206+
207+ server . storage . forEach ( ( storageItem ) => {
208+ const capacityMatch = storageItem . match ( / ( \d + (?: \. \d + ) ? ) ( G B | T B ) / i) ;
209+ const multiplierMatch = storageItem . match ( / x \s * ( \d + ) / i) ;
210+
211+ if ( capacityMatch ) {
212+ const capacity = Number . parseFloat ( capacityMatch [ 1 ] ) ;
213+ const unit = capacityMatch [ 2 ] . toUpperCase ( ) ;
214+ const multiplier = multiplierMatch
215+ ? Number . parseInt ( multiplierMatch [ 1 ] )
216+ : 1 ;
217+
218+ let capacityInGB = capacity ;
219+ if ( unit === "TB" ) {
220+ capacityInGB = capacity * 1024 ;
221+ }
222+
223+ const totalCapacity = capacityInGB * multiplier ;
224+
225+ if ( storageItem . includes ( "HDD" ) ) {
226+ storageStats . HDD += totalCapacity ;
227+ } else if ( storageItem . includes ( "NVMe" ) ) {
228+ storageStats [ "NVMe SSD" ] += totalCapacity ;
229+ } else if ( storageItem . includes ( "SSD" ) ) {
230+ storageStats [ "SATA SSD" ] += totalCapacity ;
231+ }
232+
233+ storageStats . total += totalCapacity ;
234+ }
235+ } ) ;
236+ } ) ;
237+
238+ return { totalServers, memoryStats, storageStats } ;
239+ } ;
240+
241+ const formatCapacity = ( capacityGB ) => {
242+ if ( capacityGB >= 1024 ) {
243+ const capacityTB = capacityGB / 1024 ;
244+ return capacityTB % 1 === 0
245+ ? `${ capacityTB } TB`
246+ : `${ capacityTB . toFixed ( 1 ) } TB` ;
247+ }
248+ return capacityGB % 1 === 0
249+ ? `${ capacityGB } GB`
250+ : `${ capacityGB . toFixed ( 1 ) } GB` ;
251+ } ;
252+
253+ const ServerStatsOverview = ( { isKorean } ) => {
254+ const { totalServers, memoryStats, storageStats } = calculateServerStats ( ) ;
255+
256+ return (
257+ < s . StatsContainer >
258+ < s . StatsGrid >
259+ < s . StatCard >
260+ < s . StatContent >
261+ < s . StatLabel >
262+ { isKorean ? "총 서버 대수" : "Total Servers" }
263+ </ s . StatLabel >
264+ < s . StatValue > { totalServers } </ s . StatValue >
265+ < s . StatUnit > { isKorean ? "대" : "units" } </ s . StatUnit >
266+ </ s . StatContent >
267+ </ s . StatCard >
268+
269+ < s . StatCard >
270+ < s . StatContent >
271+ < s . StatLabel >
272+ { isKorean ? "총 메모리" : "Total Memory" }
273+ </ s . StatLabel >
274+ < s . StatValue > { memoryStats . total } GB</ s . StatValue >
275+ < s . StatBreakdown >
276+ { memoryStats . DDR5 > 0 && (
277+ < span > DDR5: { memoryStats . DDR5 } GB</ span >
278+ ) }
279+ { memoryStats . DDR4 > 0 && (
280+ < span > DDR4: { memoryStats . DDR4 } GB</ span >
281+ ) }
282+ { memoryStats . DDR3 > 0 && (
283+ < span > DDR3: { memoryStats . DDR3 } GB</ span >
284+ ) }
285+ { memoryStats . LPDDR4X > 0 && (
286+ < span > LPDDR4X: { memoryStats . LPDDR4X } GB</ span >
287+ ) }
288+ </ s . StatBreakdown >
289+ </ s . StatContent >
290+ </ s . StatCard >
291+
292+ < s . StatCard >
293+ < s . StatContent >
294+ < s . StatLabel >
295+ { isKorean ? "총 스토리지" : "Total Storage" }
296+ </ s . StatLabel >
297+ < s . StatValue >
298+ { formatCapacity ( storageStats . total ) }
299+ </ s . StatValue >
300+ < s . StatBreakdown >
301+ { storageStats . HDD > 0 && (
302+ < span >
303+ HDD: { formatCapacity ( storageStats . HDD ) }
304+ </ span >
305+ ) }
306+ { storageStats [ "NVMe SSD" ] > 0 && (
307+ < span >
308+ NVMe:{ " " }
309+ { formatCapacity ( storageStats [ "NVMe SSD" ] ) }
310+ </ span >
311+ ) }
312+ { storageStats [ "SATA SSD" ] > 0 && (
313+ < span >
314+ SATA SSD:{ " " }
315+ { formatCapacity ( storageStats [ "SATA SSD" ] ) }
316+ </ span >
317+ ) }
318+ </ s . StatBreakdown >
319+ </ s . StatContent >
320+ </ s . StatCard >
321+ </ s . StatsGrid >
322+ </ s . StatsContainer >
323+ ) ;
324+ } ;
325+
176326const DropdownServerCard = ( { server, index, isKorean } ) => {
177327 const [ isOpen , setIsOpen ] = useState ( false ) ;
178328
179- // 간략한 정보 추출
180329 const getServerType = ( name ) => {
181330 if ( name . includes ( "Hypervisor" ) ) return "Hypervisor" ;
182331 if ( name . includes ( "Truenas" ) ) return "Storage" ;
@@ -202,33 +351,23 @@ const DropdownServerCard = ({ server, index, isKorean }) => {
202351 if ( cpu . includes ( "Intel Celeron J4125" ) ) return "Intel Celeron J4125" ;
203352 if ( cpu . includes ( "Apple Silicon M1" ) ) return "Apple Silicon M1" ;
204353 if ( cpu . includes ( "Intel Celeron N2930" ) ) return "Intel Celeron N2930" ;
205- return cpu . split ( " @" ) [ 0 ] ; // Fallback to CPU name without frequency
354+ return cpu . split ( " @" ) [ 0 ] ;
206355 } ;
207356
208357 const getMemorySize = ( memory ) => {
209358 const sizeMatch = memory . match ( / ( \d + ) G B / ) ;
210359 const ddrMatch = memory . match ( / ( D D R \d + L ? | L P D D R \d + X ? ) / i) ;
211-
212360 const size = sizeMatch ? `${ sizeMatch [ 1 ] } GB` : "RAM" ;
213361 const ddrType = ddrMatch ? ddrMatch [ 1 ] : "" ;
214-
215362 return ddrType ? `${ ddrType } ${ size } ` : size ;
216363 } ;
217364
218365 const getTotalStorage = ( storageArray ) => {
219- if ( ! storageArray || storageArray . length === 0 ) {
220- return "0GB" ;
221- }
366+ if ( ! storageArray || storageArray . length === 0 ) return "0GB" ;
222367
223368 let totalGB = 0 ;
224-
225369 storageArray . forEach ( ( storageItem ) => {
226- // Extract capacity and multiplier from storage string
227- // Examples: "Gen4 NVMe 512GB x 2", "SATA HDD 4TB x 2", "16TB SATA HDD"
228-
229- // Match patterns like "512GB", "4TB", etc.
230370 const capacityMatch = storageItem . match ( / ( \d + (?: \. \d + ) ? ) ( G B | T B ) / i) ;
231- // Match multiplier patterns like "x 2", "x 10", etc.
232371 const multiplierMatch = storageItem . match ( / x \s * ( \d + ) / i) ;
233372
234373 if ( capacityMatch ) {
@@ -238,33 +377,24 @@ const DropdownServerCard = ({ server, index, isKorean }) => {
238377 ? Number . parseInt ( multiplierMatch [ 1 ] )
239378 : 1 ;
240379
241- // Convert to GB
242380 let capacityInGB = capacity ;
243381 if ( unit === "TB" ) {
244- capacityInGB = capacity * 1024 ; // Convert TB to GB
382+ capacityInGB = capacity * 1024 ;
245383 }
246384
247- // Apply multiplier
248385 totalGB += capacityInGB * multiplier ;
249386 }
250387 } ) ;
251388
252- // Format the result
253389 if ( totalGB >= 1024 ) {
254390 const totalTB = totalGB / 1024 ;
255- // If it's a whole number, don't show decimals
256- if ( totalTB % 1 === 0 ) {
257- return `${ totalTB } TB` ;
258- } else {
259- return `${ totalTB . toFixed ( 1 ) } TB` ;
260- }
391+ return totalTB % 1 === 0
392+ ? `${ totalTB } TB`
393+ : `${ totalTB . toFixed ( 1 ) } TB` ;
261394 } else {
262- // If it's a whole number, don't show decimals
263- if ( totalGB % 1 === 0 ) {
264- return `${ totalGB } GB` ;
265- } else {
266- return `${ totalGB . toFixed ( 1 ) } GB` ;
267- }
395+ return totalGB % 1 === 0
396+ ? `${ totalGB } GB`
397+ : `${ totalGB . toFixed ( 1 ) } GB` ;
268398 }
269399 } ;
270400
@@ -319,7 +449,6 @@ const DropdownServerCard = ({ server, index, isKorean }) => {
319449 </ svg >
320450 </ s . DropdownIcon >
321451 </ s . ServerCardHeader >
322-
323452 < s . ServerCardContent isOpen = { isOpen } >
324453 < s . ServerCardGrid >
325454 < s . ServerCardColumn >
@@ -383,7 +512,6 @@ const StatusPage = () => {
383512 zzuniHomelab의 리소스를 확인하세요.
384513 </ s . MainContainerSubTitle >
385514 </ s . MainContainer >
386-
387515 < s . SubContainer1 >
388516 < s . SubContainer1Items >
389517 { hardwareItemsKR . map ( ( item , index ) => (
@@ -393,8 +521,8 @@ const StatusPage = () => {
393521 ) ) }
394522 </ s . SubContainer1Items >
395523 </ s . SubContainer1 >
396-
397524 < s . SubContainer2 >
525+ < ServerStatsOverview isKorean = { true } />
398526 < s . ServerSection >
399527 { serverData . map ( ( server , index ) => (
400528 < DropdownServerCard
@@ -406,7 +534,6 @@ const StatusPage = () => {
406534 ) ) }
407535 </ s . ServerSection >
408536 </ s . SubContainer2 >
409-
410537 < s . SubContainer3 >
411538 < s . SliderContainer >
412539 < s . SliderTrack >
@@ -417,7 +544,6 @@ const StatusPage = () => {
417544 < img
418545 src = {
419546 logos [ key ] ||
420- "/placeholder.svg" ||
421547 "/placeholder.svg"
422548 }
423549 alt = { key }
@@ -431,7 +557,6 @@ const StatusPage = () => {
431557 < img
432558 src = {
433559 logos [ key ] ||
434- "/placeholder.svg" ||
435560 "/placeholder.svg"
436561 }
437562 alt = { key }
@@ -452,7 +577,6 @@ const StatusPage = () => {
452577 Check out zzuniHomelab's computing resources.
453578 </ s . MainContainerSubTitle >
454579 </ s . MainContainer >
455-
456580 < s . SubContainer1 >
457581 < s . SubContainer1Items >
458582 { hardwareItemsEN . map ( ( item , index ) => (
@@ -462,8 +586,8 @@ const StatusPage = () => {
462586 ) ) }
463587 </ s . SubContainer1Items >
464588 </ s . SubContainer1 >
465-
466589 < s . SubContainer2 >
590+ < ServerStatsOverview isKorean = { false } />
467591 < s . ServerSection >
468592 { serverData . map ( ( server , index ) => (
469593 < DropdownServerCard
@@ -475,7 +599,6 @@ const StatusPage = () => {
475599 ) ) }
476600 </ s . ServerSection >
477601 </ s . SubContainer2 >
478-
479602 < s . SubContainer3 >
480603 < s . SliderContainer >
481604 < s . SliderTrack >
@@ -486,7 +609,6 @@ const StatusPage = () => {
486609 < img
487610 src = {
488611 logos [ key ] ||
489- "/placeholder.svg" ||
490612 "/placeholder.svg"
491613 }
492614 alt = { key }
@@ -500,7 +622,6 @@ const StatusPage = () => {
500622 < img
501623 src = {
502624 logos [ key ] ||
503- "/placeholder.svg" ||
504625 "/placeholder.svg"
505626 }
506627 alt = { key }
0 commit comments