https://github.com/vseventer/pelican
Pelican: a medical record tracker for pets.
https://github.com/vseventer/pelican
Last synced: 7 months ago
JSON representation
Pelican: a medical record tracker for pets.
- Host: GitHub
- URL: https://github.com/vseventer/pelican
- Owner: vseventer
- Created: 2025-06-16T19:41:35.000Z (7 months ago)
- Default Branch: master
- Last Pushed: 2025-06-23T18:33:17.000Z (7 months ago)
- Last Synced: 2025-06-23T19:36:42.480Z (7 months ago)
- Language: TypeScript
- Homepage:
- Size: 682 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Pelican
> An MVP-app for tracking your pet's medical records.
## Getting Started
Development environment:
1. `npm run install`
2. `npm run start`
Production environment:
1. `npm run install`
2. `npm run preview`
## Architecture
The basic app scaffolding and running took under 30 minutes.
- **[TanStack Start](https://tanstack.com/start)** to rapidly bootstrap the app without manually configuring:
- API layer
- React setup
- Routing
- 404 and global error handling
- While still in beta, it's ideal for MVP-level experimentation and accelerates development.
## Tooling & Core Libraries
- **Linting**: Added standard linting setup with default configurations.
- **ORM**: Selected **[Drizzle ORM](https://orm.drizzle.team/)** for database access. While I have no experience with Drizzle, I am excited to try it out hoping it's more lightweight and simpler than Prisma (which felt too heavy for MVP scope).
- **Forms**: Added **react-hook-form** to manage form state and validation in a declarative and performant way.
- **UI**: Integrated **Tailwind CSS** for consistent design and rapid UI development.
## Features
- Admin users can:
- View **soft-deleted records** (e.g., historical allergy or vaccine data that was removed)
- See full medical history for any pet, even past modifications
- Users and admins can:
- Add new **pets**, including selecting type.
- Create new **animals**, **allergies**, and **vaccines** on the fly — despite the normalized structure
- This is aided by unique constraints at the DB level to prevent duplicates
- In a production app, this would require additional validation and administrative workflows
## Database Schema
**Normalization & Modularity**:
- The schema is well-normalized:
- Animals and vaccines are decoupled, allowing fine-grained control of vaccine schedules per species.
- Pets link to users and animals, maintaining clear separation between general species-level data and individual pets.
- Vaccine records link to the `animal_vaccines` table rather than vaccines directly — enforcing correct pairings and supporting animal-specific schedules.
**Auditability:**
- Every table includes `createdAt`, and key medical records use a `deletedAt` timestamp to enable soft-deletion.
- This makes it possible to audit the full historical medical trail of any pet, even if records are “removed” from the user’s perspective.
**Extensibility:**
- By modeling vaccine applicability with the `animal_vaccines` join table, the schema supports future logic like showing overdue vaccinations based on species.
- You could easily extend this to support vaccine frequency or schedule data (e.g., interval_days).
**Data Integrity:**
- All foreign keys are explicitly enforced.
- Unique constraints on names (e.g., animals, allergies, vaccines) help reduce redundancy even when user-created entries are allowed.
- More constraints can be added - see section on improvements.
**UX Considerations:**
- Pet owners can freely add new allergy/vaccine names, with constraints helping dedupe at the DB level — a good trade-off between MVP simplicity and data cleanliness.
> [!NOTE]
>
> - All history is traceable. Nothing is permanently deleted.
> - The GitHub repo includes a pre-built SQLite file with basic sample data.
> - The `db/` folder contains the seed script. There's also `npm run seed` if you want to re-create your local database (does require some dependencies on your machine).
## Front-End
The UI consists of header (medical card with company name), sidebar, main section, and footer. On smaller viewports, the sidebar and main content are stacked instead. The pages include:
- **Landing Page** (unauthenticated):
- Lists all users
- Allows creation of a new user
- **Home Page** (authenticated):
- Lists pets for the current user on the left sidebar
- Clicking a pet opens the detailed view
- **Details Page**:
- Shows pet info, including allergies and vaccines
- Allows adding new allergies and vaccine records
## Back-End
API routes follow REST principles and are co-located with the front-end using TanStack Start’s file-based routing. Major endpoints include:
- **Users**
- `GET /api/users` — List all users
- `GET /api/users/:id` — Get a specific user
- `POST /api/users` — Create a new user
- **Pets**
- `GET /api/pets` — Admin: List all pets
- `GET /api/pets/:id` — Get a specific pet
- `DELETE`, `POST /api/pets/:id/allergy` - Manage a pet's allergies
- `DELETE`, `POST /api/pets/:id/vaccine` - Manage a pet's vaccines
- `POST /api/pets` — Create new pet
- **Validation**
- All data mutating endpoints use **Zod** for schema validation
- Shared schemas between front-end and back-end ensure consistency and type safety
## Polish
The following small items were accomplished to give the MVP a bit more character:
- Name. Pelican sounds like a bit like "Medical", "Can", and starts with "Pe" from pet.
- Logo. Yes, behind the scene it's all Dr. Pelican Ph.D. keeping the records.
- Documentation was assisted by ChatGPT, which helped structure and write readable summaries based on design and architectural decisions.
## Findings
- My unfamiliarity with **Drizzle** and **TanStack Start** slowed me down:
- While I know SQL well, I found Drizzle less productive for writing SQL-like logic programmatically. I’ll look for alternatives next time.
- TanStack Start provides a lot out-of-the-box — possibly more than this MVP required.
- Clean TypeScript usage took time, especially aligning inferred and explicit types. There’s much more that could be typed, but I had to draw the line.
- Setting up a basic query-string-based **Auth** took longer than expected.
- Added proper `NotFound` and global error pages took a bit of time, but add a lot of value.
- Implemented both frontend and backend **validation** to ensure input sanitation and avoid malicious data insertion.
## Future Improvements
There's a number of items listed below where improvements can be made.
- **Database Schema**:
- Additional constraints should be considered (an allergy is probably unique to a pet, we shouldn't be able to record two identical allergies).
- When entering a custom vaccine, if it already exists in vaccines but not in the animal-vaccine linking table, this currently is an edge case that errors out.
- **Build Optimization**:
- Remove unused CSS
- Add bundle splitting and optimize image assets for production
- **UI Enhancements**:
- Replace inline editing with modals for more structured workflows
- Enable deeplinking to modals to create new records (for better navigation and shareability)
- Extract repeated styles (e.g. inputs) to common components so that they can easily be reused.
- **Testing**:
- Add basic UI tests for core flows (authentication, adding medical records)
- Add backend tests to validate input handling and access control logic (e.g., authorization checks)