Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show the impact of an expense on the active user's balance #139

Merged
merged 4 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node-postgres
{
"name": "spliit",
"dockerComposeFile": "docker-compose.yml",
"service": "app",

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {
// "ghcr.io/frntn/devcontainers-features/prism:1": {}
// },

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "cp container.env.example .env && npm install",
"postAttachCommand": {
"npm": "npm run dev"
},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or with the host.
"forwardPorts": [3000, 5432],
"portsAttributes": {
"3000": {
"label": "App"
},
"5432": {
"label": "PostgreSQL"
}
},

// Configure tool-specific properties.
"customizations": {
"codespaces": {
"openFiles": [
"README.md"
]
}
}

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
33 changes: 33 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: '3.8'

services:
app:
image: mcr.microsoft.com/devcontainers/typescript-node:latest

volumes:
- ../..:/workspaces:cached

# Overrides default command so things don't shut down after the process ends.
command: sleep infinity

# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
network_mode: service:db

# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)

db:
image: postgres:latest
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: 1234
POSTGRES_USER: postgres
POSTGRES_DB: postgres

# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)

volumes:
postgres-data:
6 changes: 6 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ const nextConfig = {
images: {
remotePatterns
},
// Required to run in a codespace (see https://github.com/vercel/next.js/issues/58019)
experimental: {
serverActions: {
allowedOrigins: ['localhost:3000'],
},
},
}

module.exports = nextConfig
43 changes: 43 additions & 0 deletions src/app/groups/[groupId]/expenses/active-user-balance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client'
import { Money } from '@/components/money'
import { getBalances } from '@/lib/balances'
import { useActiveUser } from '@/lib/hooks'

type Props = {
groupId: string
currency: string
expense: Parameters<typeof getBalances>[0][number]
}

export function ActiveUserBalance({ groupId, currency, expense }: Props) {
const activeUserId = useActiveUser(groupId)
if (activeUserId === null || activeUserId === '' || activeUserId === 'None') {
return null
}

const balances = getBalances([expense])
let fmtBalance = <>You are not involved</>
if (Object.hasOwn(balances, activeUserId)) {
const balance = balances[activeUserId]
let balanceDetail = <></>
if (balance.paid > 0 && balance.paidFor > 0) {
balanceDetail = (
<>
{' ('}
<Money {...{ currency, amount: balance.paid }} />
{' - '}
<Money {...{ currency, amount: balance.paidFor }} />
{')'}
</>
)
}
fmtBalance = (
<>
Your balance:{' '}
<Money {...{ currency, amount: balance.total }} bold colored />
{balanceDetail}
</>
)
}
return <div className="text-xs text-muted-foreground">{fmtBalance}</div>
}
4 changes: 4 additions & 0 deletions src/app/groups/[groupId]/expenses/expense-list.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use client'
import { ActiveUserBalance } from '@/app/groups/[groupId]/expenses/active-user-balance'
import { CategoryIcon } from '@/app/groups/[groupId]/expenses/category-icon'
import { Button } from '@/components/ui/button'
import { SearchBar } from '@/components/ui/search-bar'
Expand Down Expand Up @@ -151,6 +152,9 @@ export function ExpenseList({
</Fragment>
))}
</div>
<div className="text-xs text-muted-foreground">
<ActiveUserBalance {...{ groupId, currency, expense }} />
</div>
</div>
<div className="flex flex-col justify-between items-end">
<div
Expand Down
31 changes: 31 additions & 0 deletions src/components/money.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use client'
import { cn, formatCurrency } from '@/lib/utils'

type Props = {
currency: string
amount: number
bold?: boolean
colored?: boolean
}

export function Money({
currency,
amount,
bold = false,
colored = false,
}: Props) {
return (
<span
className={cn(
colored && amount <= 1
? 'text-red-600'
: colored && amount >= 1
? 'text-green-600'
: '',
bold && 'font-bold',
)}
>
{formatCurrency(currency, amount)}
</span>
)
}
Loading