Skip to content

Commit fd01945

Browse files
AndreiAndrei
authored andcommitted
new: types & defaults
1 parent 2bbcddc commit fd01945

File tree

8 files changed

+376
-176
lines changed

8 files changed

+376
-176
lines changed

bun.lockb

2.82 KB
Binary file not shown.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"typescript": "^5.0.0"
1515
},
1616
"dependencies": {
17+
"axios": "^1.7.7",
1718
"chalk": "^5.3.0",
1819
"cheerio": "^1.0.0",
1920
"commander": "^12.1.0",

src/index.ts

Lines changed: 115 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,64 @@
11
import path from "path";
2+
import fs from "fs-extra"
23
import { Command } from "commander";
34
import logger from "./utils/logger";
45
import Invoice from "./utils/invoice";
56
import minimist from "minimist"
6-
import { initConfig, openConfigDirectory, openConfigurationFile, openInvoicesDirectory } from "./utils";
7+
import { getConfig, getInvoiceDirectory, getVersion, openConfigDirectory, openConfigurationFile, openInvoicesDirectory, timeout } from "./utils";
78
import * as os from "os"
89
import inquirer from "inquirer";
9-
10-
const args = minimist(process.argv.slice(2))
11-
const isDebug = args.debug || false
12-
13-
logger.setDebugMode(isDebug)
14-
15-
const program = new Command();
10+
import type { Config } from "./types/types";
1611

1712
export const DEFAULT_DIR = path.join(os.homedir(), ".config", "icli/");
1813
export const DEFAULT_INVOICES_DIR = path.join(os.homedir(), "Documents", "Invoices/")
1914
export const DEFAULT_PATH = path.join(os.homedir(), ".config", "icli/icli.json");
2015
export const INVOICE_PATH = path.join(path.dirname(DEFAULT_PATH), "invoices.json");
2116

17+
18+
async function init() {
19+
20+
const configExists = fs.existsSync(DEFAULT_PATH)
21+
22+
const version = await getVersion()
23+
24+
if (!configExists) {
25+
const config: Partial<Config> = {
26+
appName: "Invoice CLI Tool",
27+
createdAt: new Date().toISOString(),
28+
version
29+
}
30+
31+
try {
32+
fs.ensureFileSync(DEFAULT_PATH)
33+
fs.writeJsonSync(DEFAULT_PATH, config, { spaces: 2 })
34+
logger.debug().info(`Setup the config with no defaults`)
35+
} catch (e) {
36+
logger.debug().error(`${e}`)
37+
logger.error('An error occured.')
38+
}
39+
} else {
40+
const config = getConfig()
41+
if (config.version !== version && version !== "Unknown") logger.warn(`The version you are running is outdated. Please upgrade.`)
42+
}
43+
44+
await getInvoiceDirectory()
45+
const config = getConfig()
46+
47+
await timeout(200)
48+
49+
let unset = []
50+
for (const defaultOption of Object.keys(Invoice.defaultOptionPrompts)) {
51+
if (config?.default_values?.[defaultOption as keyof typeof config.default_values]) continue
52+
53+
unset.push(defaultOption.toLowerCase())
54+
}
55+
56+
if (unset.length) {
57+
await Invoice.setupDefaultValues(unset)
58+
}
59+
}
60+
61+
2262
export async function mainMenu() {
2363
const choices = await inquirer.prompt([
2464
{
@@ -31,10 +71,10 @@ export async function mainMenu() {
3171

3272
switch (choices.action) {
3373
case "View Invoice History":
34-
new Invoice().viewHistory();
74+
Invoice.viewHistory();
3575
break;
3676
case "Create a New Invoice":
37-
await new Invoice().createInvoice()
77+
await Invoice.createInvoice()
3878
break;
3979
case "Exit CLI":
4080
logger.info("Exiting CLI...");
@@ -44,85 +84,78 @@ export async function mainMenu() {
4484
mainMenu();
4585
}
4686

87+
const args = minimist(process.argv.slice(2))
88+
const isDebug = args.debug || false
89+
90+
logger.setDebugMode(isDebug)
91+
92+
const program = new Command();
93+
4794

48-
const commands = [
49-
{
50-
name: "setup",
51-
description: "Setup configuration file",
52-
action: async () => {
53-
await initConfig();
54-
await mainMenu();
55-
}
56-
},
57-
{
58-
name: "history",
59-
description: "View invoice history",
60-
action: async () => {
61-
new Invoice().viewHistory()
62-
await mainMenu()
63-
}
64-
},
65-
{
66-
name: "create",
67-
description: "Create a new invoice",
68-
action: async () => {
69-
await new Invoice().createInvoice(args.explicit ? false : true)
70-
await mainMenu()
71-
},
72-
option: [{
73-
value: 'explicit',
74-
description: "Explicitly select the values for your personal details"
75-
}]
76-
},
77-
{
78-
name: "config",
79-
description: "Open the configuration file",
80-
action: openConfigurationFile
81-
},
82-
{
83-
name: "cd",
84-
description: "Open the configuration directory",
85-
action: openConfigDirectory
86-
},
87-
{
88-
name: "invoices",
89-
description: "Open the configuration directory",
90-
action: openInvoicesDirectory
91-
},
92-
{
93-
name: "defaults",
94-
description: "Setup your default details for creating invoices.",
95-
// options: [Object.keys(Invoice.prototype.defaultOptionPrompts).map(k => ({value: k, description: `Change default value for: ${k}`}))],
96-
options:
97-
Object.keys(new Invoice().defaultOptionPrompts)
98-
.map(o => ({ value: o.toLowerCase(), description: `Change default for: ${o}` })),
99-
action: async () => {
100-
await new Invoice().setupDefaultValues(Array.from(new Set(Object.keys(args).filter(o => o !== "_" && o !== "debug"))))
101-
},
102-
},
103-
{
104-
name: "exit",
105-
description: "Exit the CLI tool",
106-
action: () => {
107-
logger.info('Exiting CLI...')
108-
process.exit(0)
109-
}
110-
},
111-
]
11295
program
11396
.version("1.0.0")
11497
.description("Invoice CLI Tool")
11598
.option('--debug', "Display external logs")
11699

117-
commands.forEach((c) => {
118-
const cmd = program
119-
.command(c.name)
120-
.description(c.description)
121-
.action(c.action)
100+
program
101+
.command('history')
102+
.description('View invoice history')
103+
.action(async () => {
104+
Invoice.viewHistory()
105+
await mainMenu()
106+
})
107+
108+
program
109+
.command('create')
110+
.description('Create a new invoice')
111+
.option('--explicit')
112+
.action(async () => {
113+
await Invoice.createInvoice(args.explicit ? false : true)
114+
await mainMenu()
115+
})
116+
117+
program
118+
.command('config')
119+
.description('Open the configuration file')
120+
.action(openConfigurationFile)
121+
122+
program
123+
.command('cd')
124+
.description('Open the configuration directory')
125+
.action(openConfigDirectory)
126+
127+
program
128+
.command('invoices')
129+
.description('Open the invoices directory')
130+
.action(openInvoicesDirectory)
131+
132+
program
133+
.command('defaults')
134+
.description('Setup the default details for creating new invoices')
135+
.action(async () => await Invoice.setupDefaultValues(args ? Array.from(new Set(Object.keys(args).filter(o => o !== "_" && o !== 'debug'))) : undefined))
136+
.option('--fullname')
137+
.option('--sortcode')
138+
.option('--banknum')
139+
.option('--address')
140+
.option('--postcode')
141+
.option('--email')
142+
.option('--logo')
122143

123-
c.options?.forEach(c => {
124-
cmd.option(`--${c.value}`, c.description)
144+
program
145+
.command('exit')
146+
.description('Exit the Invoice CLI tool')
147+
.action(() => {
148+
process.exit(0)
125149
})
150+
151+
152+
153+
init().then(() => {
154+
program.parse(process.argv);
155+
156+
if (!process.argv.slice(2).length) {
157+
program.outputHelp()
158+
process.exit(0)
159+
}
126160
})
127161

128-
program.parse(process.argv);

src/types/types.d.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
export interface Config {
2+
appName: string;
3+
version: string;
4+
createdAt: string;
5+
invoices_path: string;
6+
default_values: DefaultValueTypes
7+
}
8+
9+
export interface DefaultOption<T extends 'input' | 'number' = 'input'> {
10+
type: T
11+
message: string
12+
validate: (value: T extends 'number' ? number : string) => void
13+
}
14+
15+
type RequiredDefaultValues<T> = {
16+
[K in keyof T]-?: undefined extends T[K] ? never : K
17+
}[keyof T]
18+
19+
type OptionalDefaultValues<T> = {
20+
[K in keyof T]-?: undefined extends T[K] ? K : never
21+
}[keyof T]
22+
23+
type DefaultValueTypes = {
24+
[K in RequiredDefaultValues<DefaultOptionPrompts>]: any
25+
} & {
26+
[K in OptionalDefaultValues<DefaultOptionPrompts>]?: any
27+
}
28+
29+
export interface DefaultOptionPrompts {
30+
sortCode: DefaultOption
31+
bankNum: DefaultOption<"number">
32+
fullName: DefaultOption
33+
address: DefaultOption
34+
postcode: DefaultOption
35+
logo?: DefaultOption
36+
email?: DefaultOption
37+
}
38+
39+
export interface InvoiceItem {
40+
itemName: string;
41+
price: number;
42+
}
43+
44+
export type TInvoice = { [key: string]: string | InvoiceItem[] }
45+
46+

0 commit comments

Comments
 (0)