Set up proper DB migrations and fix remaining mobile responsive issues

Migrations:
- drizzle.config.ts: add out: './drizzle' so drizzle-kit generate writes
  SQL migration files instead of only supporting push
- drizzle/0000_glossy_magneto.sql: initial migration capturing all 9
  current tables (users, accounts, sessions, verification_tokens,
  sso_providers, clients, businesses, invoices, invoice_items)
- src/server/db/migrate.ts: programmatic runner using drizzle-orm's
  migrate() — tracks applied migrations in __drizzle_migrations,
  safe to run on every deploy
- package.json: db:migrate now runs the programmatic runner instead of
  drizzle-kit migrate (CLI requires devDeps at runtime)
- start.sh: replace drizzle-kit push with bun src/server/db/migrate.ts
- Dockerfile: copy drizzle/ folder into the runner image so migrations
  are available at container startup

Mobile fixes:
- data-table.tsx: pagination buttons grow from 32px to 40px on mobile
  (h-10 w-10 md:h-8 md:w-8) to meet 44px touch-target guidelines
- floating-action-bar.tsx: stack left-content + action buttons to column
  layout on narrow screens (flex-col sm:flex-row), reduce padding on
  mobile (p-3 sm:p-4)
- revenue-chart.tsx: responsive chart height (h-48 md:h-64) so the chart
  doesn't consume too much vertical space on small screens

https://claude.ai/code/session_012sqEgNQpx676isepeoX4Mi
This commit is contained in:
Claude
2026-04-05 01:59:08 +00:00
parent 563d77ba65
commit ba14526fc5
11 changed files with 1498 additions and 10 deletions
@@ -86,7 +86,7 @@ export function RevenueChart({ data }: RevenueChartProps) {
}
return (
<div className="h-64 w-full">
<div className="h-48 w-full md:h-64">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={chartData}>
<defs>
+4 -4
View File
@@ -471,7 +471,7 @@ export function DataTable<TData, TValue>({
<Button
variant="outline"
size="icon"
className="h-8 w-8"
className="h-10 w-10 md:h-8 md:w-8"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
@@ -481,7 +481,7 @@ export function DataTable<TData, TValue>({
<Button
variant="outline"
size="icon"
className="h-8 w-8"
className="h-10 w-10 md:h-8 md:w-8"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
@@ -503,7 +503,7 @@ export function DataTable<TData, TValue>({
<Button
variant="outline"
size="icon"
className="h-8 w-8"
className="h-10 w-10 md:h-8 md:w-8"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
@@ -513,7 +513,7 @@ export function DataTable<TData, TValue>({
<Button
variant="outline"
size="icon"
className="h-8 w-8"
className="h-10 w-10 md:h-8 md:w-8"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
@@ -72,7 +72,7 @@ export function FloatingActionBar({
)}
>
<Card className="hover-lift bg-card border-border border shadow-lg">
<CardContent className="flex items-center justify-between p-4">
<CardContent className="flex flex-col gap-3 p-3 sm:flex-row sm:items-center sm:justify-between sm:p-4">
{/* Left content */}
{leftContent && (
<div className="text-card-foreground animate-fade-in flex flex-1 items-center gap-3">
+49
View File
@@ -0,0 +1,49 @@
/**
* Programmatic migration runner for production deployments.
*
* Run with: bun src/server/db/migrate.ts
*
* This applies any pending migrations from the drizzle/ directory to the
* database specified by DATABASE_URL. It is safe to run multiple times —
* Drizzle tracks applied migrations in the __drizzle_migrations table.
*/
import * as dotenv from "dotenv";
// Load env files before importing anything that reads process.env
dotenv.config({ path: ".env.local" });
dotenv.config({ path: ".env" });
import { Pool } from "pg";
import { drizzle } from "drizzle-orm/node-postgres";
import { migrate } from "drizzle-orm/node-postgres/migrator";
import path from "path";
import { fileURLToPath } from "url";
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
console.error("[migrate] ERROR: DATABASE_URL is not set");
process.exit(1);
}
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const migrationsFolder = path.resolve(__dirname, "../../../drizzle");
const pool = new Pool({
connectionString: databaseUrl,
ssl: process.env.DB_DISABLE_SSL === "true" ? false : { rejectUnauthorized: false },
max: 1,
});
const db = drizzle(pool);
console.log("[migrate] Running migrations from", migrationsFolder);
try {
await migrate(db, { migrationsFolder });
console.log("[migrate] All migrations applied successfully");
} catch (err) {
console.error("[migrate] Migration failed:", err);
process.exit(1);
} finally {
await pool.end();
}