feat: project foundation - Go module, PostgreSQL Docker, React/TS frontend

- Initialize Go module with standard layout (cmd/api/, internal/, pkg/, migrations/)
- Add Makefile with build, run, test, lint, migrate targets
- Add health check endpoint (/healthz)
- Add config package with env-based configuration and tests
- Set up PostgreSQL 16 with Docker Compose for local dev
- Add .env.example with all required variables
- Initialize React 19 + TypeScript + Vite frontend in web/
- Configure TailwindCSS v4 with CSS-first config
- Set up ESLint + Prettier with typescript-eslint
- Configure path aliases (@/* -> src/*)
- Add BaseLayout component
- Add README with project structure and setup instructions

Closes #32, closes #33, closes #34
This commit is contained in:
2026-02-22 20:15:19 -06:00
parent f857f56365
commit fca56ff8aa
33 changed files with 4949 additions and 0 deletions

14
.env.example Normal file
View File

@@ -0,0 +1,14 @@
# =============================================================================
# PulseScore Environment Configuration
# =============================================================================
# Copy this file to .env and fill in the values.
# Server
PORT=8080
ENVIRONMENT=development
# PostgreSQL
POSTGRES_USER=pulsescore
POSTGRES_PASSWORD=pulsescore
POSTGRES_DB=pulsescore_dev
DATABASE_URL=postgres://pulsescore:pulsescore@localhost:5432/pulsescore_dev?sslmode=disable

3
.gitignore vendored
View File

@@ -171,6 +171,9 @@ go.work.sum
# env file
.env
# Go build output
bin/
# Editor/IDE
# .idea/
# .vscode/

33
Makefile Normal file
View File

@@ -0,0 +1,33 @@
.PHONY: build run test lint clean migrate-up migrate-down
BINARY_NAME=pulsescore-api
BUILD_DIR=bin
build:
go build -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/api
run: build
./$(BUILD_DIR)/$(BINARY_NAME)
test:
go test ./... -v -race
lint:
go vet ./...
clean:
rm -rf $(BUILD_DIR)
migrate-up:
@echo "Run migrations up (requires golang-migrate)"
migrate -path migrations -database "$(DATABASE_URL)" up
migrate-down:
@echo "Run migrations down (requires golang-migrate)"
migrate -path migrations -database "$(DATABASE_URL)" down
dev-db:
docker compose -f docker-compose.dev.yml up -d
dev-db-down:
docker compose -f docker-compose.dev.yml down

98
README.md Normal file
View File

@@ -0,0 +1,98 @@
# PulseScore
Customer health scoring platform for B2B SaaS companies. Connect Stripe, HubSpot, and Intercom to monitor customer health with automated scoring.
## Project Structure
```
pulse-score/
├── cmd/api/ # Application entry point
│ └── main.go
├── internal/ # Private application code
│ ├── config/ # Configuration management
│ ├── handler/ # HTTP handlers
│ ├── middleware/ # HTTP middleware
│ ├── model/ # Domain models
│ ├── repository/ # Database access layer
│ └── service/ # Business logic
├── pkg/ # Reusable packages
├── migrations/ # Database migrations
├── scripts/
│ └── init-db/ # PostgreSQL initialization scripts
├── web/ # React/TypeScript frontend (Vite + TailwindCSS v4)
│ └── src/
│ └── components/
├── docker-compose.dev.yml
├── Makefile
└── .env.example
```
## Tech Stack
- **Backend:** Go (net/http)
- **Frontend:** React 19, TypeScript, Vite, TailwindCSS v4
- **Database:** PostgreSQL 16
- **Deployment:** Docker, Nginx, VPS
## Prerequisites
- Go 1.24+
- Node.js 20+
- Docker & Docker Compose
## Getting Started
### 1. Clone and configure
```bash
cp .env.example .env
```
### 2. Start the database
```bash
docker compose -f docker-compose.dev.yml up -d
```
### 3. Run the API
```bash
make run
```
The API starts on http://localhost:8080. Health check: `GET /healthz`
### 4. Run the frontend
```bash
cd web
npm install
npm run dev
```
The frontend starts on http://localhost:5173.
## Development Commands
### Backend (Makefile)
| Command | Description |
| ------------------ | ------------------------------ |
| `make build` | Build the Go binary |
| `make run` | Build and run the API |
| `make test` | Run tests with race detection |
| `make lint` | Run `go vet` |
| `make dev-db` | Start development PostgreSQL |
| `make dev-db-down` | Stop development PostgreSQL |
| `make migrate-up` | Run database migrations up |
| `make migrate-down`| Roll back database migrations |
### Frontend (web/)
| Command | Description |
| ------------------- | ----------------------------- |
| `npm run dev` | Start Vite dev server |
| `npm run build` | Production build |
| `npm run lint` | ESLint check |
| `npm run format` | Format with Prettier |
| `npm run preview` | Preview production build |

34
cmd/api/main.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import (
"fmt"
"log"
"net/http"
"os"
"github.com/onnwee/pulse-score/internal/config"
)
func main() {
cfg := config.Load()
mux := http.NewServeMux()
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"status":"ok"}`)
})
addr := fmt.Sprintf(":%s", cfg.Port)
log.Printf("Starting PulseScore API on %s", addr)
srv := &http.Server{
Addr: addr,
Handler: mux,
}
if err := srv.ListenAndServe(); err != nil {
log.Printf("Server error: %v", err)
os.Exit(1)
}
}

187
dev-plan.md Normal file
View File

@@ -0,0 +1,187 @@
Plan: PulseScore Development Roadmap via GitHub Issues
Create ~211 task issues organized under 23 epic issues and 1 master roadmap issue in subculture-collective/pulse-score. Each sub-issue is scoped for a coding agent with detailed descriptions, goals, and acceptance criteria. The master roadmap lists every issue in dependency-respecting chronological order (flat, not grouped by epic). All hosting targets your VPS with Docker + Nginx + SSL (no Vercel).
Structure
1 Roadmap issue — flat chronological list of all ~211 issues
23 Epic issues — each lists its child issues in the body
~211 Task issues — each has description, goals, technical details, acceptance criteria, dependencies
Labels: phase:mvp, phase:expansion, phase:defensibility, type:epic, type:task, area:backend, area:frontend, area:infra, area:docs, area:integration, priority:critical/high/medium/low
Milestones: Phase 1: MVP, Phase 2: Expansion, Phase 3: Defensibility
Issue Template (each sub-issue follows this)
Description — What to build and why, with technical context
Goals — Specific deliverables
Technical Details — Files to create, packages to use, patterns to follow
Acceptance Criteria — Testable checkboxes including "tests written and passing"
Dependencies — Blocked by #N links
PHASE 1: MVP (15 Epics, ~143 issues)
E1: Project Foundation & Infrastructure (14 issues)
Initialize Go module with standard layout (cmd/api/, internal/, pkg/, Makefile)
Set up PostgreSQL with Docker Compose for local dev
Initialize React/TypeScript project (Vite + TailwindCSS v4)
Set up Go HTTP server with Chi router + base middleware (logging, recovery, request ID)
Set up database migration framework (golang-migrate)
Configure environment variable management (config package, .env.example)
Set up GitHub Actions CI (Go lint/test/build, React lint/test/build)
Create multi-stage Dockerfile for Go API
Create Dockerfile for React frontend with Nginx (SPA-routing)
Create production Docker Compose (PostgreSQL + API + frontend)
Set up Nginx reverse proxy with SSL (Let's Encrypt/Certbot)
Create VPS deployment scripts (initial setup + deploy command)
Implement health check endpoints (/healthz, /readyz)
Configure CORS, security headers, and rate limiting middleware
E2: Database Schema & Migrations (10 issues)
Organizations table migration
Users + user_organizations join table migration
Integration_connections table migration (OAuth tokens, encrypted)
Customers table migration (tenant-scoped, external_id, MRR, metadata JSONB)
Customer_events table migration (event log, typed, timestamped)
Health_scores + health_score_history tables migration
Stripe-specific data tables (subscriptions, payments)
Alert_rules + alert_history tables migration
Database seed script (1 org, 3 users, 50 customers, events, scores)
Database connection pool + query helpers (pgxpool, repository pattern)
E3: Authentication & Multi-tenancy (15 issues)
User registration endpoint (bcrypt, email validation, creates org)
User login endpoint (JWT access + refresh tokens)
JWT authentication middleware
Refresh token endpoint
Multi-tenant isolation middleware (org_id scoping)
Organization creation flow (auto on registration)
Team member invitation endpoint (generate token, RBAC-gated)
Invite email sending via SendGrid
Invite acceptance endpoint (create/link user to org)
RBAC middleware (owner/admin/member)
Password reset flow (request + completion endpoints)
User profile API (GET/PATCH)
Login page (React)
Registration page (React)
Auth state management + protected routes (React context, token refresh)
E4: Stripe Data Integration (12 issues)
Stripe OAuth connect flow backend (initiate + callback)
Stripe connection UI in settings (React)
Stripe customer data sync service
Stripe subscription data sync service
Stripe payment/invoice data sync service
Stripe webhook handler (signature verification, event routing)
MRR calculation service (normalize annual → monthly)
Failed payment detection and tracking
Payment recency calculation per customer
Initial data sync orchestrator (full sync on connect)
Incremental sync scheduler (periodic delta updates)
Connection monitoring + error handling (retry, status updates)
E5: Health Scoring Engine (11 issues)
Scoring configuration model + migration (weights, thresholds, per-org)
Payment recency scoring factor (configurable decay curve)
MRR trend scoring factor (growth/decline over time windows)
Failed payment history scoring factor
Support ticket volume scoring factor (normalized)
Engagement/activity scoring factor (event frequency, graceful degradation)
Weighted score aggregation service (handles missing factors)
Score recalculation scheduler (batch + event-triggered)
Score change detection + history tracking
Customer risk categorization (green/yellow/red thresholds)
Score configuration admin API (GET/PUT with validation)
E6: REST API Layer (10 issues)
Customer list endpoint (paginated, sortable, filterable by risk/search)
Customer detail endpoint (profile + score factors + subscriptions)
Customer timeline/events endpoint (paginated, filterable)
Dashboard summary stats endpoint (customer count, at-risk, MRR, trends)
Health score distribution endpoint (histogram/risk breakdown)
Integration management endpoints (list/disconnect/sync-status)
Organization settings endpoints (GET/PATCH)
User management endpoints (list/update-role/remove members)
Alert rules CRUD endpoints
OpenAPI/Swagger documentation generation
E7: Dashboard Core UI (17 issues)
App shell + sidebar navigation
Responsive navigation (collapsible sidebar, mobile hamburger)
Dashboard overview page with summary stat cards
Health score distribution chart (Recharts)
MRR trend chart (line chart, time range selector)
Customer list page with data table (sort, filter, paginate)
Health score badge/indicator component (reusable, color-coded)
Customer search + filter controls (URL-synced)
Customer detail page layout (header, tabs/sections)
Customer event timeline component
Customer health score history chart
Integration status indicators
Settings page with tabs (Org, Profile, Integrations, Scoring, Billing, Team)
Toast/notification system
Loading skeletons + empty state components
Error boundary + 404 page
Dark mode / theme toggle
E8: HubSpot Integration (8 issues) — OAuth, contact/deal/company sync, data enrichment, webhooks, incremental sync
E9: Intercom Integration (7 issues) — OAuth, conversation/contact sync, ticket volume metrics, webhooks, incremental sync
E10: Email Alerts (8 issues) — SendGrid setup, email templates, alert rule evaluation engine, score drop triggers, alert UI, delivery tracking, preferences
E11: Onboarding Wizard (7 issues) — Multi-step wizard shell, welcome/org setup step, Stripe/HubSpot/Intercom connection steps, "generating scores" preview step, tracking + resume
E12: Billing & Subscription (9 issues) — Stripe billing products setup, checkout via Stripe Checkout, billing webhook handler, subscription tracking, feature gating middleware (Free: 10 customers/1 integration → Scale: unlimited), pricing page, checkout flow, subscription management, Customer Portal redirect
E13: Landing Page (6 issues) — Hero with CTA, features section, pricing comparison, social proof, footer, SEO meta/OG tags + sitemap/robots.txt
E14: Documentation (5 issues) — API reference, quickstart guide, Stripe setup guide, HubSpot/Intercom guides, scoring methodology docs
E15: Stripe App Marketplace (4 issues) — Requirements research, manifest packaging, app install flow, marketplace listing content
PHASE 2: EXPANSION (4 Epics, ~36 issues)
E16: Automated Playbooks (10 issues) — Data model, CRUD API, visual builder UI, trigger: score threshold, trigger: customer event, action: email customer, action: internal alert, action: tag customer, execution engine, execution history UI
E17: Team Collaboration (8 issues) — Account assignment API + UI, customer notes API + UI, team activity feed API + page, @mentions + notifications, account handoff workflow
E18: Additional Integrations (12 issues) — Generic connector interface, Zendesk (OAuth + sync + UI), Salesforce (OAuth + sync + UI), PostHog (API key + sync + UI), generic webhook receiver + config UI, integration health monitoring dashboard
E19: Advanced Pricing & Feature Gating (6 issues) — Growth/Scale tier definitions, granular feature flags system, tier-based gating, upgrade prompts, usage analytics tracking
PHASE 3: DEFENSIBILITY (4 Epics, ~28 issues)
E20: Anonymized Benchmarking (7 issues) — Data model + anonymization pipeline, aggregation service, benchmark calculation (percentiles), comparison dashboard, industry classification, privacy controls, insight notifications
E21: AI-Powered Insights (7 issues) — LLM integration service (GPT-4o-mini), prompt template design, per-customer analysis pipeline, insights UI on detail page, action recommendations, cost tracking + rate limiting, dashboard summary insights
E22: Predictive Churn Models (6 issues) — Feature engineering pipeline, training data preparation, churn model implementation, prediction scoring service, churn indicators in UI, churn forecast dashboard
E23: Integration Marketplace (8 issues) — Connector SDK design, registration/discovery API, marketplace browse UI, installation flow, developer docs + example, community submission, review pipeline, connector analytics
Chronological Order (Master Roadmap)
The roadmap issue lists all ~211 issues flat, ordered by dependency chains:
Order Batch Issues
1-14 Foundation Go setup → React setup (parallel) → PostgreSQL → Chi server → env config → migrations → health checks → CORS/security → DB pool
15-23 Schema All E2 table migrations in order → seed script
24-38 Auth Registration → login → JWT middleware → refresh → multi-tenant → org creation → RBAC → profile → password reset → invitations → frontend auth pages + state management
39-44 CI/CD & Deploy GitHub Actions → Dockerfiles → production compose → Nginx + SSL → VPS deploy scripts (parallel with auth)
45-56 Stripe Integration OAuth → connection UI → customer/subscription/payment sync → webhooks → MRR calc → failed payments → recency → sync orchestrator → scheduler → monitoring
57-67 Health Scoring Config model → 5 scoring factors → aggregation → risk categorization → scheduler → change detection → config API
68-77 API Layer All REST endpoints → OpenAPI docs
78-94 Dashboard UI Shell → nav → shared components → dashboard page → charts → customer list → detail → timeline → settings → dark mode
95-109 HubSpot + Intercom Both integration flows (parallel)
110-117 Email Alerts SendGrid → templates → evaluation → triggers → UI → tracking → preferences
118-124 Onboarding Wizard shell → steps → score generation → tracking
125-133 Billing Products → checkout → webhooks → tracking → gating → pricing page → management
134-139 Landing Page Hero → features → pricing → social proof → footer → SEO
140-144 Docs API ref → quickstart → integration guides → scoring docs
145-148 Stripe Marketplace Research → package → install flow → listing
149-184 Expansion Playbooks → Team Collaboration → Additional Integrations → Advanced Pricing
185-211 Defensibility Benchmarking → AI Insights → Predictive Models → Integration Marketplace
Decisions
VPS deployment (not Vercel) — Docker Compose + Nginx + Certbot on user's VPS
Tests baked in — Each sub-issue acceptance criteria includes "tests written and passing"
Tech stack: Go (Chi) + React/TypeScript (Vite/Tailwind) + PostgreSQL — per the plan doc
All 3 phases covered — MVP through defensibility, ~211 issues total
Issue scoping: Each sub-issue targets 1-4 hours of focused coding agent work with clear inputs/outputs
Further Considerations
Password reset + invite emails: The plan references SendGrid for alerts, but auth emails (password reset, invites) are needed before the alerts epic. Recommendation: Set up SendGrid basics in E3-8 (invite email) and reuse in E10. Already reflected in the plan.
Stripe Connect vs Stripe OAuth: The plan mentions "Stripe Connect" but the use case is reading customer data, not processing payments on behalf. Recommendation: Use Stripe OAuth (standard) for reading data, and a separate Stripe account (direct) for PulseScore's own billing. This is how it's structured in E4 (data) vs E12 (billing).
Frontend component library: No specific UI component library is mentioned. Recommendation: Use shadcn/ui (TailwindCSS-native, widely supported) or keep it custom with Tailwind. This can be decided in E1-3 (React project setup). Which do you prefer?

26
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,26 @@
services:
postgres:
image: postgres:16-alpine
container_name: pulsescore-postgres
restart: unless-stopped
ports:
- '5432:5432'
environment:
POSTGRES_USER: ${POSTGRES_USER:-pulsescore}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-pulsescore}
POSTGRES_DB: ${POSTGRES_DB:-pulsescore_dev}
volumes:
- pgdata:/var/lib/postgresql/data
- ./scripts/init-db:/docker-entrypoint-initdb.d
healthcheck:
test:
[
'CMD-SHELL',
'pg_isready -U ${POSTGRES_USER:-pulsescore} -d ${POSTGRES_DB:-pulsescore_dev}',
]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/onnwee/pulse-score
go 1.24.3

26
internal/config/config.go Normal file
View File

@@ -0,0 +1,26 @@
package config
import "os"
// Config holds application configuration.
type Config struct {
Port string
DatabaseURL string
Environment string
}
// Load reads configuration from environment variables with sensible defaults.
func Load() *Config {
return &Config{
Port: getEnv("PORT", "8080"),
DatabaseURL: getEnv("DATABASE_URL", "postgres://pulsescore:pulsescore@localhost:5432/pulsescore_dev?sslmode=disable"),
Environment: getEnv("ENVIRONMENT", "development"),
}
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}

View File

@@ -0,0 +1,40 @@
package config
import (
"os"
"testing"
)
func TestLoadDefaults(t *testing.T) {
// Unset any env vars that might be set
os.Unsetenv("PORT")
os.Unsetenv("DATABASE_URL")
os.Unsetenv("ENVIRONMENT")
cfg := Load()
if cfg.Port != "8080" {
t.Errorf("expected default port 8080, got %s", cfg.Port)
}
if cfg.Environment != "development" {
t.Errorf("expected default environment development, got %s", cfg.Environment)
}
}
func TestLoadFromEnv(t *testing.T) {
os.Setenv("PORT", "3000")
os.Setenv("ENVIRONMENT", "production")
defer func() {
os.Unsetenv("PORT")
os.Unsetenv("ENVIRONMENT")
}()
cfg := Load()
if cfg.Port != "3000" {
t.Errorf("expected port 3000, got %s", cfg.Port)
}
if cfg.Environment != "production" {
t.Errorf("expected environment production, got %s", cfg.Environment)
}
}

View File

View File

0
internal/model/.gitkeep Normal file
View File

View File

View File

0
migrations/.gitkeep Normal file
View File

0
pkg/.gitkeep Normal file
View File

View File

@@ -0,0 +1,6 @@
-- PulseScore initial database setup
-- This script runs automatically when the PostgreSQL container is first created.
-- Enable useful extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

24
web/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

6
web/.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all"
}

73
web/README.md Normal file
View File

@@ -0,0 +1,73 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

25
web/eslint.config.js Normal file
View File

@@ -0,0 +1,25 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import eslintConfigPrettier from 'eslint-config-prettier'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
eslintConfigPrettier,
])

13
web/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>web</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

4163
web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

38
web/package.json Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "web",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.2.0",
"axios": "^1.13.5",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.0",
"tailwindcss": "^4.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"prettier": "^3.8.1",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^7.3.1"
}
}

16
web/src/App.tsx Normal file
View File

@@ -0,0 +1,16 @@
import BaseLayout from "@/components/BaseLayout";
function App() {
return (
<BaseLayout>
<div className="text-center">
<h2 className="text-2xl font-semibold text-gray-900">Dashboard</h2>
<p className="mt-2 text-gray-600">
Welcome to PulseScore. Connect your integrations to get started.
</p>
</div>
</BaseLayout>
);
}
export default App;

View File

@@ -0,0 +1,31 @@
import type { ReactNode } from "react";
interface BaseLayoutProps {
children: ReactNode;
}
export default function BaseLayout({ children }: BaseLayoutProps) {
return (
<div className="min-h-screen bg-gray-50 text-gray-900">
<header className="border-b border-gray-200 bg-white">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
<h1 className="text-xl font-bold text-indigo-600">PulseScore</h1>
<nav className="flex gap-4 text-sm font-medium text-gray-600">
<a href="/" className="hover:text-gray-900">
Dashboard
</a>
<a href="/customers" className="hover:text-gray-900">
Customers
</a>
<a href="/settings" className="hover:text-gray-900">
Settings
</a>
</nav>
</div>
</header>
<main className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
{children}
</main>
</div>
);
}

1
web/src/index.css Normal file
View File

@@ -0,0 +1 @@
@import "tailwindcss";

10
web/src/main.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);

32
web/tsconfig.app.json Normal file
View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"]
}

7
web/tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

26
web/tsconfig.node.json Normal file
View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

14
web/vite.config.ts Normal file
View File

@@ -0,0 +1,14 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'path'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})