diff --git a/.github/workflows/guardrails.yml b/.github/workflows/guardrails.yml index 480aa82ae..5cf95179b 100644 --- a/.github/workflows/guardrails.yml +++ b/.github/workflows/guardrails.yml @@ -32,7 +32,7 @@ jobs: ".ai-notes" ".agent" ) - + for pattern in "${BLOCKED_FILES[@]}"; do if find . -name "$pattern" -type f 2>/dev/null | grep -q .; then echo "❌ Found blocked file: $pattern" @@ -53,7 +53,7 @@ jobs: "openhands" "opencode" ) - + for pattern in "${SENSITIVE_PATTERNS[@]}"; do if grep -r "$pattern" . --include="*.ts" --include="*.js" --include="*.py" --include="*.md" 2>/dev/null | grep -v "node_modules" | grep -v ".git" | grep -q .; then echo "⚠️ Found reference: $pattern" diff --git a/.github/workflows/security-codeql.yml b/.github/workflows/security-codeql.yml index 01232a69c..2d8db3f65 100644 --- a/.github/workflows/security-codeql.yml +++ b/.github/workflows/security-codeql.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - language: ["javascript", "python"] + language: ["javascript"] steps: - name: Checkout uses: actions/checkout@v6 diff --git a/README.md b/README.md index 2a22ce317..471d5212c 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,18 @@ Visit `http://localhost:3000` to access the application. --- +## 📖 Documentation + +- **[User Manual](docs/user-manual.md)** — the full feature list and how to + configure and use Folio to manage your personal finances. +- **[Getting Started](docs/getting-started.md)** — install, set up your first + account, and learn the core concepts. +- **[Activity Types](docs/activities/activity-types.md)** — the 14 transaction + types and when to use each one. +- **[Addons](docs/addons/index.md)** — extend Folio with community features. + +--- + ## 🛠️ Tech Stack | Component | Technology | @@ -115,15 +127,19 @@ Visit `http://localhost:3000` to access the application. ## 📊 Supported Banks -Folio supports automatic import from major Australian banks: +**Bank Connect** (automatic import from the desktop app) supports: -- Commonwealth Bank -- Westpac +- CommBank (CBA) - ANZ -- NAB - ING -- Macquarie -- And more... +- Bank of Melbourne +- Beyond Bank +- Interactive Brokers (broker, Beta) + +Coming soon: CommSec, Sharesies, SelfWealth, and Stake. + +Any other bank or broker can be tracked by importing a **CSV, OFX, or QIF** +statement file. Most Australian institutions offer a statement export. --- diff --git a/addons/goal-progress-tracker/src/components/help-popover.tsx b/addons/goal-progress-tracker/src/components/help-popover.tsx index 7c448d13d..9ee6ef3ed 100644 --- a/addons/goal-progress-tracker/src/components/help-popover.tsx +++ b/addons/goal-progress-tracker/src/components/help-popover.tsx @@ -13,7 +13,7 @@ function HelpPopover() {
-

How It Works

+

How It Works

• Select a goal from your saved goals or track a custom target

• Each dot represents your chosen step amount towards your investment target

diff --git a/addons/goal-progress-tracker/src/components/investment-calendar.tsx b/addons/goal-progress-tracker/src/components/investment-calendar.tsx index c7351a399..858d798fa 100644 --- a/addons/goal-progress-tracker/src/components/investment-calendar.tsx +++ b/addons/goal-progress-tracker/src/components/investment-calendar.tsx @@ -116,7 +116,7 @@ function Tooltip({
-

Step {data.stepIndex + 1}

+

Step {data.stepIndex + 1}

Target: {formatCurrency(data.stepAmount, isBalanceHidden)}

@@ -135,7 +135,7 @@ function Tooltip({
{/* Tooltip arrow */} -
+
); diff --git a/addons/investment-fees-tracker/src/components/account-breakdown.tsx b/addons/investment-fees-tracker/src/components/account-breakdown.tsx index aab86ddb3..7240e9a28 100644 --- a/addons/investment-fees-tracker/src/components/account-breakdown.tsx +++ b/addons/investment-fees-tracker/src/components/account-breakdown.tsx @@ -23,7 +23,7 @@ function EmptyPlaceholder({ {icon}

{title}

-

+

{description}

@@ -138,7 +138,7 @@ export function AccountBreakdown({ {percentage.toFixed(1)}% of total fees {/* Tooltip arrow */} -
+
diff --git a/addons/investment-fees-tracker/src/components/fee-categories-chart.tsx b/addons/investment-fees-tracker/src/components/fee-categories-chart.tsx index dbafa56b6..cc207fe50 100644 --- a/addons/investment-fees-tracker/src/components/fee-categories-chart.tsx +++ b/addons/investment-fees-tracker/src/components/fee-categories-chart.tsx @@ -73,7 +73,7 @@ export const FeeCategoriesChart = ({
- + Fee Categories
diff --git a/addons/investment-fees-tracker/src/components/fee-history-chart.tsx b/addons/investment-fees-tracker/src/components/fee-history-chart.tsx index 72c30b18a..5a4f92b26 100644 --- a/addons/investment-fees-tracker/src/components/fee-history-chart.tsx +++ b/addons/investment-fees-tracker/src/components/fee-history-chart.tsx @@ -74,7 +74,7 @@ export function FeeHistoryChart({ Fee History {periodDescription}
- + {chartData.length === 0 ? (

{title}

-

+

{description}

@@ -138,7 +138,7 @@ export function TopFeeSources({ feeAnalytics, currency, isBalanceHidden }: TopFe {percentage.toFixed(1)}% of total fees {/* Tooltip arrow */} -
+
diff --git a/addons/swingfolio-addon/src/components/distribution-charts.tsx b/addons/swingfolio-addon/src/components/distribution-charts.tsx index 9b5f37415..955a18e0b 100644 --- a/addons/swingfolio-addon/src/components/distribution-charts.tsx +++ b/addons/swingfolio-addon/src/components/distribution-charts.tsx @@ -100,7 +100,7 @@ export function DistributionCharts({ distribution, currency }: DistributionChart return ( <>
= 0 ? "border-success/10 bg-success/10" : "border-destructive/10 bg-destructive/10"}`} > - + P/L
-
+
{selectedPeriod} → {getChartPeriodDisplay(selectedPeriod).type}
diff --git a/apps/frontend/src/addons/addons-core.ts b/apps/frontend/src/addons/addons-core.ts index 6a3db8ee7..fd1768ac9 100644 --- a/apps/frontend/src/addons/addons-core.ts +++ b/apps/frontend/src/addons/addons-core.ts @@ -138,9 +138,7 @@ async function loadAddon(addonFile: AddonFile, _context: AddonContext): Promise< default?: unknown; }; const defaultObj = asRecord.default as - | ((ctx: AddonContext) => unknown) - | { enable?: (ctx: AddonContext) => unknown } - | undefined; + ((ctx: AddonContext) => unknown) | { enable?: (ctx: AddonContext) => unknown } | undefined; const enableFunction: EnableFn = (typeof defaultObj === "function" ? defaultObj : null) ?? (defaultObj && diff --git a/apps/frontend/src/components/account-selector-mobile.tsx b/apps/frontend/src/components/account-selector-mobile.tsx index ed1345249..024942201 100644 --- a/apps/frontend/src/components/account-selector-mobile.tsx +++ b/apps/frontend/src/components/account-selector-mobile.tsx @@ -148,7 +148,7 @@ export const AccountSelectorMobile = forwardRef - + Select Account Choose an account to add to the comparison diff --git a/apps/frontend/src/components/account-selector.tsx b/apps/frontend/src/components/account-selector.tsx index 02016aba3..fb58c78a0 100644 --- a/apps/frontend/src/components/account-selector.tsx +++ b/apps/frontend/src/components/account-selector.tsx @@ -351,7 +351,7 @@ export const AccountSelector = forwardRef diff --git a/apps/frontend/src/components/action-palette.tsx b/apps/frontend/src/components/action-palette.tsx index e704326ce..b9c88bced 100644 --- a/apps/frontend/src/components/action-palette.tsx +++ b/apps/frontend/src/components/action-palette.tsx @@ -65,7 +65,7 @@ export function ActionPalette({ side={side} sideOffset={8} className={cn( - "w-auto min-w-[260px] max-w-[320px] p-0", + "w-auto max-w-[320px] min-w-[260px] p-0", "rounded-2xl", "border-border/50 border dark:border-white/10", "bg-card backdrop-blur-xl", @@ -74,7 +74,7 @@ export function ActionPalette({ > {/* Header - only show if title provided */} {title && ( -
+

{title}

diff --git a/apps/frontend/src/components/app-launcher.tsx b/apps/frontend/src/components/app-launcher.tsx index d245292c6..2be25af62 100644 --- a/apps/frontend/src/components/app-launcher.tsx +++ b/apps/frontend/src/components/app-launcher.tsx @@ -749,7 +749,7 @@ export function AppLauncher() { Quick launch @@ -758,7 +758,7 @@ export function AppLauncher() { className={cn( "flex flex-1 flex-col bg-transparent", "[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-4 [&_[cmdk-group-heading]]:font-medium", - "[&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2", + "[&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0", "[&_[cmdk-input-wrapper]]:px-5 [&_[cmdk-input]]:h-14 [&_[cmdk-input]]:text-base", "[&_[cmdk-item]]:px-4 [&_[cmdk-item]]:py-4 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5", "[&_[data-cmdk-input-wrapper]_svg]:h-5 [&_[data-cmdk-input-wrapper]_svg]:w-5", diff --git a/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx b/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx index b2e7bf7d6..d3a25a47a 100644 --- a/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx +++ b/apps/frontend/src/components/benchmark-symbol-selector-mobile.tsx @@ -187,7 +187,7 @@ export function BenchmarkSymbolSelectorMobile({ {!iconOnly && "Add Benchmark"} - + Select Benchmark Choose a benchmark or search for any symbol @@ -197,13 +197,13 @@ export function BenchmarkSymbolSelectorMobile({ {/* Search Input */}
- + setSearchQuery(e.target.value)} - className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-10 w-full rounded-md border px-3 py-2 pl-9 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2" + className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-10 w-full rounded-md border px-3 py-2 pl-9 text-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none" />
diff --git a/apps/frontend/src/components/classification/classification-sheet.tsx b/apps/frontend/src/components/classification/classification-sheet.tsx index 4b791f4cc..2e362a283 100644 --- a/apps/frontend/src/components/classification/classification-sheet.tsx +++ b/apps/frontend/src/components/classification/classification-sheet.tsx @@ -78,7 +78,7 @@ export function ClassificationSheet({
-
+
{/* Loading State */} {isLoading && } diff --git a/apps/frontend/src/components/classification/multi-select-taxonomy.tsx b/apps/frontend/src/components/classification/multi-select-taxonomy.tsx index 69c620e79..22bf50dc9 100644 --- a/apps/frontend/src/components/classification/multi-select-taxonomy.tsx +++ b/apps/frontend/src/components/classification/multi-select-taxonomy.tsx @@ -284,7 +284,7 @@ export function MultiSelectTaxonomy({ className="h-8 pr-6 text-sm" placeholder="100" /> - + %
diff --git a/apps/frontend/src/components/font-selector.tsx b/apps/frontend/src/components/font-selector.tsx index 8f83f9235..590b3576a 100644 --- a/apps/frontend/src/components/font-selector.tsx +++ b/apps/frontend/src/components/font-selector.tsx @@ -44,7 +44,7 @@ export function FontSelector({ value, onChange, className }: FontSelectorProps)
{font.label}
{value === font.value && ( -
+
)} ))} diff --git a/apps/frontend/src/components/liquid-glass.tsx b/apps/frontend/src/components/liquid-glass.tsx index 1a32be097..5fdbfcf53 100644 --- a/apps/frontend/src/components/liquid-glass.tsx +++ b/apps/frontend/src/components/liquid-glass.tsx @@ -53,7 +53,7 @@ export function LiquidGlass({ style={{ ...style }} >
{children}
-
+
); } diff --git a/apps/frontend/src/components/mobile-actions-menu.tsx b/apps/frontend/src/components/mobile-actions-menu.tsx index 92f59886d..e207a4470 100644 --- a/apps/frontend/src/components/mobile-actions-menu.tsx +++ b/apps/frontend/src/components/mobile-actions-menu.tsx @@ -38,7 +38,7 @@ export function MobileActionsMenu({ - + {title} {description} @@ -53,7 +53,7 @@ export function MobileActionsMenu({ action.onClick(); onOpenChange(false); }} - className="hover:bg-accent active:bg-accent/80 focus:ring-primary flex items-center gap-4 rounded-lg border p-4 text-left transition-colors focus:outline-none focus:ring-2" + className="hover:bg-accent active:bg-accent/80 focus:ring-primary flex items-center gap-4 rounded-lg border p-4 text-left transition-colors focus:ring-2 focus:outline-none" >
diff --git a/apps/frontend/src/components/mobile-loading-indicator.tsx b/apps/frontend/src/components/mobile-loading-indicator.tsx index c367da5e0..8c1e79a7a 100644 --- a/apps/frontend/src/components/mobile-loading-indicator.tsx +++ b/apps/frontend/src/components/mobile-loading-indicator.tsx @@ -8,7 +8,7 @@ export function MobileLoadingIndicator() { } return ( -
+
{/* Smooth indeterminate progress strip */}
onViewChange(view.value)} className={cn( "relative flex cursor-pointer items-center gap-2 rounded-full px-3 py-1.5 text-sm font-medium transition-colors duration-200", - "focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2", + "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none", isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground/80", )} aria-current={isActive ? "page" : undefined} @@ -102,7 +102,7 @@ function MobileNavigation({ onClick={() => onViewChange(item.value)} className={cn( "relative flex cursor-pointer items-center justify-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors duration-200", - "focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2", + "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none", isActive ? "text-foreground" : "text-muted-foreground", )} aria-label={item.label} @@ -229,7 +229,7 @@ export function SwipablePage({ /* Desktop: Navigation at top center + content below */
{/* Header with Navigation and Actions */} -
+
{title &&

{title}

} @@ -200,7 +200,7 @@ export function SwipableRoutesPage({ - + {showCustomAssetForm ? ( <> {/* Custom Asset Form Header */} @@ -291,13 +291,13 @@ export const SymbolSelectorMobile = forwardRef
- + setSearchQuery(e.target.value)} - className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-14 w-full rounded-md border px-4 py-3 pl-12 text-base focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2" + className="bg-background border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring h-14 w-full rounded-md border px-4 py-3 pl-12 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none" autoFocus />
diff --git a/apps/frontend/src/components/ticker-search.tsx b/apps/frontend/src/components/ticker-search.tsx index 3cea90d9e..1ab6c1365 100644 --- a/apps/frontend/src/components/ticker-search.tsx +++ b/apps/frontend/src/components/ticker-search.tsx @@ -514,7 +514,7 @@ const TickerSearchInput = forwardRef( diff --git a/apps/frontend/src/components/update-dialog.tsx b/apps/frontend/src/components/update-dialog.tsx index 723edd6e2..42cb63bdd 100644 --- a/apps/frontend/src/components/update-dialog.tsx +++ b/apps/frontend/src/components/update-dialog.tsx @@ -117,10 +117,10 @@ export function UpdateDialog() { {/* Scrollable content area */}
{/* Header */} -
+
diff --git a/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx b/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx index e5ddbf911..1d12a6b31 100644 --- a/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx +++ b/apps/frontend/src/features/devices-sync/components/pairing-flow/index.tsx @@ -20,12 +20,7 @@ interface PairingFlowProps { // Claimer flow steps type ClaimerStep = - | "enter_code" - | "connecting" - | "waiting_keys" - | "verify_sas" - | "success" - | "error"; + "enter_code" | "connecting" | "waiting_keys" | "verify_sas" | "success" | "error"; export function PairingFlow({ onComplete, onCancel }: PairingFlowProps) { const { state } = useDeviceSync(); diff --git a/apps/frontend/src/features/devices-sync/types.ts b/apps/frontend/src/features/devices-sync/types.ts index 4b5115228..59c6c672c 100644 --- a/apps/frontend/src/features/devices-sync/types.ts +++ b/apps/frontend/src/features/devices-sync/types.ts @@ -4,14 +4,7 @@ export type TrustState = "trusted" | "untrusted" | "revoked"; export type DevicePlatform = - | "ios" - | "android" - | "mac" - | "macos" - | "windows" - | "linux" - | "web" - | "server"; + "ios" | "android" | "mac" | "macos" | "windows" | "linux" | "web" | "server"; export type PairingRole = "issuer" | "claimer"; export type PairingStatus = "open" | "claimed" | "approved" | "completed" | "cancelled" | "expired"; export type KeyState = "ACTIVE" | "PENDING"; diff --git a/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx b/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx index 383eff4cd..81ee23046 100644 --- a/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx +++ b/apps/frontend/src/features/wealthfolio-connect/components/connect-empty-state.tsx @@ -83,7 +83,7 @@ export function ConnectEmptyState() { {/* CTA */}