Skip to content

Commit

Permalink
show total coins in header, add pagination on coin transaction (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
dohsimpson authored Jan 4, 2025
1 parent fadf33e commit 86a517a
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 56 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Version 0.1.12

### Added

- show total coins in header
- pagination for coin transactions history

## Version 0.1.11

### Added
Expand Down
172 changes: 119 additions & 53 deletions components/CoinsManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default function CoinsManager() {
const [settings] = useAtom(settingsAtom)
const DEFAULT_AMOUNT = '0'
const [amount, setAmount] = useState(DEFAULT_AMOUNT)
const [pageSize, setPageSize] = useState(50)
const [currentPage, setCurrentPage] = useState(1)

const handleAddRemoveCoins = async () => {
const numAmount = Number(amount)
Expand Down Expand Up @@ -143,77 +145,141 @@ export default function CoinsManager() {

<Card className="md:col-span-2">
<CardHeader>
<CardTitle className="flex justify-between items-center">
<span>Transaction History</span>
<span className="text-sm font-normal text-muted-foreground">
Total: {transactions.length} transactions
</span>
</CardTitle>
<CardTitle>Transaction History</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex justify-between items-center mb-4">
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">Show:</span>
<select
className="border rounded p-1"
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value))
setCurrentPage(1) // Reset to first page when changing page size
}}
>
<option value={50}>50</option>
<option value={100}>100</option>
<option value={500}>500</option>
</select>
<span className="text-sm text-muted-foreground">entries</span>
</div>
<div className="text-sm text-muted-foreground">
Showing {Math.min((currentPage - 1) * pageSize + 1, transactions.length)} to {Math.min(currentPage * pageSize, transactions.length)} of {transactions.length} entries
</div>
</div>

{transactions.length === 0 ? (
<EmptyState
icon={History}
title="No transactions yet"
description="Your transaction history will appear here once you start earning or spending coins"
/>
) : (
transactions.map((transaction) => {
const getBadgeStyles = () => {
switch (transaction.type) {
case 'HABIT_COMPLETION':
return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100'
case 'HABIT_UNDO':
return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100'
case 'WISH_REDEMPTION':
return 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-100'
case 'MANUAL_ADJUSTMENT':
return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-100'
default:
return 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100'
}
}
<>
{transactions
.slice((currentPage - 1) * pageSize, currentPage * pageSize)
.map((transaction) => {
const getBadgeStyles = () => {
switch (transaction.type) {
case 'HABIT_COMPLETION':
return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100'
case 'HABIT_UNDO':
return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100'
case 'WISH_REDEMPTION':
return 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-100'
case 'MANUAL_ADJUSTMENT':
return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-100'
default:
return 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-100'
}
}

return (
<div
key={transaction.id}
className="flex justify-between items-center p-3 border rounded hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
<div className="space-y-1">
<div className="flex items-center gap-2">
{transaction.relatedItemId ? (
<Link
href={`${transaction.type === 'WISH_REDEMPTION' ? '/wishlist' : '/habits'}?highlight=${transaction.relatedItemId}`}
className="font-medium hover:underline"
scroll={true}
>
{transaction.description}
</Link>
) : (
<p className="font-medium">{transaction.description}</p>
)}
return (
<div
key={transaction.id}
className="flex justify-between items-center p-3 border rounded hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
<div className="space-y-1">
<div className="flex items-center gap-2">
{transaction.relatedItemId ? (
<Link
href={`${transaction.type === 'WISH_REDEMPTION' ? '/wishlist' : '/habits'}?highlight=${transaction.relatedItemId}`}
className="font-medium hover:underline"
scroll={true}
>
{transaction.description}
</Link>
) : (
<p className="font-medium">{transaction.description}</p>
)}
<span
className={`text-xs px-2 py-1 rounded-full ${getBadgeStyles()}`}
>
{transaction.type.split('_').join(' ')}
</span>
</div>
<p className="text-sm text-gray-500">
{d2s({ dateTime: t2d({ timestamp: transaction.timestamp, timezone: settings.system.timezone }), timezone: settings.system.timezone })}
</p>
</div>
<span
className={`text-xs px-2 py-1 rounded-full ${getBadgeStyles()}`}
className={`font-mono ${transaction.amount >= 0
? 'text-green-600 dark:text-green-400'
: 'text-red-600 dark:text-red-400'
}`}
>
{transaction.type.split('_').join(' ')}
{transaction.amount >= 0 ? '+' : ''}{transaction.amount}
</span>
</div>
<p className="text-sm text-gray-500">
{d2s({ dateTime: t2d({ timestamp: transaction.timestamp, timezone: settings.system.timezone }), timezone: settings.system.timezone })}
</p>
)
})}

<div className="flex justify-center items-center gap-4 mt-6">
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setCurrentPage(1)}
disabled={currentPage === 1}
>
«
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
disabled={currentPage === 1}
>
</Button>
<div className="flex items-center gap-1 px-4 py-2 rounded-md bg-muted">
<span className="text-sm font-medium">Page</span>
<span className="text-sm font-bold">{currentPage}</span>
<span className="text-sm font-medium">of</span>
<span className="text-sm font-bold">{Math.ceil(transactions.length / pageSize)}</span>
</div>
<span
className={`font-mono ${transaction.amount >= 0
? 'text-green-600 dark:text-green-400'
: 'text-red-600 dark:text-red-400'
}`}
<Button
variant="outline"
size="sm"
onClick={() => setCurrentPage(prev => Math.min(Math.ceil(transactions.length / pageSize), prev + 1))}
disabled={currentPage >= Math.ceil(transactions.length / pageSize)}
>
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setCurrentPage(Math.ceil(transactions.length / pageSize))}
disabled={currentPage >= Math.ceil(transactions.length / pageSize)}
>
{transaction.amount >= 0 ? '+' : ''}{transaction.amount}
</span>
»
</Button>
</div>
)
})
</div>
</>
)}
</div>
</CardContent>
Expand Down
11 changes: 9 additions & 2 deletions components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import { useState } from 'react'
import { useAtom } from 'jotai'
import { settingsAtom } from '@/lib/atoms'
import { Bell, Menu, Settings, User, Info } from 'lucide-react'
import { settingsAtom, coinsAtom } from '@/lib/atoms'
import { Bell, Menu, Settings, User, Info, Coins } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Logo } from '@/components/Logo'
import {
Expand All @@ -23,6 +23,7 @@ interface HeaderProps {
export default function Header({ className }: HeaderProps) {
const [showAbout, setShowAbout] = useState(false)
const [settings] = useAtom(settingsAtom)
const [coins] = useAtom(coinsAtom)
return (
<>
<header className={`border-b bg-white dark:bg-gray-800 shadow-sm ${className || ''}`}>
Expand All @@ -32,6 +33,12 @@ export default function Header({ className }: HeaderProps) {
<Logo />
</Link>
<div className="flex items-center gap-2">
<Link href="/coins" className="flex items-center gap-1 px-2 py-1 bg-amber-100 hover:bg-amber-200 dark:bg-amber-900 dark:hover:bg-amber-800 rounded-full transition-colors">
<Coins className="text-amber-500 dark:text-amber-300" />
<span className="text-amber-600 dark:text-amber-400 font-medium">
{coins.balance}
</span>
</Link>
<Button variant="ghost" size="icon" aria-label="Notifications">
<Bell className="h-5 w-5" />
</Button>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "habittrove",
"version": "0.1.11",
"version": "0.1.12",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
Expand Down

0 comments on commit 86a517a

Please sign in to comment.