Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ jobs:

- name: Lint
run: bun run lint
working-directory: apps/mobile
working-directory: apps/native

- name: Type check
run: bun run type-check
working-directory: apps/mobile
working-directory: apps/native

- name: Test
run: bun test
working-directory: apps/mobile
working-directory: apps/native
Comment on lines 32 to +34
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspect CI test step:"
rg -n -C2 'name:\s*Test|run:\s*bun test|run:\s*bun run test|working-directory:\s*apps/native' .github/workflows/ci.yml

echo
echo "Inspect native package test script:"
rg -n -C2 '"test"\s*:\s*".*"' apps/native/package.json

Repository: MrDemonWolf/conpaws

Length of output: 576


Use bun run test so CI executes the configured Vitest suite.

Line 33 currently runs bun test, which invokes Bun's built-in test runner. However, apps/native/package.json defines test as vitest run. This mismatch means CI runs a different test path than intended. Additionally, this is inconsistent with the other steps (lines 25, 29) which use bun run lint and bun run type-check.

Proposed fix
       - name: Test
-        run: bun test
+        run: bun run test
         working-directory: apps/native
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Test
run: bun test
working-directory: apps/mobile
working-directory: apps/native
- name: Test
run: bun run test
working-directory: apps/native
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 32 - 34, The CI step named "Test"
currently runs "bun test" which invokes Bun's builtin runner instead of the
project's configured Vitest script; update the step so the run command uses "bun
run test" (matching the package.json "test": "vitest run" and consistent with
the other steps using "bun run lint" and "bun run type-check") so the Vitest
suite defined in apps/native/package.json is executed by CI.

38 changes: 38 additions & 0 deletions .github/workflows/update-license-year.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Update License Year

on:
schedule:
- cron: '0 6 1 1 *'
workflow_dispatch:

jobs:
update-license-year:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Get current year
id: year
run: echo "year=$(date +%Y)" >> "$GITHUB_OUTPUT"

- name: Update year in LICENSE
run: sed -i "s/Copyright (c) [0-9]\{4\}/Copyright (c) ${{ steps.year.outputs.year }}/" LICENSE

- name: Check for changes
id: diff
run: |
if git diff --quiet LICENSE; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi

- name: Create Pull Request
if: steps.diff.outputs.changed == 'true'
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
branch: chore/update-license-year
commit-message: 'chore: update license year to ${{ steps.year.outputs.year }}'
title: 'chore: update license year to ${{ steps.year.outputs.year }}'
body: Automated annual update of the MIT license copyright year.
labels: chore
111 changes: 31 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,97 +1,48 @@
# conpaws
# ConPaws

This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines Next.js, and more.
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/MrDemonWolf/conpaws/blob/main/LICENSE)

## Features
ConPaws is an open-source furry convention companion app built by [MrDemonWolf](https://github.com/MrDemonWolf), coming soon to the Apple App Store and Google Play Store. Feel free to fork it and adapt it for your own conventions or fandom community.

- **TypeScript** - For type safety and improved developer experience
- **Next.js** - Full-stack React framework
- **React Native** - Build mobile apps using React
- **Expo** - Tools for React Native development
- **TailwindCSS** - Utility-first CSS for rapid UI development
- **Shared UI package** - shadcn/ui primitives live in `packages/ui`
- **Turborepo** - Optimized monorepo build system
## What it does

## Getting Started
- **Local-first** — all core features work offline on iOS, Android, and Web
- **Import schedules** — pull in convention events via iCal files or Sched.com URLs
- **Build your schedule** — mark events you want to attend and get reminders
- **ConPaws+** — premium features via RevenueCat (Phase 3+)

First, install the dependencies:
## Project Structure

```bash
bun install
```text
conpaws/
├── apps/mobile/ # Expo React Native app (iOS, Android, Web)
├── apps/web/ # Next.js static site → conpaws.com
├── apps/server/ # Hono API → api.conpaws.com (Cloudflare Workers)
└── apps/docs/ # Fumadocs → docs.conpaws.com
```

Then, run the development server:
## Getting Started

```bash
bun run dev
bun install
```

Open [http://localhost:3001](http://localhost:3001) in your browser to see the web application.
Use the Expo Go app to run the mobile application.

## UI Customization

React web apps in this stack share shadcn/ui primitives through `packages/ui`.

- Change design tokens and global styles in `packages/ui/src/styles/globals.css`
- Update shared primitives in `packages/ui/src/components/*`
- Adjust shadcn aliases or style config in `packages/ui/components.json` and `apps/web/components.json`

### Add more shared components

Run this from the project root to add more primitives to the shared UI package:
### Mobile app (`apps/mobile`)

```bash
bunx shadcn@latest add accordion dialog popover sheet table -c packages/ui
```

Import shared components like this:

```tsx
import { Button } from "@conpaws/ui/components/button";
```

### Add app-specific blocks

If you want to add app-specific blocks instead of shared primitives, run the shadcn CLI from `apps/web`.

## Project Structure

bun start # Start Expo dev server (development variant)
bun start:preview # Start Expo dev server (preview variant)
bun start:prod # Start Expo dev server (production variant)
bun android # Run on Android
bun ios # Run on iOS
bun web # Run web version
bun lint # Run ESLint
bun type-check # Run TypeScript type checking
bun test # Run tests (Vitest)
bun prebuild # Generate native projects
bun prebuild:clean # Clean and regenerate native projects
```
conpaws/
├── apps/
│ ├── web/ # Frontend application (Next.js)
│ ├── native/ # Mobile application (React Native, Expo)
├── packages/
│ ├── ui/ # Shared shadcn/ui components and styles
```

## Available Scripts

### Root (Monorepo)

- `bun run dev` — Start all applications in development mode
- `bun run build` — Build all applications
- `bun run check-types` — Check TypeScript types across all apps
- `bun run dev:native` — Start the React Native/Expo development server
- `bun run dev:web` — Start only the web application

### Native App (`apps/native`)

- `bun start` — Start Expo dev server (development variant)
- `bun start:preview` — Start Expo dev server (preview variant)
- `bun start:prod` — Start Expo dev server (production variant)
- `bun android` — Run on Android
- `bun ios` — Run on iOS
- `bun web` — Run web version
- `bun run lint` — Run ESLint
- `bun run type-check` — Run TypeScript type checking
- `bun test` — Run tests (Vitest)
- `bun run prebuild` — Generate native projects
- `bun run prebuild:clean` — Clean and regenerate native projects

### Web App (`apps/web`)
## Want to use this for your own fandom?

- `bun run dev` — Start Next.js dev server
- `bun run build` — Build for production
- `bun run start` — Start production server
Fork it. The app is local-first with all core features working offline. Supabase (cloud sync) is optional and planned for Phase 2+, so you can self-host with Supabase if you want cloud sync. Configure your RevenueCat keys for premium features, and point it at your conventions. The iCal import works with any standard `.ics` file, so it should work with most convention schedule tools out of the box.
64 changes: 64 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# ConPaws — Go Live Checklist

## 0. Pre-flight

- [ ] Run `bun test` (should be 16/16)
- [ ] Commit all Phase 1D–1G changes to main
- [ ] Push to GitHub

---

## 1. API — Cloudflare Workers (`api.conpaws.com`)

- [ ] `cd apps/server && bun install`
- [ ] `wrangler login` (if not already authed)
- [ ] `wrangler secret put BREVO_API_KEY` (paste key when prompted)
- [ ] Verify `BREVO_LIST_ID` in `wrangler.toml` matches your actual Brevo list ID
- [ ] `wrangler deploy`
- [ ] Smoke test: `curl https://conpaws-api.workers.dev/health` → `{ "status": "ok" }`
- [ ] Add custom domain `api.conpaws.com` in Cloudflare Workers dashboard

---

## 2. Website — Cloudflare Pages (`conpaws.com`)

- [ ] In Cloudflare Pages: Create project → Connect GitHub repo
- [ ] Framework preset: **Next.js (static)**
- [ ] Build command: `cd apps/web && bun install && bun run build`
- OR: Build command: `bun run build`, Root directory: `apps/web`
- [ ] Output directory: `apps/web/out`
- [ ] Add env var: `NEXT_PUBLIC_API_URL` = `https://api.conpaws.com`
- [ ] Deploy
- [ ] Add custom domain: `conpaws.com` (and `www.conpaws.com`)
- [ ] Verify: `conpaws.com` loads, `/privacy` and `/terms` work

> **Note:** `apps/web/next.config.ts` uses `output: 'export'` — static files go to `out/`.
> The `"start": "next start"` script in `apps/web/package.json` is **incompatible** with static export and must not be used for production. Cloudflare Pages ignores it, but it should be cleaned up post-launch (see step 5).

---

## 3. DNS

- [ ] `conpaws.com` → Cloudflare Pages (CNAME to `.pages.dev` URL, or use Cloudflare nameservers for automatic routing)
- [ ] `api.conpaws.com` → Workers custom domain (configured in step 1)
- [ ] `www.conpaws.com` → redirect to `conpaws.com` (set up in Cloudflare Pages → Custom domains)

---

## 4. Smoke Tests

- [ ] `GET https://api.conpaws.com/health` → `{ "status": "ok" }`
- [ ] `POST https://api.conpaws.com/subscribe` with valid `name` + `email` → `200`
- [ ] `POST https://api.conpaws.com/subscribe` with honeypot field filled → `200` (silent success, no contact created)
- [ ] `https://conpaws.com` → hero, features, and signup form visible
- [ ] `https://conpaws.com/privacy` → loads
- [ ] `https://conpaws.com/terms` → loads
- [ ] Submit signup form on live site → contact appears in Brevo

---

## 5. Post-deploy

- [ ] Update `DEPLOY_WEB.md` to reflect Cloudflare Pages approach (not Coolify + `next start`)
- [ ] Set up auto-deploy: Cloudflare Pages deploys on every push to `main` (enable in Pages settings)
- [ ] Fix `apps/web/package.json` `start` script — remove or replace `"next start"` with a static serve alternative (e.g. `"serve out"` using the `serve` package), since it will never work with `output: 'export'`
8 changes: 8 additions & 0 deletions apps/docs/content/docs/api/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: API Overview
description: ConPaws API reference
---

## API Overview

Content coming soon.
8 changes: 8 additions & 0 deletions apps/docs/content/docs/dev/setup.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Development Setup
description: How to set up the ConPaws development environment
---

## Development Setup

Content coming soon.
8 changes: 8 additions & 0 deletions apps/docs/content/docs/guides/getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Getting Started
description: How to use ConPaws for your first convention
---

## Getting Started

Content coming soon. Check back after the ConPaws launch!
2 changes: 2 additions & 0 deletions apps/native/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
APP_VARIANT=

# Supabase
EXPO_PUBLIC_SUPABASE_URL=
EXPO_PUBLIC_SUPABASE_ANON_KEY=
Expand Down
19 changes: 17 additions & 2 deletions apps/native/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { ConfigContext, ExpoConfig } from "expo/config";

const APP_VARIANT = process.env.APP_VARIANT ?? "production";
const IS_PRODUCTION = APP_VARIANT === "production";
const EAS_PROJECT_ID = "0ad7171c-1b3e-48b2-a806-554aeea30048";

const getAppName = (): string => {
switch (APP_VARIANT) {
Expand Down Expand Up @@ -39,15 +41,29 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: getAppName(),
slug: "conpaws",
owner: "mrdemonwolf-org",
version: "1.0.0",
orientation: "portrait",
orientation: "default",
icon: "./assets/images/icon.png",
scheme: getScheme(),
userInterfaceStyle: "automatic",
runtimeVersion: { policy: "appVersion" },
updates: {
url: `https://u.expo.dev/${EAS_PROJECT_ID}`,
},
extra: {
eas: {
projectId: EAS_PROJECT_ID,
},
},
...(IS_PRODUCTION ? {} : { developmentClient: {} }),
ios: {
supportsTablet: true,
bundleIdentifier: getBundleId(),
associatedDomains: ["applinks:conpaws.app"],
config: {
usesNonExemptEncryption: false,
},
infoPlist: {
NSCameraUsageDescription:
"ConPaws needs access to your camera to take photos.",
Expand All @@ -72,7 +88,6 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
"RECEIVE_BOOT_COMPLETED",
"VIBRATE",
],
enableEdgeToEdge: true,
},
androidStatusBar: {
translucent: true,
Expand Down
5 changes: 5 additions & 0 deletions apps/native/app/(onboarding)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Stack } from 'expo-router';

export default function OnboardingLayout() {
return <Stack screenOptions={{ headerShown: false }} />;
}
42 changes: 42 additions & 0 deletions apps/native/app/(onboarding)/complete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { View } from 'react-native';
import { router } from 'expo-router';
import { useTranslation } from 'react-i18next';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { SafeView, Text, Button } from '@/components/ui';

export default function CompleteScreen() {
const { t } = useTranslation();

async function handleLetsGo() {
try {
await AsyncStorage.setItem('hasCompletedOnboarding', 'true');
} catch {
// Storage write failed — still navigate so user isn't stuck
} finally {
router.replace('/(tabs)');
}
}

return (
<SafeView>
<View className="flex-1 items-center justify-center px-6 gap-6">
<View className="w-20 h-20 rounded-full bg-green-100 dark:bg-green-900/30 items-center justify-center">
<Text className="text-4xl">✓</Text>
</View>
<View className="items-center gap-2">
<Text variant="h2" className="text-center">
{t('onboarding.complete.title')}
</Text>
<Text variant="caption" className="text-center">
{t('onboarding.complete.subtitle')}
</Text>
</View>
</View>
<View className="px-6 pb-8">
<Button size="lg" onPress={handleLetsGo} className="w-full">
{t('onboarding.complete.letsGo')}
</Button>
</View>
</SafeView>
);
}
Loading
Loading