User reported the social-account OAuth callback popup ('Threads account
connected!') stayed in English regardless of the active locale. The
hardcoded message was wired through SocialController and 11 platform
controllers (Bluesky, Facebook, Instagram, InstagramFacebook, LinkedIn,
LinkedInPage, Mastodon, Pinterest, Threads, TikTok, YouTube), plus the
Blade view that the popup renders.
Adds an accounts.popup_callback i18n block (en/pt-BR/es) covering:
- The popup chrome (title, closing/close-now status text).
- Generic success/reconnect messages (one shared 'Account connected!'
/ 'Account reconnected!' line — the popup already shows a checkmark
and lives for ~2s so platform-specific wording wasn't pulling weight).
- Error variants (account/page/channel) and contextual edge cases
(page not found, no Facebook pages, no YouTube channels, etc.).
Updates every popupCallback() callsite to read from these keys, plus
the Blade view's title and submessage. Regenerates the JSON locale
bundle so laravel-vue-i18n stays in sync.
- Block all billing/usage/subscribe routes in self-hosted mode (redirect to calendar)
- Hide billing_email field in account settings when self-hosted
- Migrate deprecated Facebook Page Insights metrics to new Media Views API (v25.0)
- Fix X analytics token refresh to use Basic Auth (matching XPublisher/ConnectionVerifier)
- Prevent duplicate social accounts by using updateOrCreate across all OAuth controllers
- New card-based layout showing connected accounts with avatar, platform
badge, brand label, and connection date
- "Add Social" button opens dialog with platform grid for connecting
- Removed unique constraint on workspace_id+platform to allow multiple
accounts per platform
- Removed "already connected" checks from all 13 OAuth controllers
- Updated tests to verify multi-account connection works
- Refactor WorkspacePolicy to use pivot role instead of workspace.user_id
- Add manageBilling policy (owner only) to BillingController
- Fix ApiKeyController authorization (view → manageTeam for store/destroy)
- Fix WorkspaceInviteController using workspace.user_id for owner checks
- Fix WorkspaceController settings is_owner using workspace.user_id
- Create PostAction enum for UpdatePost/PostController action strings
- Create ApiToken\Status enum
- Add User::SUBSCRIPTION_NAME constant, replace all hardcoded 'default'
- Convert wantsEmailFor to accept NotificationType enum
- Convert all $data[] to data_get() across publishers, controllers, jobs
- Fix SocialLoginController callback missing try/catch
- Fix SocialController::toggleActive missing workspace null check
- Fix UpdatePost NPE on meta merge when postPlatform not found
- Remove HTML5 required attributes from form inputs
- Convert function declarations to arrow functions in Vue components
- Replace hardcoded URLs with Wayfinder route helpers
- Replace new Date() with dayjs
- Add 16 new test files covering policies, authorization, publishing
Settings pages:
- Redesign layout to match Sendkit (max-w-4xl, space-y-12, Separator sections)
- Merge Members page into Workspace settings with Table, invite Dialog, ConfirmDeleteModal
- Add workspace logo upload/delete routes and controller methods
- Translate all hardcoded strings in Workspace.vue modals
Language system:
- Drop languages table, replace language_id FK with locale string column on users
- Create config/languages.php for available languages and default locale
- Add Spanish (es) translations (13 files)
- Simplify HandleInertiaRequests, ProfileController, RegisteredUserController
Code quality:
- Add declare(strict_types=1) to all PHP files
- Fix MastodonPublisher using wrong attribute (filename -> original_filename)
- Fix HasMediaTest for new has_photo/photo_url accessors
- Fix PublishToSocialPlatformTest type error revealed by strict_types
- Remove orphaned Language model from AppServiceProvider morph map
- Update User TypeScript interface (has_photo, photo_url, locale)
- Eager load media relation on workspaces to prevent N+1
- Add 8 new tests for workspace logo upload/delete
- Update workspace settings test to assert members/invitations props
All 710 tests passing.
- Extract business logic from controllers into Action classes:
Post/, Workspace/, Hashtag/, Label/, Invite/, ApiKey/
- Create subdomain routing: app.trypost.test (Inertia dashboard),
api.trypost.test (REST API with token auth)
- Add ApiToken model with tp_ prefix, token_lookup/hash auth
- Add AuthenticateApiToken middleware for API authentication
- Create Api controllers with JSON Resources for all entities
- Create App controllers that use Actions + Inertia responses
- Organize Form Requests into Api/ and App/ directories
- Add api_tokens migration
- Update all route names with app. prefix
- Update all tests to use new route names (684 passing)
- Add Bluesky platform to Platform and ContentType enums
- Create BlueskyController with custom auth flow (not OAuth)
- Create BlueskyPublisher service for posting via AT Protocol
- Add BlueskyConnect.vue page with handle/app password form
- Add BlueskyPreview.vue component for post preview
- Register Bluesky in PublishToSocialPlatform job
- Update Edit.vue with Bluesky logo and content type options
- Add Bluesky config toggle in trypost.php
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>