Live demo: andrzejpudzisz.com/cennik-vet
- 💻 Built with Next.js 15 (App Router, Exported as static site)
- 🎨 Styled with Tailwind CSS 4
- 🧠 LocalStorage-based persistence (no backend)
- 📝 Rich transaction form with quantity, discounts, fees, and remarks
- 📄 PDF export (with full Polish locale support)
- 🔌 Offline support with PWA (via
next-pwa
) - ☁️ Works 100% offline (great for home visits)
- 📲 Suggests installation on mobile for quick "Add to Home Screen"
- 🌘 Dark mode with automatic theme detection
- 🧾 Combobox-based product & client selectors
- ⚡ Smart auto-saving for drafts
- ✅ Description field included in data & PDF
Cennik Vet is designed for individual veterinarians or small practices that need a simple, fast, and private way to handle transactions — without internet or external dependencies.
git clone https://github.com/andrew2630/cennik-vet.git
cd cennik-vet
npm install
npm run dev
Open http://localhost:3000/cennik-vet/
in your browser. You can override the
/cennik-vet
part by setting the BASE_PATH
environment variable before
starting the server:
BASE_PATH=/my-dev-path npm run dev
Then visit http://localhost:3000/my-dev-path/
.
npm run build
npm start
App is exported to the out/
folder and served statically. You can set a
different base path during the build step using BASE_PATH
:
BASE_PATH=/my-dev-path npm run build
The exported site will then expect to be hosted under that path.
Deploy via any static host:
- ✅ andrzejpudzisz.com/cennik-vet
- Vercel / Netlify / GitHub Pages (with routing config)
- Create a new Supabase project and note the
SUPABASE_URL
andSUPABASE_ANON_KEY
values. - In the project, create tables named
products
,clients
andtransactions
. Each table should contain all fields from the local models and auser_id
column of typeuuid
. - Enable Row Level Security on each table and add a policy to allow users to access rows where
user_id = auth.uid()
. - In your
.env
file expose these keys to the frontend:
NEXT_PUBLIC_SUPABASE_URL=<your url>
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your key>
Note: Admin actions such as deleting a user account require the Supabase service role key and cannot be performed directly from the browser. To enable account deletion, create a server-side endpoint (e.g. a Supabase Edge Function) that calls the Auth Admin API.
When you sign in from the Settings page the app will sync any queued changes to Supabase whenever you are online. Login, registration and logout events display toast messages so you know whether authentication succeeded.
Backups exported to JSON now include an exportedAt
timestamp. When you import a backup while logged in, this timestamp is used to immediately sync the restored data with Supabase.
Whenever data is downloaded from Supabase it is merged with your local storage using an updatedAt
timestamp so that the newest version of every product, client and transaction is kept.
If you created data before logging in, open Settings and use Include offline data to merge it with your account and sync.
.
├── components/ # UI components & custom logic
├── app/ # App router structure (Next.js)
├── utils/ # LocalStorage helpers
├── types/ # TypeScript shared types
├── public/ # Icons, manifest, etc.
└── ...
interface Transaction {
id: string;
clientId: string;
date: string;
items: TransactionItem[];
discount?:
| number
| { type: 'value'; value: number }
| { type: 'percentage'; value: number; scope: 'all' | 'no-travel' | 'services' | 'products' };
additionalFee?: number;
totalPrice: number;
status: 'draft' | 'finalised';
description?: string;
}
Q: Does this app send data anywhere?
A: No. All data stays in your browser via localStorage
. Each account (including when not logged in) uses its own storage so your data never mixes with other users.
Q: Can I use it offline? A: Yes! It’s a PWA — install it and it works offline like a native app.
Q: Where is the data saved? A: In your browser. You can clear or export it manually in the future.
Built by @andrew2630 Site: andrzejpudzisz.com