Skip to content

Commit f641193

Browse files
committed
feat: Added import and export. Extended also the test
1 parent 8ccb28e commit f641193

File tree

4 files changed

+368
-157
lines changed

4 files changed

+368
-157
lines changed

docs/.vitepress/theme/components/price-estimator/PriceEstimator.vue

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,47 +22,18 @@ function triggerFileUpload() {
2222
fileInput.value?.click()
2323
}
2424
25-
// TODO: Fix this later
2625
async function handleFileUpload(event: Event) {
27-
console.log("This does not work for now")
28-
// const target = event.target as HTMLInputElement
29-
// const file = target.files?.[0]
30-
// if (!file) return
31-
32-
// const reader = new FileReader()
33-
34-
// reader.onload = async e => {
35-
// try {
36-
// priceEstimatorStore.updateTotalSummary()
37-
38-
// const data = JSON.parse(e.target?.result as string)
39-
// if (!data.version || !Array.isArray(data.labs)) {
40-
// throw new Error("Invalid JSON format")
41-
// }
42-
43-
// priceEstimatorStore.isInitializingPriseEstimator = true
44-
// priceEstimatorStore.clearAllLabs()
45-
46-
// data.labs.forEach((lab: any) => {
47-
// priceEstimatorStore.labs.push({
48-
// id: lab.id,
49-
// title: lab.name,
50-
// priceComputeYearly: 0,
51-
// selectedCompute: lab.compute || [],
52-
// selectedStorage: lab.storage || [],
53-
// })
54-
// })
55-
56-
// await nextTick()
57-
// priceEstimatorStore.isInitializingPriseEstimator = false
58-
// target.value = ""
59-
// } catch (err) {
60-
// console.error(err)
61-
// alert("Error importing file.")
62-
// }
63-
// }
64-
65-
// reader.readAsText(file)
26+
const target = event.target as HTMLInputElement
27+
const file = target.files?.[0]
28+
if (!file) return
29+
30+
try {
31+
await priceEstimatorStore.importLabs(file)
32+
target.value = ""
33+
} catch (err) {
34+
console.error(err)
35+
alert(err)
36+
}
6637
}
6738
</script>
6839

docs/.vitepress/theme/components/price-estimator/TotalBlock.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,17 @@ const tableItems = computed(() => {
9292
9393
<template>
9494
<v-card class="ma-4 pa-4">
95-
<v-card-title>Total Summary</v-card-title>
95+
<v-row align="center" justify="space-between">
96+
<v-col>
97+
<v-card-title>Total Summary</v-card-title>
98+
</v-col>
99+
<v-col cols="auto">
100+
<v-btn density="default" size="large" dark @click="priceEstimatorStore.exportItems()">
101+
<v-icon left>mdi-export</v-icon>
102+
Export
103+
</v-btn>
104+
</v-col>
105+
</v-row>
96106
97107
<v-data-table :headers="headers" :items="tableItems" hide-default-footer item-value="id" class="mt-2">
98108
<template #item.name="{ item }">

docs/.vitepress/theme/components/price-estimator/stores/priceEstimatorStore.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,148 @@ export const priceEstimatorStore = reactive({
537537
const storageSum = this.calculateTotalStorageCost()
538538
console.log("SUMMARY", storageSum)
539539
summary.allStorage = storageSum
540+
console.log("FINAL SUMMARY", summary)
540541
return summary
541542
},
543+
544+
exportItems() {
545+
const labsToExport = this.labs.map(lab => ({
546+
id: lab.id,
547+
name: lab.title,
548+
compute: lab.selectedCompute.map(c => ({
549+
id: c.id,
550+
name: c.name,
551+
flavor: c.flavor,
552+
core_count: c.core_count,
553+
ram: c.ram,
554+
gpu: c.gpu,
555+
type: c.type,
556+
})),
557+
storage: lab.selectedStorage.map(s => ({
558+
id: s.id,
559+
name: s.name,
560+
usage: s.usage,
561+
type: s.type,
562+
size: s.size,
563+
})),
564+
}))
565+
566+
const exportData = {
567+
version: "1.0",
568+
labs: labsToExport,
569+
}
570+
571+
const jsonString = JSON.stringify(exportData, null, 2)
572+
const blob = new Blob([jsonString], { type: "application/json" })
573+
const url = URL.createObjectURL(blob)
574+
const a = document.createElement("a")
575+
a.href = url
576+
a.download = "hunt-cloud-estimate.json"
577+
document.body.appendChild(a)
578+
a.click()
579+
document.body.removeChild(a)
580+
URL.revokeObjectURL(url)
581+
},
582+
583+
async importLabs(file: File) {
584+
return new Promise<void>((resolve, reject) => {
585+
const reader = new FileReader()
586+
587+
reader.onload = e => {
588+
try {
589+
const data = JSON.parse(e.target?.result as string)
590+
591+
// Validation
592+
if (!data.version) throw new Error("Invalid JSON format: Missing version")
593+
if (!Array.isArray(data.labs)) throw new Error("Invalid JSON format: 'labs' must be an array")
594+
595+
data.labs.forEach((lab: any, labIndex: number) => {
596+
if (!lab.name) throw new Error(`Invalid lab at index ${labIndex}: Missing 'name'`)
597+
598+
if (lab.compute) {
599+
if (!Array.isArray(lab.compute)) throw new Error(`Invalid lab '${lab.name}': 'compute' must be an array`)
600+
lab.compute.forEach((comp: any, compIndex: number) => {
601+
const required = ["name", "flavor", "core_count", "ram", "type"]
602+
const missing = required.filter(
603+
field => comp[field] === undefined || comp[field] === null || comp[field] === "",
604+
)
605+
if (missing.length > 0) {
606+
throw new Error(
607+
`Invalid compute item at index ${compIndex} in lab '${lab.name}': Missing fields: ${missing.join(", ")}`,
608+
)
609+
}
610+
})
611+
}
612+
613+
if (lab.storage) {
614+
if (!Array.isArray(lab.storage)) throw new Error(`Invalid lab '${lab.name}': 'storage' must be an array`)
615+
lab.storage.forEach((store: any, storeIndex: number) => {
616+
const required = ["name", "usage", "type", "size"]
617+
const missing = required.filter(
618+
field => store[field] === undefined || store[field] === null || store[field] === "",
619+
)
620+
if (missing.length > 0) {
621+
throw new Error(
622+
`Invalid storage item at index ${storeIndex} in lab '${lab.name}': Missing fields: ${missing.join(", ")}`,
623+
)
624+
}
625+
})
626+
}
627+
})
628+
629+
this.isInitializingPriseEstimator = true
630+
this.clearAllLabs()
631+
632+
for (const labData of data.labs) {
633+
const newLabId = this.labs.length
634+
635+
this.labs.push({
636+
id: newLabId,
637+
title: labData.name,
638+
selectedCompute: [],
639+
selectedStorage: [],
640+
})
641+
642+
if (labData.compute) {
643+
for (const comp of labData.compute) {
644+
this.addComputeToLab(newLabId, {
645+
name: comp.name,
646+
flavor: comp.flavor,
647+
core_count: comp.core_count,
648+
ram: comp.ram,
649+
type: comp.type,
650+
gpu: comp.gpu,
651+
})
652+
}
653+
}
654+
655+
if (labData.storage) {
656+
for (const store of labData.storage) {
657+
this.addStorageToLab(newLabId, {
658+
name: store.name,
659+
usage: store.usage,
660+
type: store.type,
661+
size: store.size,
662+
})
663+
}
664+
}
665+
}
666+
667+
this.updateTotalSummary()
668+
this.isInitializingPriseEstimator = false
669+
resolve()
670+
} catch (err) {
671+
this.isInitializingPriseEstimator = false
672+
reject(err)
673+
}
674+
}
675+
676+
reader.onerror = () => {
677+
this.isInitializingPriseEstimator = false
678+
reject(new Error("Failed to read file"))
679+
}
680+
681+
reader.readAsText(file)
682+
})
683+
},
542684
})

0 commit comments

Comments
 (0)