diff --git a/package-lock.json b/package-lock.json index 0e3cd024..b4dfbd9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,13 +16,19 @@ "@codemirror/search": "^6.5.11", "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.38.1", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", "@uiw/react-codemirror": "^4.25.1", "codemirror": "^6.0.2", "diff-match-patch": "^1.0.5", + "html2pdf.js": "^0.12.1", "js-beautify": "^1.15.4", + "jspdf": "^3.0.3", + "jspdf-autotable": "^5.0.2", "lucide-react": "^0.540.0", "papaparse": "^5.5.3", "react": "18.3.1", @@ -2372,6 +2378,59 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "dev": true, @@ -4092,6 +4151,12 @@ "@types/node": "*" } }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", + "license": "MIT" + }, "node_modules/@types/parse-json": { "version": "4.0.2", "dev": true, @@ -4112,6 +4177,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/range-parser": { "version": "1.2.7", "dev": true, @@ -4177,7 +4249,7 @@ }, "node_modules/@types/trusted-types": { "version": "2.0.7", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/ws": { @@ -5416,6 +5488,15 @@ "version": "1.0.2", "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/batch": { "version": "0.6.1", "dev": true, @@ -5717,6 +5798,26 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", "dev": true, @@ -6191,7 +6292,7 @@ }, "node_modules/core-js": { "version": "3.45.1", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "MIT", "funding": { @@ -6310,6 +6411,15 @@ "postcss": "^8.4" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-loader": { "version": "6.11.0", "dev": true, @@ -7074,6 +7184,16 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "dev": true, @@ -8291,6 +8411,17 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "license": "MIT", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, "node_modules/fast-uri": { "version": "3.0.6", "dev": true, @@ -8333,6 +8464,12 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "dev": true, @@ -9287,6 +9424,29 @@ } } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/html2pdf.js": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.12.1.tgz", + "integrity": "sha512-3rBWQ96H5oOU9jtoz3MnE/epGi27ig9h8aonBk4JTpvUERM3lMRxhIRckhJZEi4wE0YfRINoYOIDY0hLY0CHgQ==", + "license": "MIT", + "dependencies": { + "html2canvas": "^1.0.0", + "jspdf": "^3.0.0" + } + }, "node_modules/htmlparser2": { "version": "6.1.0", "dev": true, @@ -9541,6 +9701,12 @@ "node": ">= 0.4" } }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", + "license": "MIT" + }, "node_modules/ipaddr.js": { "version": "2.2.0", "dev": true, @@ -11229,6 +11395,32 @@ "node": ">=0.10.0" } }, + "node_modules/jspdf": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.3.tgz", + "integrity": "sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.9", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.2.4", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf-autotable": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.2.tgz", + "integrity": "sha512-YNKeB7qmx3pxOLcNeoqAv3qTS7KuvVwkFe5AduCawpop3NOkBUtqDToxNc225MlNecxT4kP2Zy3z/y/yvGdXUQ==", + "license": "MIT", + "peerDependencies": { + "jspdf": "^2 || ^3" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "dev": true, @@ -12089,6 +12281,12 @@ "version": "1.0.1", "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/papaparse": { "version": "5.5.3", "license": "MIT" @@ -12209,7 +12407,7 @@ }, "node_modules/performance-now": { "version": "2.1.0", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/picocolors": { @@ -13716,7 +13914,7 @@ }, "node_modules/raf": { "version": "3.4.1", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "performance-now": "^2.1.0" @@ -14172,7 +14370,7 @@ }, "node_modules/regenerator-runtime": { "version": "0.13.11", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/regex-parser": { @@ -14418,6 +14616,16 @@ "node": ">=0.10.0" } }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "dev": true, @@ -15191,6 +15399,16 @@ "node": ">=8" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stackframe": { "version": "1.3.4", "dev": true, @@ -15681,6 +15899,16 @@ "dev": true, "license": "MIT" }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/svgo": { "version": "1.3.2", "dev": true, @@ -16042,6 +16270,15 @@ "node": "*" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "dev": true, @@ -16175,7 +16412,6 @@ }, "node_modules/tslib": { "version": "2.8.1", - "dev": true, "license": "0BSD" }, "node_modules/tsutils": { @@ -16523,6 +16759,15 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "8.3.2", "dev": true, diff --git a/package.json b/package.json index 5bbb3f42..aff8dfd5 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,19 @@ "@codemirror/search": "^6.5.11", "@codemirror/theme-one-dark": "^6.1.3", "@codemirror/view": "^6.38.1", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", "@uiw/react-codemirror": "^4.25.1", "codemirror": "^6.0.2", "diff-match-patch": "^1.0.5", + "html2pdf.js": "^0.12.1", "js-beautify": "^1.15.4", + "jspdf": "^3.0.3", + "jspdf-autotable": "^5.0.2", "lucide-react": "^0.540.0", "papaparse": "^5.5.3", "react": "18.3.1", diff --git a/public/utils/currencies.json b/public/utils/currencies.json new file mode 100644 index 00000000..22049ad6 --- /dev/null +++ b/public/utils/currencies.json @@ -0,0 +1 @@ +[ { "code":"AED", "name":"United Arab Emirates dirham", "symbol":"" }, { "code":"AFN", "name":"Afghan afghani", "symbol":"" }, { "code":"ALL", "name":"Albanian lek", "symbol":"" }, { "code":"AMD", "name":"Armenian dram", "symbol":"" }, { "code":"ANG", "name":"Netherlands Antillean guilder", "symbol":"" }, { "code":"AOA", "name":"Angolan kwanza", "symbol":"" }, { "code":"ARS", "name":"Argentine peso", "symbol":"" }, { "code":"AUD", "name":"Australian dollar", "symbol":"" }, { "code":"AWG", "name":"Aruban florin", "symbol":"" }, { "code":"AZN", "name":"Azerbaijani manat", "symbol":"" }, { "code":"BAM", "name":"Bosnia and Herzegovina convertible mark", "symbol":"" }, { "code":"BBD", "name":"Barbados dollar", "symbol":"" }, { "code":"BDT", "name":"Bangladeshi taka", "symbol":"" }, { "code":"BGN", "name":"Bulgarian lev", "symbol":"" }, { "code":"BHD", "name":"Bahraini dinar", "symbol":"" }, { "code":"BIF", "name":"Burundian franc", "symbol":"" }, { "code":"BMD", "name":"Bermudian dollar", "symbol":"" }, { "code":"BND", "name":"Brunei dollar", "symbol":"" }, { "code":"BOB", "name":"Boliviano", "symbol":"" }, { "code":"BRL", "name":"Brazilian real", "symbol":"" }, { "code":"BSD", "name":"Bahamian dollar", "symbol":"" }, { "code":"BTN", "name":"Bhutanese ngultrum", "symbol":"" }, { "code":"BWP", "name":"Botswana pula", "symbol":"" }, { "code":"BYN", "name":"New Belarusian ruble", "symbol":"" }, { "code":"BYR", "name":"Belarusian ruble", "symbol":"" }, { "code":"BZD", "name":"Belize dollar", "symbol":"" }, { "code":"CAD", "name":"Canadian dollar", "symbol":"" }, { "code":"CDF", "name":"Congolese franc", "symbol":"" }, { "code":"CHF", "name":"Swiss franc", "symbol":"" }, { "code":"CLF", "name":"Unidad de Fomento", "symbol":"" }, { "code":"CLP", "name":"Chilean peso", "symbol":"" }, { "code":"CNY", "name":"Renminbi|Chinese yuan", "symbol":"" }, { "code":"COP", "name":"Colombian peso", "symbol":"" }, { "code":"CRC", "name":"Costa Rican colon", "symbol":"₡" }, { "code":"CUC", "name":"Cuban convertible peso", "symbol":"" }, { "code":"CUP", "name":"Cuban peso", "symbol":"" }, { "code":"CVE", "name":"Cape Verde escudo", "symbol":"" }, { "code":"CZK", "name":"Czech koruna", "symbol":"" }, { "code":"DJF", "name":"Djiboutian franc", "symbol":"" }, { "code":"DKK", "name":"Danish krone", "symbol":"" }, { "code":"DOP", "name":"Dominican peso", "symbol":"" }, { "code":"DZD", "name":"Algerian dinar", "symbol":"" }, { "code":"EGP", "name":"Egyptian pound", "symbol":"" }, { "code":"ERN", "name":"Eritrean nakfa", "symbol":"" }, { "code":"ETB", "name":"Ethiopian birr", "symbol":"" }, { "code":"EUR", "name":"Euro", "symbol":"€" }, { "code":"FJD", "name":"Fiji dollar", "symbol":"" }, { "code":"FKP", "name":"Falkland Islands pound", "symbol":"" }, { "code":"GBP", "name":"Pound sterling", "symbol":"£" }, { "code":"GEL", "name":"Georgian lari", "symbol":"" }, { "code":"GHS", "name":"Ghanaian cedi", "symbol":"" }, { "code":"GIP", "name":"Gibraltar pound", "symbol":"" }, { "code":"GMD", "name":"Gambian dalasi", "symbol":"" }, { "code":"GNF", "name":"Guinean franc", "symbol":"" }, { "code":"GTQ", "name":"Guatemalan quetzal", "symbol":"" }, { "code":"GYD", "name":"Guyanese dollar", "symbol":"" }, { "code":"HKD", "name":"Hong Kong dollar", "symbol":"" }, { "code":"HNL", "name":"Honduran lempira", "symbol":"" }, { "code":"HRK", "name":"Croatian kuna", "symbol":"" }, { "code":"HTG", "name":"Haitian gourde", "symbol":"" }, { "code":"HUF", "name":"Hungarian forint", "symbol":"" }, { "code":"IDR", "name":"Indonesian rupiah", "symbol":"Rp" }, { "code":"ILS", "name":"Israeli new shekel", "symbol":"₪" }, { "code":"INR", "name":"Indian rupee", "symbol":"₹" }, { "code":"IQD", "name":"Iraqi dinar", "symbol":"" }, { "code":"IRR", "name":"Iranian rial", "symbol":"" }, { "code":"ISK", "name":"Icelandic króna", "symbol":"" }, { "code":"JMD", "name":"Jamaican dollar", "symbol":"" }, { "code":"JOD", "name":"Jordanian dinar", "symbol":"" }, { "code":"JPY", "name":"Japanese yen", "symbol":"¥" }, { "code":"KES", "name":"Kenyan shilling", "symbol":"" }, { "code":"KGS", "name":"Kyrgyzstani som", "symbol":"" }, { "code":"KHR", "name":"Cambodian riel", "symbol":"" }, { "code":"KMF", "name":"Comoro franc", "symbol":"" }, { "code":"KPW", "name":"North Korean won", "symbol":"" }, { "code":"KRW", "name":"South Korean won", "symbol":"₩" }, { "code":"KWD", "name":"Kuwaiti dinar", "symbol":"" }, { "code":"KYD", "name":"Cayman Islands dollar", "symbol":"" }, { "code":"KZT", "name":"Kazakhstani tenge", "symbol":"" }, { "code":"LAK", "name":"Lao kip", "symbol":"" }, { "code":"LBP", "name":"Lebanese pound", "symbol":"" }, { "code":"LKR", "name":"Sri Lankan rupee", "symbol":"" }, { "code":"LRD", "name":"Liberian dollar", "symbol":"" }, { "code":"LSL", "name":"Lesotho loti", "symbol":"" }, { "code":"LYD", "name":"Libyan dinar", "symbol":"" }, { "code":"MAD", "name":"Moroccan dirham", "symbol":"" }, { "code":"MDL", "name":"Moldovan leu", "symbol":"" }, { "code":"MGA", "name":"Malagasy ariary", "symbol":"" }, { "code":"MKD", "name":"Macedonian denar", "symbol":"" }, { "code":"MMK", "name":"Myanmar kyat", "symbol":"" }, { "code":"MNT", "name":"Mongolian tögrög", "symbol":"" }, { "code":"MOP", "name":"Macanese pataca", "symbol":"" }, { "code":"MRO", "name":"Mauritanian ouguiya", "symbol":"" }, { "code":"MUR", "name":"Mauritian rupee", "symbol":"" }, { "code":"MVR", "name":"Maldivian rufiyaa", "symbol":"" }, { "code":"MWK", "name":"Malawian kwacha", "symbol":"" }, { "code":"MXN", "name":"Mexican peso", "symbol":"" }, { "code":"MXV", "name":"Mexican Unidad de Inversion", "symbol":"" }, { "code":"MYR", "name":"Malaysian ringgit", "symbol":"RM" }, { "code":"MZN", "name":"Mozambican metical", "symbol":"" }, { "code":"NAD", "name":"Namibian dollar", "symbol":"" }, { "code":"NGN", "name":"Nigerian naira", "symbol":"₦" }, { "code":"NIO", "name":"Nicaraguan córdoba", "symbol":"" }, { "code":"NOK", "name":"Norwegian krone", "symbol":"" }, { "code":"NPR", "name":"Nepalese rupee", "symbol":"" }, { "code":"NZD", "name":"New Zealand dollar", "symbol":"" }, { "code":"OMR", "name":"Omani rial", "symbol":"" }, { "code":"PAB", "name":"Panamanian balboa", "symbol":"" }, { "code":"PEN", "name":"Peruvian Sol", "symbol":"" }, { "code":"PGK", "name":"Papua New Guinean kina", "symbol":"" }, { "code":"PHP", "name":"Philippine peso", "symbol":"₱" }, { "code":"PKR", "name":"Pakistani rupee", "symbol":"" }, { "code":"PLN", "name":"Polish złoty", "symbol":"zł" }, { "code":"PYG", "name":"Paraguayan guaraní", "symbol":"₲" }, { "code":"QAR", "name":"Qatari riyal", "symbol":"" }, { "code":"RON", "name":"Romanian leu", "symbol":"" }, { "code":"RSD", "name":"Serbian dinar", "symbol":"" }, { "code":"RUB", "name":"Russian ruble", "symbol":"" }, { "code":"RWF", "name":"Rwandan franc", "symbol":"" }, { "code":"SAR", "name":"Saudi riyal", "symbol":"" }, { "code":"SBD", "name":"Solomon Islands dollar", "symbol":"" }, { "code":"SCR", "name":"Seychelles rupee", "symbol":"" }, { "code":"SDG", "name":"Sudanese pound", "symbol":"" }, { "code":"SEK", "name":"Swedish krona", "symbol":"" }, { "code":"SGD", "name":"Singapore dollar", "symbol":"" }, { "code":"SHP", "name":"Saint Helena pound", "symbol":"" }, { "code":"SLL", "name":"Sierra Leonean leone", "symbol":"" }, { "code":"SOS", "name":"Somali shilling", "symbol":"" }, { "code":"SRD", "name":"Surinamese dollar", "symbol":"" }, { "code":"SSP", "name":"South Sudanese pound", "symbol":"" }, { "code":"STD", "name":"São Tomé and Príncipe dobra", "symbol":"" }, { "code":"SVC", "name":"Salvadoran colón", "symbol":"" }, { "code":"SYP", "name":"Syrian pound", "symbol":"" }, { "code":"SZL", "name":"Swazi lilangeni", "symbol":"" }, { "code":"THB", "name":"Thai baht", "symbol":"฿" }, { "code":"TJS", "name":"Tajikistani somoni", "symbol":"" }, { "code":"TMT", "name":"Turkmenistani manat", "symbol":"" }, { "code":"TND", "name":"Tunisian dinar", "symbol":"" }, { "code":"TOP", "name":"Tongan paʻanga", "symbol":"" }, { "code":"TRY", "name":"Turkish lira", "symbol":"" }, { "code":"TTD", "name":"Trinidad and Tobago dollar", "symbol":"" }, { "code":"TWD", "name":"New Taiwan dollar", "symbol":"" }, { "code":"TZS", "name":"Tanzanian shilling", "symbol":"" }, { "code":"UAH", "name":"Ukrainian hryvnia", "symbol":"₴" }, { "code":"UGX", "name":"Ugandan shilling", "symbol":"" }, { "code":"USD", "name":"United States dollar", "symbol":"$" }, { "code":"UYI", "name":"Uruguay Peso en Unidades Indexadas", "symbol":"" }, { "code":"UYU", "name":"Uruguayan peso", "symbol":"" }, { "code":"UZS", "name":"Uzbekistan som", "symbol":"" }, { "code":"VEF", "name":"Venezuelan bolívar", "symbol":"" }, { "code":"VND", "name":"Vietnamese đồng", "symbol":"₫" }, { "code":"VUV", "name":"Vanuatu vatu", "symbol":"" }, { "code":"WST", "name":"Samoan tala", "symbol":"" }, { "code":"XAF", "name":"Central African CFA franc", "symbol":"" }, { "code":"XCD", "name":"East Caribbean dollar", "symbol":"" }, { "code":"XOF", "name":"West African CFA franc", "symbol":"" }, { "code":"XPF", "name":"CFP franc", "symbol":"" }, { "code":"XXX", "name":"No currency", "symbol":"" }, { "code":"YER", "name":"Yemeni rial", "symbol":"" }, { "code":"ZAR", "name":"South African rand", "symbol":"" }, { "code":"ZMW", "name":"Zambian kwacha", "symbol":"" }, { "code":"ZWL", "name":"Zimbabwean dollar", "symbol":"" } ] \ No newline at end of file diff --git a/src/App.js b/src/App.js index b9cb1133..d605eb62 100644 --- a/src/App.js +++ b/src/App.js @@ -13,6 +13,9 @@ import DiffTool from './pages/DiffTool'; import TextLengthTool from './pages/TextLengthTool'; import ObjectEditor from './pages/ObjectEditor'; import TableEditor from './pages/TableEditor'; +import InvoiceEditor from './pages/InvoiceEditor'; +import InvoicePreview from './pages/InvoicePreview'; +import InvoicePreviewMinimal from './pages/InvoicePreviewMinimal'; import ReleaseNotes from './pages/ReleaseNotes'; import TermsOfService from './pages/TermsOfService'; import PrivacyPolicy from './pages/PrivacyPolicy'; @@ -42,6 +45,9 @@ function App() { } /> } /> } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/src/components/CodeEditor.js b/src/components/CodeEditor.js new file mode 100644 index 00000000..3b400b74 --- /dev/null +++ b/src/components/CodeEditor.js @@ -0,0 +1,99 @@ +import React from 'react'; +import CodeMirror from '@uiw/react-codemirror'; +import { javascript } from '@codemirror/lang-javascript'; +import { json } from '@codemirror/lang-json'; +import { html } from '@codemirror/lang-html'; +import { css } from '@codemirror/lang-css'; +import { oneDark } from '@codemirror/theme-one-dark'; +import { EditorView } from '@codemirror/view'; + +const CodeEditor = ({ + value, + onChange, + language = 'json', + placeholder = '', + readOnly = false, + height = '300px', + className = '', + theme = 'light' +}) => { + // Language extensions mapping + const getLanguageExtension = (lang) => { + switch (lang.toLowerCase()) { + case 'javascript': + case 'js': + return [javascript()]; + case 'json': + return [json()]; + case 'html': + return [html()]; + case 'css': + return [css()]; + default: + return [json()]; // Default to JSON + } + }; + + // Theme configuration + const getTheme = () => { + if (theme === 'dark') { + return oneDark; + } + return undefined; // Use default light theme + }; + + // Extensions + const extensions = [ + ...getLanguageExtension(language), + EditorView.theme({ + '&': { + fontSize: '14px', + }, + '.cm-content': { + padding: '16px', + minHeight: height, + }, + '.cm-focused': { + outline: 'none', + }, + '.cm-editor': { + borderRadius: '8px', + }, + '.cm-scroller': { + fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace', + }, + }), + EditorView.lineWrapping, + ]; + + return ( +
+ +
+ ); +}; + +export default CodeEditor; diff --git a/src/components/Layout.js b/src/components/Layout.js index 66e211ef..afd0dc58 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -1,8 +1,10 @@ import React, { useState, useEffect, useRef } from 'react'; -import { Link, useLocation } from 'react-router-dom'; -import { Home, Menu, X, ChevronDown, Terminal, Sparkles } from 'lucide-react'; -import ThemeToggle from './ThemeToggle'; +import { useLocation } from 'react-router-dom'; import ToolSidebar from './ToolSidebar'; +import NavigationConfirmModal from './NavigationConfirmModal'; +import useNavigationGuard from '../hooks/useNavigationGuard'; +import { Menu, X, ChevronDown, Terminal, Sparkles, Home } from 'lucide-react'; +import ThemeToggle from './ThemeToggle'; import SEOHead from './SEOHead'; import ConsentBanner from './ConsentBanner'; import { NON_TOOLS, TOOLS, SITE_CONFIG, getCategoryConfig } from '../config/tools'; @@ -10,6 +12,7 @@ import { useAnalytics } from '../hooks/useAnalytics'; const Layout = ({ children }) => { const location = useLocation(); + const { showModal, pendingNavigation, handleConfirm, handleCancel, hasUnsavedData, navigateWithGuard } = useNavigationGuard(); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const dropdownRef = useRef(null); @@ -43,6 +46,9 @@ const Layout = ({ children }) => { // Check if we're on a tool page (not homepage) const isToolPage = location.pathname !== '/'; + + // Check if we're on invoice preview page (no sidebar needed) + const isInvoicePreviewPage = location.pathname === '/invoice-preview'; return (
@@ -50,10 +56,10 @@ const Layout = ({ children }) => { {/* Header */} -
+
- +
{/* Desktop Navigation - only show on homepage */} {!isToolPage && (