Misc. pizzazz
This commit is contained in:
parent
e0a73c5032
commit
cc539727ca
|
@ -1,7 +1,7 @@
|
||||||
import { useSignal } from 'https://esm.sh/v135/@preact/signals@1.2.2/X-ZS8q/dist/signals.js'
|
import { useSignal } from 'https://esm.sh/v135/@preact/signals@1.2.2/X-ZS8q/dist/signals.js'
|
||||||
import { JSX } from 'preact'
|
import { JSX } from 'preact'
|
||||||
|
|
||||||
import { Budget } from '../models.ts'
|
import { Budget, currency, percentage } from '../models.ts'
|
||||||
|
|
||||||
interface IconButtonProps {
|
interface IconButtonProps {
|
||||||
active?: boolean
|
active?: boolean
|
||||||
|
@ -15,30 +15,47 @@ function IconButton(
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class={'py-2 px-4 flex flex-col sm:flex-row flex-1 sm:flex-none bg-bg justify-center items-center sm:justify-start text-center sm:text-left rounded' +
|
class={'p-2 sm:px-4 flex flex-col sm:flex-row flex-1 sm:flex-none bg-mantle justify-center items-center sm:justify-start text-center sm:text-left rounded' +
|
||||||
(active ? ' text-primary' : '')}
|
(active ? ' text-primary' : '')}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<span class='sm:mr-2'>{icon}</span>
|
<div class='sm:mr-2'>{icon}</div>
|
||||||
<span class=''>{children}</span>
|
<div class=''>{children}</div>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function BudgetCard(
|
function BudgetCard(
|
||||||
{ children, ...props }:
|
{ budget, ...props }:
|
||||||
& { budget: Budget }
|
& { budget: Budget }
|
||||||
& JSX.HTMLAttributes<HTMLDivElement>,
|
& JSX.HTMLAttributes<HTMLDivElement>,
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<button
|
<div
|
||||||
class={'py-2 px-4 flex flex-col sm:flex-row flex-1 sm:flex-none bg-bg justify-center items-center sm:justify-start text-center sm:text-left rounded' +
|
class='bg-surface0 rounded shadow hover:bg-surface1 transition-colors flex justify-between'
|
||||||
(active ? ' text-primary' : '')}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<span class='sm:mr-2'>{icon}</span>
|
<div class='p-2'>
|
||||||
<span class=''>{children}</span>
|
{budget.name}: {currency(budget.spent)} / {currency(budget.target)}{' '}
|
||||||
|
({percentage(budget.spent / budget.target)})
|
||||||
|
</div>
|
||||||
|
<button class='text-surface1 cursor-grab p-2'>
|
||||||
|
<svg
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
fill='none'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
strokeWidth={1.5}
|
||||||
|
stroke='currentColor'
|
||||||
|
className='size-6'
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeLinejoin='round'
|
||||||
|
d='M3.75 9h16.5m-16.5 6.75h16.5'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +67,21 @@ export default function Dashboard() {
|
||||||
target: 1000,
|
target: 1000,
|
||||||
spent: 367.97,
|
spent: 367.97,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Fast Food',
|
||||||
|
target: 300,
|
||||||
|
spent: 420.69,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Insurance',
|
||||||
|
target: 220.44,
|
||||||
|
spent: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Mortgage',
|
||||||
|
target: 1310.47,
|
||||||
|
spent: 1310.47,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{
|
{
|
||||||
|
@ -113,11 +145,11 @@ export default function Dashboard() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class='grid grid-rows-[1fr_auto] sm:grid-cols-2 sm:grid-rows-1 sm:grid-cols-[1fr_auto] h-[100vh]'>
|
<div class='grid grid-rows-[1fr_auto] sm:grid-cols-2 sm:grid-rows-1 sm:grid-cols-[1fr_auto] h-[100vh]'>
|
||||||
<main class='p-2'>
|
<main class='p-2 sm:pr-0 flex flex-col gap-2'>
|
||||||
This is the main content
|
{budgets.map((budget, _i) => <BudgetCard budget={budget} />)}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<nav class='w-full flex flex-1 sm:flex-col bg-mantle gap-1 p-1'>
|
<nav class='w-full flex flex-1 sm:flex-col sm:bg-bg gap-2 p-2'>
|
||||||
{navItems.map(({ text, ...props }, i) => (
|
{navItems.map(({ text, ...props }, i) => (
|
||||||
<IconButton
|
<IconButton
|
||||||
{...props}
|
{...props}
|
||||||
|
|
18
models.ts
18
models.ts
|
@ -16,3 +16,21 @@ export interface Transaction {
|
||||||
budget: string
|
budget: string
|
||||||
amount: number
|
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) + '%'
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue