Skip to content

Commit 0cf8835

Browse files
authored
Merge pull request mouredev#2327 from hozlucas28/Solution-11-TypeScript
#11 - TypeScript
2 parents cfefe8c + 5c9938c commit 0cf8835

File tree

1 file changed

+327
-0
lines changed

1 file changed

+327
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
import * as fs from 'node:fs'
2+
import * as readline from 'readline-sync'
3+
4+
/*
5+
Files...
6+
*/
7+
8+
console.log('Files...')
9+
10+
const FILE_PATH: string = 'hozlucas28.txt'
11+
12+
console.log(`\n// Delete file if exist
13+
if (fs.existsSync(FILE_PATH)) fs.rmSync(FILE_PATH)
14+
15+
// Create file with content
16+
const fileContent: string = ['Lucas Hoz', '22', 'TypeScript'].join('\\n')
17+
fs.writeFileSync(FILE_PATH, fileContent, { encoding: 'utf-8' })
18+
19+
// Print file content
20+
const data: string = fs.readFileSync(FILE_PATH, { encoding: 'utf-8' })
21+
console.log(data)
22+
23+
// Delete file
24+
fs.rmSync(FILE_PATH)\n`)
25+
26+
// Delete file if exist
27+
if (fs.existsSync(FILE_PATH)) fs.rmSync(FILE_PATH)
28+
29+
// Create file with content
30+
const fileContent: string = ['Lucas Hoz', '22', 'TypeScript'].join('\n')
31+
fs.writeFileSync(FILE_PATH, fileContent, { encoding: 'utf-8' })
32+
33+
// Print file content
34+
const data: string = fs.readFileSync(FILE_PATH, { encoding: 'utf-8' })
35+
console.log(data)
36+
37+
// Delete file
38+
fs.rmSync(FILE_PATH)
39+
40+
console.log(
41+
'\n# ---------------------------------------------------------------------------------- #\n'
42+
)
43+
44+
/*
45+
Additional challenge...
46+
*/
47+
48+
type Operation = Uppercase<
49+
| 'add'
50+
| 'delete'
51+
| 'print'
52+
| 'total sales by product'
53+
| 'total sales'
54+
| 'update'
55+
| 'exit'
56+
>
57+
58+
type Product = {
59+
amount: number
60+
name: string
61+
price: number
62+
total: number
63+
}
64+
65+
type Constructor = {
66+
filePath: string
67+
products?: Omit<Product, 'total'>[]
68+
}
69+
70+
class SalesFile {
71+
private _fileDeleted: boolean
72+
private _filePath: string
73+
private _products: Product[]
74+
75+
constructor({ products = [], filePath }: Constructor) {
76+
const mappedProducts: Product[] = this.mapProducts(products)
77+
78+
this._fileDeleted = false
79+
this._filePath = filePath
80+
this._products = mappedProducts
81+
82+
this.writeFile()
83+
}
84+
85+
public get products(): Product[] {
86+
return this._products
87+
}
88+
89+
private set products(products: Omit<Product, 'total'>[]) {
90+
const mappedProducts = this.mapProducts(products)
91+
this._products = mappedProducts
92+
this.writeFile()
93+
}
94+
95+
public getProductByName(productName: string): {
96+
index: number
97+
} & Product {
98+
let productIndex: number = -1
99+
const product: undefined | Product = this._products.find(
100+
(product, index) => {
101+
if (product.name.toUpperCase() === productName.toUpperCase()) {
102+
productIndex = index
103+
return true
104+
}
105+
}
106+
)
107+
if (!product) throw new Error("Product name doesn't exist")
108+
109+
return {
110+
amount: product.amount,
111+
index: productIndex,
112+
name: product.name,
113+
price: product.price,
114+
total: product.total,
115+
}
116+
}
117+
118+
public getTotalSales(): { amount: number; total: number } {
119+
return this._products.reduce(
120+
(prev, current) => ({
121+
amount: prev.amount + current.amount,
122+
total: prev.total + current.total,
123+
}),
124+
{ amount: 0, total: 0 }
125+
)
126+
}
127+
128+
private joinProducts(products: Product[]): Product[] {
129+
const joinedProducts: Product[] = []
130+
131+
for (let i = 0; i < products.length; i++) {
132+
let { amount, name, price, total } = products[i]
133+
134+
for (let j = i + 1; j < products.length; j++) {
135+
const {
136+
amount: nextAmount,
137+
name: nextName,
138+
price: nextPrice,
139+
} = products[j]
140+
if (name.toUpperCase() === nextName.toUpperCase()) {
141+
amount += nextAmount
142+
price = Math.max(price, nextPrice)
143+
total = price * amount
144+
products.splice(j, 1)
145+
}
146+
}
147+
joinedProducts.push({ amount, name, price, total })
148+
}
149+
150+
return joinedProducts
151+
}
152+
153+
private mapProducts(products: Omit<Product, 'total'>[]): Product[] {
154+
let mappedProducts: Product[] = products.map((product) => ({
155+
...product,
156+
total: product.price * product.amount,
157+
}))
158+
mappedProducts = this.joinProducts(mappedProducts)
159+
160+
return mappedProducts
161+
}
162+
163+
private parseProducts(products = this._products): string {
164+
return products.reduce((prevParsedProd, currentProd) => {
165+
const { amount, name, price } = currentProd
166+
const parsedProduct: string = `${name}, ${amount}, ${price}`
167+
return `${prevParsedProd}\n${parsedProduct}`.trim()
168+
}, '')
169+
}
170+
171+
private shouldMethodFail(): never | void {
172+
if (this._fileDeleted)
173+
throw new Error(
174+
'The file was deleted! You should not try any method of this class instance'
175+
)
176+
}
177+
178+
private writeFile(): void {
179+
fs.writeFileSync(this._filePath, this.parseProducts(this._products), {
180+
encoding: 'utf-8',
181+
})
182+
}
183+
184+
public appendProducts(products: Omit<Product, 'total'>[]): this | never {
185+
this.shouldMethodFail()
186+
this.products = [...this._products, ...products]
187+
return this
188+
}
189+
190+
public deleteFile(): void {
191+
this.shouldMethodFail()
192+
193+
fs.rmSync(this._filePath)
194+
this._fileDeleted = true
195+
}
196+
197+
public deleteProduct(productName: string): this | never {
198+
this.shouldMethodFail()
199+
200+
const { index } = this.getProductByName(productName)
201+
202+
this.products = [
203+
...this._products.slice(0, index),
204+
...this._products.slice(index + 1),
205+
]
206+
return this
207+
}
208+
209+
public updateProduct({
210+
amount,
211+
name,
212+
price,
213+
}: {
214+
amount?: number
215+
name: string
216+
price?: number
217+
}): this | never {
218+
this.shouldMethodFail()
219+
220+
const originalProduct = this.getProductByName(name)
221+
amount ??= originalProduct.amount
222+
price ??= originalProduct.price
223+
224+
const updatedProduct = Object.assign<Product, Partial<Product>>(
225+
{
226+
amount: originalProduct.amount,
227+
name: originalProduct.name,
228+
price: originalProduct.price,
229+
total: originalProduct.total,
230+
},
231+
{
232+
amount,
233+
price,
234+
total: price * amount,
235+
}
236+
)
237+
238+
this.products = [
239+
...this._products.slice(0, originalProduct.index),
240+
updatedProduct,
241+
...this._products.slice(originalProduct.index + 1),
242+
]
243+
return this
244+
}
245+
}
246+
247+
const salesFile: SalesFile = new SalesFile({ filePath: 'sells.txt' })
248+
249+
let userInput: Operation
250+
251+
do {
252+
userInput = readline
253+
.question(
254+
"Write an operation ('Add', 'Delete', 'Print', 'Total sales by product', 'Total sales', 'Update', or 'Exit'): "
255+
)
256+
.toUpperCase() as Operation
257+
258+
switch (userInput) {
259+
case 'ADD':
260+
const newProductName = readline.question('Product name: ')
261+
if (!newProductName) continue
262+
263+
const newProductAmount = readline.questionInt('Product amount: ')
264+
const newProductPrice = readline.questionInt('Product price: ')
265+
266+
salesFile.appendProducts([
267+
{
268+
name: newProductName,
269+
amount: newProductAmount,
270+
price: newProductPrice,
271+
},
272+
])
273+
break
274+
275+
case 'DELETE':
276+
const productNameToDelete = readline.question('Product name to delete: ')
277+
if (!productNameToDelete) continue
278+
279+
salesFile.deleteProduct(productNameToDelete)
280+
break
281+
282+
case 'PRINT':
283+
console.table(salesFile.products)
284+
break
285+
286+
case 'TOTAL SALES BY PRODUCT':
287+
const productNameToGetTotalSales = readline.question(
288+
'Product name to get total sales: '
289+
)
290+
if (!productNameToGetTotalSales) continue
291+
292+
const product = salesFile.getProductByName(productNameToGetTotalSales)
293+
console.table({
294+
amount: product.amount,
295+
price: product.price,
296+
total: product.total,
297+
})
298+
break
299+
300+
case 'TOTAL SALES':
301+
const { amount, total }: { amount: number; total: number } =
302+
salesFile.getTotalSales()
303+
console.table({ amount, total })
304+
break
305+
306+
case 'UPDATE':
307+
const productNameToUpdate = readline.question('Product name to update: ')
308+
if (!productNameToUpdate) continue
309+
310+
const newAmount = readline.questionInt('New amount for the product: ')
311+
const newPrice = readline.questionFloat('New price for the product: ')
312+
313+
salesFile.updateProduct({
314+
amount: newAmount,
315+
name: productNameToUpdate,
316+
price: newPrice,
317+
})
318+
break
319+
320+
case 'EXIT':
321+
salesFile.deleteFile()
322+
break
323+
324+
default:
325+
console.log('Invalid operation! Try again...')
326+
}
327+
} while (userInput !== 'EXIT')

0 commit comments

Comments
 (0)