diff --git a/components/Currency.tsx b/components/Currency.tsx new file mode 100644 index 0000000..c5aef91 --- /dev/null +++ b/components/Currency.tsx @@ -0,0 +1,20 @@ +import { JSX } from 'preact/jsx-runtime' + +export const currencyFormat = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + maximumFractionDigits: 2, + minimumFractionDigits: 2, +}) + +export function Currency( + { amount, ...props }: + & { amount: number } + & JSX.HTMLAttributes, +) { + return ( + + {currencyFormat.format(amount)} + + ) +} diff --git a/components/IconButton.tsx b/components/IconButton.tsx new file mode 100644 index 0000000..2eab7f0 --- /dev/null +++ b/components/IconButton.tsx @@ -0,0 +1,24 @@ +import { JSX } from 'preact/jsx-runtime' + +interface IconButtonProps { + active?: boolean + text?: string + icon?: JSX.Element +} + +export function IconButton( + { icon, children, active, ...props }: + & IconButtonProps + & JSX.HTMLAttributes, +) { + return ( + + ) +} diff --git a/components/Percentage.tsx b/components/Percentage.tsx new file mode 100644 index 0000000..7e4cb53 --- /dev/null +++ b/components/Percentage.tsx @@ -0,0 +1,23 @@ +const percentageFormat = new Intl.NumberFormat('en-US', { + currency: 'USD', + maximumFractionDigits: 2, +}) +export function percentage(ratio: number): string { + return percentageFormat.format(ratio * 100) + '%' +} + +export function clamp(min: number, val: number, max: number): number { + return Math.max(min, Math.min(max, val)) +} + +export function Percentage( + { ratio, ...props }: + & { ratio: number } + & JSX.HTMLAttributes, +) { + return ( + + {percentage(ratio)} + + ) +} diff --git a/deno.json b/deno.json index c1dd6f3..3107d16 100644 --- a/deno.json +++ b/deno.json @@ -9,8 +9,17 @@ "preview": "deno run -A main.ts", "update": "deno run -A -r https://fresh.deno.dev/update ." }, - "lint": { "rules": { "tags": ["fresh", "recommended"] } }, - "exclude": ["**/_fresh/*"], + "lint": { + "rules": { + "tags": [ + "fresh", + "recommended" + ] + } + }, + "exclude": [ + "**/_fresh/*" + ], "imports": { "$fresh/": "https://deno.land/x/fresh@1.6.8/", "preact": "https://esm.sh/preact@10.19.6", @@ -22,7 +31,14 @@ "tailwindcss/plugin": "npm:/tailwindcss@3.4.1/plugin.js", "$std/": "https://deno.land/std@0.216.0/" }, - "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" }, - "fmt": { "useTabs": true, "semiColons": false, "singleQuote": true }, + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "preact" + }, + "fmt": { + "useTabs": true, + "semiColons": false, + "singleQuote": true + }, "nodeModulesDir": true -} +} \ No newline at end of file diff --git a/fresh.gen.ts b/fresh.gen.ts index 579fc83..205300a 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -7,6 +7,7 @@ import * as $_app from './routes/_app.tsx' import * as $api_joke from './routes/api/joke.ts' import * as $greet_name_ from './routes/greet/[name].tsx' import * as $index from './routes/index.tsx' +import * as $BudgetCard from './islands/BudgetCard.tsx' import * as $Counter from './islands/Counter.tsx' import * as $Dashboard from './islands/Dashboard.tsx' import { type Manifest } from '$fresh/server.ts' @@ -20,6 +21,7 @@ const manifest = { './routes/index.tsx': $index, }, islands: { + './islands/BudgetCard.tsx': $BudgetCard, './islands/Counter.tsx': $Counter, './islands/Dashboard.tsx': $Dashboard, }, diff --git a/islands/BudgetCard.tsx b/islands/BudgetCard.tsx new file mode 100644 index 0000000..0a41316 --- /dev/null +++ b/islands/BudgetCard.tsx @@ -0,0 +1,66 @@ +import { JSX } from 'preact/jsx-runtime' +import { Budget } from '../models.ts' +import { Currency } from '../components/Currency.tsx' +import { Percentage } from '../components/Percentage.tsx' + +export function BudgetCard( + { budget, ...props }: + & { budget: Budget } + & JSX.HTMLAttributes, +) { + const remainingRatio = 1.0 - (budget.spent / budget.target) + const bgColor = remainingRatio < 0 ? 'bg-red' : 'bg-green' + const textColor = remainingRatio < 0 ? 'text-red' : 'text-green' + + return ( +
+
+

+ {budget.name} +

+
+ + + {remainingRatio >= 0 ? ` left ` : ` over `} + +
+ + of{' '} + + {' spent '} + ( remaining) + +
+ { + /* + + */ + } +
+ ) +} diff --git a/islands/Dashboard.tsx b/islands/Dashboard.tsx index 5379936..38b7758 100644 --- a/islands/Dashboard.tsx +++ b/islands/Dashboard.tsx @@ -1,63 +1,7 @@ import { useSignal } from 'https://esm.sh/v135/@preact/signals@1.2.2/X-ZS8q/dist/signals.js' -import { JSX } from 'preact' - -import { Budget, currency, percentage } from '../models.ts' - -interface IconButtonProps { - active?: boolean - text?: string - icon?: JSX.Element -} -function IconButton( - { icon, children, active, ...props }: - & IconButtonProps - & JSX.HTMLAttributes, -) { - return ( - - ) -} - -function BudgetCard( - { budget, ...props }: - & { budget: Budget } - & JSX.HTMLAttributes, -) { - return ( -
-
- {budget.name}: {currency(budget.spent)} / {currency(budget.target)}{' '} - ({percentage(budget.spent / budget.target)}) -
- -
- ) -} +import { Budget } from '../models.ts' +import { BudgetCard } from './BudgetCard.tsx' +import { IconButton } from '../components/IconButton.tsx' export default function Dashboard() { const activeNavItemIndex = useSignal(0) @@ -67,6 +11,12 @@ export default function Dashboard() { target: 1000, spent: 367.97, }, + { + name: + 'A very long budget name that should definitely be shortened to something reasonable', + target: 100000000, + spent: 26893085.56, + }, { name: 'Fast Food', target: 300, @@ -144,25 +94,33 @@ export default function Dashboard() { ] return ( -
-
- {budgets.map((budget, _i) => )} -
+
+
+

FlanBank

+
+
+
+ {/*

Budgets Overview

*/} +
+ {budgets.map((budget, _i) => )} +
+
- + +
) } diff --git a/models.ts b/models.ts index 3466528..a2b2a91 100644 --- a/models.ts +++ b/models.ts @@ -16,21 +16,3 @@ export interface Transaction { budget: string amount: number } - -const currencyFormat = new Intl.NumberFormat('en-US', { - style: 'currency', - currency: 'USD', - maximumFractionDigits: 2, - minimumFractionDigits: 2, -}) -export function currency(amount: number): string { - return currencyFormat.format(amount) -} - -const percentageFormat = new Intl.NumberFormat('en-US', { - currency: 'USD', - maximumFractionDigits: 2, -}) -export function percentage(ratio: number): string { - return percentageFormat.format(ratio * 100) + '%' -} diff --git a/routes/_app.tsx b/routes/_app.tsx index d83c539..161dac7 100644 --- a/routes/_app.tsx +++ b/routes/_app.tsx @@ -8,7 +8,7 @@ export default function App({ Component }: PageProps) { Bank - +