Skip to content

Commit 0ccec0c

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 7066583 + 29a353c commit 0ccec0c

File tree

17 files changed

+761
-194
lines changed

17 files changed

+761
-194
lines changed

.github/copilot-instructions.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ SplitPro is a **Next.js PWA** expense-splitting app (Splitwise alternative). Cor
1313

1414
### 1. **BigInt for All Financial Values**
1515
- **All amounts are stored and computed as `BigInt`, never floats** to prevent rounding errors
16-
- When displaying: convert via `toUIString()` (divides by 100 for cents)
17-
- When receiving input: convert via `toSafeBigInt()` (multiplies by 100)
18-
- `BigMath` utility provides safe arithmetic operations
19-
- Read: `README.md` FAQ and `jest.config.ts` for BigInt JSON serialization
16+
- Use `getCurrencyHelpers({ currency, locale })` from `src/utils/numbers.ts` to get currency-aware helpers:
17+
- `toUIString(value)` - Display BigInt as formatted currency (divides by 100 for cents)
18+
- `toSafeBigInt(input)` - Parse user input to BigInt (multiplies by 100)
19+
- Access these via `CurrencyHelpersContext` in React components
20+
- `BigMath` utility (`src/utils/numbers.ts`) provides safe arithmetic: `abs`, `sign`, `min`, `max`, `roundDiv`
21+
- **Jest setup**: `BigInt.prototype.toJSON` is defined in `jest.config.ts` for JSON serialization
2022

2123
### 2. **Double-Entry Balance Accounting**
2224
Every expense creates **two balance records** (bidirectional):
@@ -85,7 +87,22 @@ pnpm db:studio # Open Prisma Studio GUI
8587
pnpm db:seed # Seed database with dummy data
8688
```
8789

88-
**Pre-commit hooks** (Husky): prettier + oxlint auto-fixes. Override with `git commit --no-verify`.
90+
**Pre-commit hooks** (Husky):
91+
```bash
92+
pnpm lint-staged # Runs prettier on staged files
93+
pnpm tsgo --noEmit # TypeScript type checking without emit
94+
```
95+
Override with `git commit --no-verify` if needed.
96+
97+
## Environment Setup
98+
99+
Environment variables are validated via `@t3-oss/env-nextjs` in `src/env.ts`. Required variables:
100+
- **Database**: `DATABASE_URL` or separate `POSTGRES_*` vars
101+
- **Auth**: `NEXTAUTH_URL`, `NEXTAUTH_SECRET`, OAuth provider credentials (Google/Email SMTP)
102+
- **Storage**: R2/S3 credentials (`R2_*` or `AWS_S3_*` variables)
103+
- **Optional**: Bank integration (Plaid/GoCardless), currency rate API keys, push notification VAPID keys
104+
105+
For local dev: run `pnpm d` to start PostgreSQL + MinIO in Docker. Access MinIO console at `http://localhost:9001` (user: `splitpro`, pass: `password`) to create access keys.
89106

90107
## Data Flow Specifics
91108

@@ -113,6 +130,7 @@ GroupBalance(groupId, currency, userId, firendId) // note: typo "firendId"
113130
- **Currency Rates**: Pluggable providers (Frankfurter, OXR, NBP) in `currencyRateService.ts`
114131
- **i18n**: next-i18next with Weblate; always add English keys first, let community translate
115132
- **Dates**: Use `date-fns` for formatting/parsing, store all dates in UTC
133+
- **PWA**: Serwist service worker (`worker/index.ts``public/sw.js`), disabled in dev mode
116134

117135

118136
## Avoiding Common Pitfalls
@@ -240,3 +258,5 @@ The backend uses **tRPC** for type-safe API communication. Routers are in `src/s
240258
Always use context7 when I need code generation, setup or configuration steps, or
241259
library/API documentation. This means you should automatically use the Context7 MCP
242260
tools to resolve library id and get library docs without me having to explicitly ask.
261+
262+
Do not generate documentation or tests if not explicitly requested.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ We are grateful for the support of our sponsors.
149149
<a href="https://github.com/igorrrpawlowski"><img src="https:&#x2F;&#x2F;github.com&#x2F;igorrrpawlowski.png" width="60px" alt="User avatar: igorrrpawlowski" /></a>
150150
<a href="https://github.com/probeonstimpack"><img src="https:&#x2F;&#x2F;github.com&#x2F;probeonstimpack.png" width="60px" alt="User avatar: Marcel Szmeterowicz" /></a>
151151
<a href="https://github.com/mexicanhatman"><img src="https://avatars.githubusercontent.com/u/78694887?v=4" width="60px" alt="User avatar: mexicanhatman" /></a>
152+
<a href="https://github.com/felixdz"><img src="https://avatars.githubusercontent.com/u/11851415?v=4" width="60px" alt="User avatar: FelixDz" /></a>
152153

153154
## Star History
154155

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
CREATE VIEW
2+
"public"."BalanceView" AS
3+
-- Use a Common Table Expression (CTE) to calculate the "one-way" balances.
4+
-- This query is based on YOUR schema where `ep.amount < 0` is a debt.
5+
WITH
6+
"BaseBalance" AS (
7+
SELECT
8+
CASE
9+
WHEN ep."userId" < e."paidBy" THEN ep."userId"
10+
ELSE e."paidBy"
11+
END AS "user_id_A", -- The person who owes (debtor)
12+
CASE
13+
WHEN ep."userId" < e."paidBy" THEN e."paidBy"
14+
ELSE ep."userId"
15+
END AS "user_id_B", -- The person who is owed (creditor)
16+
e."groupId",
17+
e.currency,
18+
SUM(
19+
ep."amount" * (
20+
CASE
21+
WHEN ep."userId" < e."paidBy"
22+
-- A=participant, B=payer. A owes B. ep.amount is < 0. We need a NEGATIVE result.
23+
THEN 1
24+
-- B=participant, A=payer. B owes A. ep.amount is < 0. We need a POSITIVE result.
25+
ELSE -1
26+
END
27+
)
28+
) AS "net_amount",
29+
MIN(e."createdAt") AS "createdAt",
30+
MAX(e."updatedAt") AS "updatedAt"
31+
FROM
32+
"public"."ExpenseParticipant" AS ep
33+
JOIN "public"."Expense" AS e ON ep."expenseId" = e."id"
34+
WHERE
35+
-- A user can't owe themselves
36+
ep."userId" != e."paidBy"
37+
AND e."deletedAt" IS NULL
38+
GROUP BY
39+
-- Note: We group by the original columns
40+
"user_id_A",
41+
"user_id_B",
42+
e."groupId",
43+
e.currency
44+
)
45+
-- Query 1: The debtor's perspective (e.g., User 5, Friend 10, Amount -100)
46+
-- This shows what "userId" owes to "friendId"
47+
SELECT
48+
"user_id_A" AS "userId",
49+
"user_id_B" AS "friendId",
50+
"groupId",
51+
currency,
52+
"net_amount" AS amount,
53+
"createdAt",
54+
"updatedAt"
55+
FROM
56+
"BaseBalance"
57+
UNION ALL
58+
-- Query 2: The creditor's perspective (e.g., User 10, Friend 5, Amount +100)
59+
-- This shows what "userId" (the creditor) is owed by "friendId" (the debtor)
60+
SELECT
61+
"user_id_B" AS "userId", -- Swapped
62+
"user_id_A" AS "friendId", -- Swapped
63+
"groupId",
64+
currency,
65+
-- Invert the sign to show a positive balance (is owed)
66+
- ("net_amount") AS amount,
67+
"createdAt",
68+
"updatedAt"
69+
FROM
70+
"BaseBalance";
71+
72+
-- We may want to migrate this to a materialized view in the future for performance reasons.
73+
-- CREATE UNIQUE INDEX ON "public"."BalanceView" ("userId", "friendId", "groupId", currency);
74+
-- CREATE INDEX ON "public"."BalanceView" ("userId");

0 commit comments

Comments
 (0)