diff --git a/flake/node-package.nix b/flake/node-package.nix index 2cf10dc2..d02dcd8d 100644 --- a/flake/node-package.nix +++ b/flake/node-package.nix @@ -1912,6 +1912,15 @@ let sha512 = "7BUT1sEFSNBIcc0wlwKn2l3l3OnYJdjsrlruDbAp6hpOK3HbpgMjLVH4ql6xXwD+qYy+XEHrb2EMkIpo9kWZ+Q=="; }; }; + "@types/tabulator-tables-5.5.7" = { + name = "_at_types_slash_tabulator-tables"; + packageName = "@types/tabulator-tables"; + version = "5.5.7"; + src = fetchurl { + url = "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-5.5.7.tgz"; + sha512 = "2G6i6QhJ/L+8Xk3KAfFZ91qADS9MEu6ve1uT59iaA7fpA6h6AswbFP/5dl3yg8lUhMsP4Zcst073FhbK7Y0TJA=="; + }; + }; "@types/tar-6.1.10" = { name = "_at_types_slash_tar"; packageName = "@types/tar"; @@ -8610,6 +8619,15 @@ let sha512 = "C2PqiSdxDA0v+OH9SP8UxyyfTRLzdxtdwgMjeX/5fvPPYbFixaUXp0hQw3aDN2RrLrwE2vmRJK3sAOICk+0wHA=="; }; }; + "tabulator-tables-5.5.4" = { + name = "tabulator-tables"; + packageName = "tabulator-tables"; + version = "5.5.4"; + src = fetchurl { + url = "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-5.5.4.tgz"; + sha512 = "hVcITAfO2G3gm2ILW9GN2ORgcmNsbVmC+Q+2E3xfthIE9xtFxGKSbhbsNk39h11Uzm9GNUvjGfos1IVKrfeeOA=="; + }; + }; "tailwindcss-3.4.0" = { name = "tailwindcss"; packageName = "tailwindcss"; @@ -9762,6 +9780,7 @@ let sources."@types/sprintf-js-1.1.4" sources."@types/svg2ttf-5.0.3" sources."@types/svgicons2svgfont-10.0.5" + sources."@types/tabulator-tables-5.5.7" sources."@types/tar-6.1.10" sources."@types/ttf2eot-2.0.2" sources."@types/ttf2woff-2.0.4" @@ -10731,6 +10750,7 @@ let sources."svgo-3.0.3" sources."svgpath-2.6.0" sources."svgtofont-4.1.1" + sources."tabulator-tables-5.5.4" (sources."tailwindcss-3.4.0" // { dependencies = [ (sources."postcss-load-config-4.0.2" // { diff --git a/package-lock.json b/package-lock.json index e3423f55..056ac60a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "svelte-local-storage-store": "^0.6.0", "svelte-select": "^5.8.0", "svelte-tiny-virtual-list": "^2.0.5", + "tabulator-tables": "^5.5.4", "textures": "^1.2.3", "tippy.js": "^6.3.7", "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz", @@ -71,6 +72,7 @@ "@types/lodash": "^4.14.194", "@types/papaparse": "^5.3.7", "@types/sprintf-js": "^1.1.2", + "@types/tabulator-tables": "^5.5.7", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.14", @@ -2639,6 +2641,12 @@ "@types/node": "*" } }, + "node_modules/@types/tabulator-tables": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-5.5.7.tgz", + "integrity": "sha512-2G6i6QhJ/L+8Xk3KAfFZ91qADS9MEu6ve1uT59iaA7fpA6h6AswbFP/5dl3yg8lUhMsP4Zcst073FhbK7Y0TJA==", + "dev": true + }, "node_modules/@types/tar": { "version": "6.1.10", "resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.10.tgz", @@ -11795,6 +11803,11 @@ "url": "https://jaywcjlove.github.io/#/sponsor" } }, + "node_modules/tabulator-tables": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-5.5.4.tgz", + "integrity": "sha512-hVcITAfO2G3gm2ILW9GN2ORgcmNsbVmC+Q+2E3xfthIE9xtFxGKSbhbsNk39h11Uzm9GNUvjGfos1IVKrfeeOA==" + }, "node_modules/tailwindcss": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", @@ -14957,6 +14970,12 @@ "@types/node": "*" } }, + "@types/tabulator-tables": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-5.5.7.tgz", + "integrity": "sha512-2G6i6QhJ/L+8Xk3KAfFZ91qADS9MEu6ve1uT59iaA7fpA6h6AswbFP/5dl3yg8lUhMsP4Zcst073FhbK7Y0TJA==", + "dev": true + }, "@types/tar": { "version": "6.1.10", "resolved": "https://registry.npmjs.org/@types/tar/-/tar-6.1.10.tgz", @@ -21796,6 +21815,11 @@ "yargs": "~17.7.1" } }, + "tabulator-tables": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-5.5.4.tgz", + "integrity": "sha512-hVcITAfO2G3gm2ILW9GN2ORgcmNsbVmC+Q+2E3xfthIE9xtFxGKSbhbsNk39h11Uzm9GNUvjGfos1IVKrfeeOA==" + }, "tailwindcss": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", diff --git a/package.json b/package.json index a1d3b69a..e3dd3250 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "svelte-local-storage-store": "^0.6.0", "svelte-select": "^5.8.0", "svelte-tiny-virtual-list": "^2.0.5", + "tabulator-tables": "^5.5.4", "textures": "^1.2.3", "tippy.js": "^6.3.7", "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz", @@ -78,6 +79,7 @@ "@types/lodash": "^4.14.194", "@types/papaparse": "^5.3.7", "@types/sprintf-js": "^1.1.2", + "@types/tabulator-tables": "^5.5.7", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", "autoprefixer": "^10.4.14", diff --git a/src/app.scss b/src/app.scss index 4b3ae29b..7116a665 100644 --- a/src/app.scss +++ b/src/app.scss @@ -31,6 +31,45 @@ $editor-full-height: calc(100vh - 45.5px - 7px - 15.75px - 21px - 57.75px - 10.5 @import "bulma-switch/src/sass/index.sass"; @import "@cityssm/bulma-sticky-table/sticky-table"; +$backgroundColor: $white; +$borderColor: none; +$textSize: 1rem; +$headerBackgroundColor: $white; +$headerTextColor: $grey-dark; +$headerBorderColor: rgba($grey-lightest, 1); +$headerSeparatorColor: rgba($grey-light, 0.5); +$headerMargin: 0.2857rem; +$sortArrowActive: $black; +$sortArrowInactive: $grey-light; +$rowBackgroundColor: $white; +$rowAltBackgroundColor: $white; +$rowBorderColor: rgba($grey-lightest, 0.5); +$rowTextColor: $grey-dark; +$rowHoverBackground: $white-bis; +$rowSelectedBackground: #9abcea; +$rowSelectedBackgroundHover: #769bcc; +$editBoxColor: #1d68cd; +$errorColor: #dd0000; +$footerBackgroundColor: #fff; +$footerTextColor: #555; +$footerBorderColor: #aaa; +$footerSeparatorColor: #999; +$footerActiveColor: #d00; + +@import "tabulator-tables/src/scss/themes/tabulator_simple.scss"; + +.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title { + white-space: normal; +} + +.tabulator { + border-radius: $radius; +} + +.tabulator-col { + padding: 0.5rem; +} + // override message style .message { diff --git a/src/lib/components/AssetsBalance.svelte b/src/lib/components/AssetsBalance.svelte index e744ed46..cd7aabf0 100644 --- a/src/lib/components/AssetsBalance.svelte +++ b/src/lib/components/AssetsBalance.svelte @@ -1,75 +1,65 @@ -
- - - - - - - - - - - - - - - {#each Object.values(breakdowns) as b} - {@const indentWidth = indent ? _.repeat("  ", depth(b.group) - 1) : ""} - {@const gain = b.gainAmount} - {@const changeClass = calculateChangeClass(gain)} - - - - - - - - - - - {/each} - -
AccountInvestment AmountWithdrawal AmountBalance UnitsMarket ValueChangeXIRRAbsolute Return
{@html indentWidth}{iconText(b.group)} - {indent ? lastName(b.group) : b.group}{!isZero(b.investmentAmount) ? formatCurrency(b.investmentAmount) : ""}{!isZero(b.withdrawalAmount) ? formatCurrency(b.withdrawalAmount) : ""}{b.balanceUnits > 0 ? formatFloat(b.balanceUnits, 4) : ""}{!isZero(b.marketAmount) ? formatCurrency(b.marketAmount) : ""}{!isZero(b.investmentAmount) && !isZero(gain) ? formatCurrency(gain) : ""}{!isZero(b.xirr) ? formatFloat(b.xirr) : ""}{!isZero(b.absoluteReturn) ? formatPercentage(b.absoluteReturn, 2) : ""}
-
+{#if indent} + +{:else} +
+{/if} diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 5af0abbe..96477bab 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -118,7 +118,7 @@ label: "More", href: "/more", children: [ - { label: "Configuration", href: "/config", tag: "alpha", help: "config" }, + { label: "Configuration", href: "/config", help: "config" }, { label: "Sheets", href: "/sheets", help: "sheets", disablePreload: true }, { label: "Goals", href: "/goals", help: "goals" }, { label: "Doctor", href: "/doctor" }, diff --git a/src/lib/components/Table.svelte b/src/lib/components/Table.svelte new file mode 100644 index 00000000..37f0533c --- /dev/null +++ b/src/lib/components/Table.svelte @@ -0,0 +1,47 @@ + + +
diff --git a/src/lib/table_formatters.ts b/src/lib/table_formatters.ts new file mode 100644 index 00000000..c64386fa --- /dev/null +++ b/src/lib/table_formatters.ts @@ -0,0 +1,81 @@ +import { type CellComponent } from "tabulator-tables"; +import { formatCurrency, formatFloat, formatPercentage, isZero, lastName } from "./utils"; +import { iconText } from "./icon"; + +export function indendedAssetAccountName(cell: CellComponent) { + const account = cell.getValue(); + let children = ""; + const data = cell.getData(); + if ((data._children?.length || 0) > 0) { + children = `(${data._children?.length})`; + } + return ` + + ${iconText(account)} + ${lastName(account)} + ${children} + +`; +} + +export function indendedLiabilityAccountName(cell: CellComponent) { + const account = cell.getValue(); + let children = ""; + const data = cell.getData(); + if ((data._children?.length || 0) > 0) { + children = `(${data._children?.length})`; + } + return ` + + ${iconText(account)} + ${lastName(account)} + ${children} + +`; +} + +export function accountName(cell: CellComponent) { + const account = cell.getValue(); + return ` + + ${iconText(account)} + ${account} + +`; +} + +function calculateChangeClass(gain: number) { + let changeClass = ""; + if (gain > 0) { + changeClass = "has-text-success"; + } else if (gain < 0) { + changeClass = "has-text-danger"; + } + return changeClass; +} + +export function nonZeroCurrency(cell: CellComponent) { + const value = cell.getValue(); + return isZero(value) ? "" : formatCurrency(value); +} + +export function nonZeroFloatChange(cell: CellComponent) { + const value = cell.getValue(); + return isZero(value) + ? "" + : `${formatFloat(value)}`; +} + +export function nonZeroPercentageChange(cell: CellComponent) { + const value = cell.getValue(); + return isZero(value) + ? "" + : `${formatPercentage(value, 2)}`; +} + +export function formatCurrencyChange(cell: CellComponent) { + const value = cell.getValue(); + return isZero(value) + ? "" + : `${formatCurrency(value)}`; +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index de971f33..690720c6 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1276,3 +1276,30 @@ export function dueDateIcon(dueDate: dayjs.Dayjs, clearedDate: dayjs.Dayjs) { return { icon, color, svgColor, glyph }; } + +export function buildTree(items: I[], accountAccessor: (item: I) => string): I[] { + const result: I[] = []; + + const sorted = _.sortBy(items, accountAccessor); + + for (const item of sorted) { + const account = accountAccessor(item); + const parts = account.split(":"); + let current = result; + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + let found: any = current.find((c) => accountAccessor(c).split(":")[i] === part); + if (!found) { + found = { ...item }; + current.push(found); + } + + if (i !== parts.length - 1) { + found._children = found._children || []; + current = found._children; + } + } + } + + return result; +} diff --git a/src/routes/(app)/liabilities/balance/+page.svelte b/src/routes/(app)/liabilities/balance/+page.svelte index 8e06d2ef..0755d34a 100644 --- a/src/routes/(app)/liabilities/balance/+page.svelte +++ b/src/routes/(app)/liabilities/balance/+page.svelte @@ -1,29 +1,18 @@
@@ -51,48 +80,7 @@
-
-
- - - - - - - - - - - - {#each Object.values(breakdowns) as b} - {@const indent = _.repeat("  ", depth(b.group) - 1)} - {@const changeClass = calculateChangeClass(-b.interest_amount)} - - - - - - - - - {/each} - -
AccountDrawn AmountRepaid AmountBalance AmountInterestAPR
{@html indent}{iconText(b.group)} - {lastName(b.group)}{b.drawn_amount != 0 ? formatCurrency(b.drawn_amount) : ""}{b.repaid_amount != 0 ? formatCurrency(b.repaid_amount) : ""}{b.balance_amount != 0 ? formatCurrency(b.balance_amount) : ""}{b.interest_amount != 0 ? formatCurrency(b.interest_amount) : ""}{b.apr > 0.0001 || b.apr < -0.0001 ? formatFloat(b.apr) : ""}
- + diff --git a/src/routes/(app)/more/goals/savings/[slug]/+page.svelte b/src/routes/(app)/more/goals/savings/[slug]/+page.svelte index 083428f5..0528116e 100644 --- a/src/routes/(app)/more/goals/savings/[slug]/+page.svelte +++ b/src/routes/(app)/more/goals/savings/[slug]/+page.svelte @@ -177,7 +177,7 @@
-
+