diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a627cd..8e52338 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,11 @@ on: push: branches: - main + - dev pull_request: branches: - main + - dev concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -69,7 +71,7 @@ jobs: cd: needs: ci - if: ${{ github.event_name == 'push' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} permissions: contents: read packages: write diff --git a/apps/api/deploy/.env.example b/apps/api/deploy/.env.example index 6622c97..324b788 100644 --- a/apps/api/deploy/.env.example +++ b/apps/api/deploy/.env.example @@ -1,33 +1,15 @@ -# Copy to .env next to compose.yaml on the VM (/opt/apps/snapsplit) and fill in. -# docker compose reads this file for the api service. NEVER commit the real .env -# — it holds secrets. - -# Image tag to pull for the api service. Use a published tag, e.g. latest, or a -# sha-<7> produced by `bun nx publish api`. The deploy script only rewrites this -# line on the VM. API_IMAGE_TAG=latest -# Base DuckDNS domain; the caddy-docker-proxy label builds snapsplit-api.. DUCKDNS_DOMAIN=your-subdomain.duckdns.org -# API runtime NODE_ENV=production -# One proxy (Caddy) in front of the API; without this the rate limiter keys every -# caller by Caddy's address and throttles them as one. TRUST_PROXY=1 -# Web origin allowed by CORS. This is the Worker URL (or its custom domain), NOT -# the API's own domain. A missing origin fails at request time in production. CORS_ORIGIN=https://example.workers.dev -# Gemini key for receipt extraction. GOOGLE_GENERATIVE_AI_API_KEY=your-gemini-api-key -# MongoDB Atlas (M0 free tier) MONGODB_URI=mongodb+srv://:@.mongodb.net/snapsplit?retryWrites=true&w=majority DATABASE_NAME=snapsplit -# Receipt object storage (S3-compatible OCI Object Storage). Fill from: -# terraform -chdir=infra output s3_endpoint s3_region s3_bucket s3_access_key_id -# terraform -chdir=infra output -raw s3_secret_access_key S3_ENDPOINT=https://.compat.objectstorage..oraclecloud.com S3_REGION= S3_BUCKET=snapsplit-receipts diff --git a/apps/api/scripts/publish.sh b/apps/api/scripts/publish.sh index 4b460a1..7d37564 100755 --- a/apps/api/scripts/publish.sh +++ b/apps/api/scripts/publish.sh @@ -15,6 +15,8 @@ exec docker buildx build \ --file apps/api/Dockerfile \ --target prebuilt \ --platform linux/amd64,linux/arm64 \ + --cache-from "type=registry,ref=$image:buildcache" \ + --cache-to "type=registry,ref=$image:buildcache,mode=max" \ "$@" \ --label "org.opencontainers.image.source=https://github.com/$GITHUB_REPOSITORY" \ --label "org.opencontainers.image.revision=$GITHUB_SHA" \ diff --git a/apps/web/src/features/session/components/line-item-row-shell.tsx b/apps/web/src/features/session/components/line-item-row-shell.tsx index 12d478e..841374e 100644 --- a/apps/web/src/features/session/components/line-item-row-shell.tsx +++ b/apps/web/src/features/session/components/line-item-row-shell.tsx @@ -23,7 +23,7 @@ export function LineItemRowShell({
  • diff --git a/apps/web/src/features/session/components/receipt-capture.tsx b/apps/web/src/features/session/components/receipt-capture.tsx index 217ea26..bfeefad 100644 --- a/apps/web/src/features/session/components/receipt-capture.tsx +++ b/apps/web/src/features/session/components/receipt-capture.tsx @@ -61,14 +61,14 @@ export function ReceiptCapture() { function AnalyzingView({ imageUrl }: { imageUrl: string }) { return ( -
    +

    Reading your receipt

    -
    +
    Your receipt +
    @@ -113,7 +113,6 @@ function CaptureView({ inputRef, onCapture, errorMessage }: CaptureViewProps) { ref={inputRef} type="file" accept="image/*" - capture="environment" className="hidden" onChange={(event) => { const image = event.target.files?.[0]; diff --git a/apps/web/src/features/session/components/session-draft.tsx b/apps/web/src/features/session/components/session-draft.tsx index c9e0ade..e817c38 100644 --- a/apps/web/src/features/session/components/session-draft.tsx +++ b/apps/web/src/features/session/components/session-draft.tsx @@ -1,5 +1,16 @@ import { useNavigate } from '@tanstack/react-router'; import { AlertTriangle, Camera } from 'lucide-react'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from 'shadcn-ui/alert-dialog'; import { Button } from 'shadcn-ui/button'; import { useDeleteSession } from '@/features/session/api/delete-session'; import { useUpdateSession } from '@/features/session/api/update-session'; @@ -109,21 +120,43 @@ function RetakePhotoButton({ session }: { session: Session }) { const deleteSession = useDeleteSession({ sessionId: session.id }); return ( - + + + + + } + > + + + Retake photo? + + This receipt and its items will be discarded so you can take a new + photo. + + + + + Cancel + + + deleteSession.mutate( + { sessionId: session.id }, + { onSettled: () => navigate({ to: '/' }) }, + ) + } + > + Retake + + + + ); }