Self-hosted · Laravel + Inertia + React

WedFlow documentation

A premium wedding planning platform for couples, planners and their collaborators — one workspace for the guest list, checklist, budget, seating, vendors, RSVP collection, photo sharing and more. Wrapped in the Atelier editorial design system.

Highlights

Requirements

Installation

The fastest path is the web installer. If you prefer the CLI:

# 1. Clone or extract the archive
cd wedflow

# 2. Install dependencies
composer install
npm install

# 3. Environment
cp .env.example .env
php artisan key:generate

# 4. Database
touch database/database.sqlite       # if using sqlite
php artisan migrate --seed           # creates an admin + a fully populated demo wedding

# 5. Build assets
npm run build                        # production, or `npm run dev` for development

# 6. Serve
php artisan serve                    # http://127.0.0.1:8000

Single-command dev loop (server + queue + logs + Vite):

composer dev

Seeded accounts (after --seed)

RoleEmailPassword
Adminadmin@wedflow.testpassword
Couple (with full demo wedding)couple@wedflow.testpassword

⚠ Change these immediately in production.

Web installer

Browse to /install after extracting the archive. The four-step wizard runs a server requirements check, captures your database connection, runs migrations, and creates your first administrator. On completion it writes storage/installed and every install route 302s home — so it's safe to leave deployed.

Need to re-run the installer? Delete the lock file:
rm storage/installed

Configuration

Most production settings live in the admin panel at /admin/settings — branding, mail driver, storage driver, Google OAuth, Stripe keys, etc. Database-backed values take precedence over .env so a non-technical operator can manage the install from the browser.

.env is only required for low-level Laravel config (database connection, app key, queue/cache drivers). See the annotated .env.example for the full reference.

Admin guide

  1. Sign in with the seeded admin account or promote any user with:
    php artisan tinker
    >>> \App\Models\User::where('email', 'you@example.com')->update(['is_admin' => true]);
  2. Visit /admin for the dashboard (user counts, plan distribution).
  3. /admin/users — toggle admin, suspend, change plan, delete.
  4. /admin/settings — six sections:
    • Branding — app name, logo, base URL, support email.
    • Email Delivery — Resend, SMTP, or no-op (built-in test-send).
    • Email previews — inline samples of Save-the-Date, Invite, RSVP confirmation, verification, password reset, collaborator invite.
    • File Storage — local or S3 / R2 with a separate public URL.
    • Google Sign-In — OAuth client.
    • Billing (Stripe) — secret key, webhook signing secret, Premium & Planner price IDs, and the display prices shown on the public /pricing page.
  5. /admin/languages — add a language, choose the default, toggle which locales appear in the switcher, and edit every translation string in a per-language editor (with progress and search). New languages start in English; untranslated strings fall back to English.

Stripe billing setup

  1. Create two products in Stripe with recurring prices — one per plan (Premium, Planner). Copy each price ID (price_…).
  2. In /admin/settings → Billing (Stripe) paste your Secret Key, Webhook Signing Secret and the two price IDs. Save.
  3. Add a webhook endpoint in Stripe pointing at https://yourdomain.com/billing/webhook and subscribing to checkout.session.completed.
  4. Buyers upgrade from /pricing. Limits are enforced in App\Services\Plans.

Plan limits

PlanWeddingsGuestsCollaborators
Free1502
Premium1
Planner

Adjust App\Services\Plans::LIMITS to change the caps.

Deployment

Project layout

app/
  Http/Controllers/        Feature controllers (Wedding, Guest, Budget, …)
  Http/Middleware/         EnsureWeddingSection, AdminMiddleware, …
  Models/                  Eloquent models, all UUID-keyed
  Policies/                WeddingPolicy — section + access gates
  Services/                Plans (billing), EmailService, RolePermissions
config/
  wedflow.php              Product metadata (version, codename)
database/
  migrations/              Schema (incl. platform_settings, stripe_events)
  seeders/                 DemoWeddingSeeder — admin + full demo wedding
lang/
  en.json / fr.json        Translation strings (shared via Inertia)
resources/
  js/
    layouts/AppLayout.tsx  Sidebar shell used by every authenticated page
    pages/                 Inertia pages, one per route
    components/            Shared UI (UpgradeToast, ThemeToggle, …)
    lib/i18n.ts            useTranslator() hook
  views/
    app.blade.php          Inertia root
    install/               Web installer (Blade)
    errors/                Branded 4xx/5xx pages
routes/
  web.php                  All routes (public, auth, wedding-scoped, admin, install)

Tech stack

Translations

The interface ships fully localised in English and French (lang/en.json and lang/fr.json, at complete key parity). A language switcher lives in the sidebar footer; the choice is stored per session by App\Http\Middleware\SetLocale and works for guests and signed-in users alike.

Manage languages from the admin panel at /admin/languages — add a locale, set the default, toggle which locales appear in the switcher, and edit every string in a per-language editor. A new language starts as a copy of English and is translated in place; blank strings fall back to English. (To add one by hand, drop a lang/{locale}.json file in and it becomes available to enable.)

In components, translate through the useTranslator() hook, which reads the shared translations prop injected by HandleInertiaRequests:

import { useTranslator } from '@/lib/i18n';

const t = useTranslator();
<button>{t('common.save')}</button>
<p>{t('billing.upgrade_to', { plan: 'Premium' })}</p>  // {placeholder} interpolation

Missing keys fall back to the key itself, so untranslated strings are easy to spot.

Customising the look

The Atelier design system is defined in resources/css/app.css and uses Tailwind 4 tokens. Primary colour, accent, surfaces and font choices are all CSS variables — change them once and the whole app re-themes. The dark mode palette lives under [data-theme="dark"]; toggle it via the sidebar moon icon.

Security

Support & license

Buyers should reach support via the Envato item page. Bundle a SUPPORT.md for your own SLA and FAQ before publishing. Released under the Envato Regulated Marketplace License. One licence per end-product.