Commit graph

36 commits

Author SHA1 Message Date
Paulo Castellano
ec23928908 refactor: replace Inertia-based OAuth authorization view with a static Blade template and remove unused language files. 2026-05-03 20:26:09 -03:00
Paulo Castellano
2cc0aed846 fix: enable database destructive command prohibition in production environments 2026-05-03 19:56:36 -03:00
Paulo Castellano
28ea0c8222 chore: comment out destructive command prohibition in AppServiceProvider 2026-05-03 19:45:38 -03:00
Paulo Castellano
20eeeaa493 feat: migrate custom API token implementation to Laravel Passport for authentication and token management 2026-05-03 18:38:17 -03:00
Paulo Castellano
488d4a41e5 feat: implement feature gating with persistent usage tracking and a reactive upgrade dialog for plan management. 2026-05-03 16:52:28 -03:00
Paulo Castellano
b47f2488d0 refactor: replace hashtags functionality with reusable signatures feature 2026-05-03 15:23:30 -03:00
Paulo Castellano
d39124e90b refactor: reorganize settings UI, migrate post templates to a file-based registry, and remove legacy video generation features 2026-05-03 13:44:13 -03:00
Paulo Castellano
1e1519876d feat: replace legacy AI assistant with modular post content generation, review, and template management system 2026-05-03 09:36:50 -03:00
Paulo Castellano
0d1ca71380 feat: add platform rules for all 12 supported social networks
Each platform has a rule class exposing specs() (char limits, aspect
ratios, media limits, format-specific constraints) and summary() (a
short concise description the agent can render into instructions).

Registered in AppServiceProvider::configurePlatformRules() mapping
Platform enum values to rule classes. Both InstagramFacebook and
LinkedInPage share rules with their non-business siblings.

Contract + Registry follow the lookup-map pattern. Registry is a
static registry seeded at boot — rules are cheap enough to new up
per request.

Tests cover all 12 platforms via Pest dataset and verify the forMany
fan-out, clear/register behavior, and representative summaries.
2026-04-16 10:03:14 -03:00
Paulo Castellano
e3f80b91d4 refactor: migrate PostAssistantController to SDK Agent + tools
Controller now delegates to SocialMediaAssistant::prompt() and reads
generated attachments from AttachmentCollector (request-scoped). The
three preg_match branches for [GENERATE_IMAGE/VIDEO/AUDIO] commands
are gone — the LLM invokes tools directly with typed parameters.

Deleted:
- app/Services/Ai/GeminiTextGenerationService.php
- app/Services/Ai/TextGenerationService.php  (OpenAI alternative)
- app/Services/Ai/ImageGenerationService.php
- app/Services/Ai/AudioGenerationService.php
- app/Services/Ai/Contracts/TextGenerationInterface.php

Kept: VideoGenerationService (wrapped by GenerateVideo tool since
Veo is not in the SDK's provider matrix) and IntentDetector.

Tests now fake the agent via SocialMediaAssistant::fake() with either
canned text or a callable that simulates tool side-effects by pushing
directly into AttachmentCollector.
2026-04-16 09:59:10 -03:00
Paulo Castellano
fb5d2e4f02 feat: add GenerateImage, GenerateVideo, GenerateAudio SDK tools
Each tool implements Laravel\Ai\Contracts\Tool with description(),
handle(Request), and schema(JsonSchema). They share a common pattern:
- Constructor receives Workspace, optional Post, userId, and an optional
  AttachmentCollector (resolves from container if not injected).
- handle() does the work (Image::of(), Audio::of(), or calls our custom
  VideoGenerationService for Veo), persists media + usage log, pushes
  the full attachment shape into the collector, and returns a short
  text summary to the LLM.
- schema() exposes typed parameters with enum constraints and doc
  strings so the LLM selects valid inputs.

Also fix the Ai::extend closure signature — MultipleInstanceManager
passes (app, config) not just (config) to custom creators.
2026-04-16 09:56:25 -03:00
Paulo Castellano
93a84f8f95 feat: add AttachmentCollector request-scoped side-channel for tools 2026-04-16 09:52:41 -03:00
Paulo Castellano
a962124097 feat: extend GeminiProvider with social-media aspect ratios
The stock provider only exposes 1:1, 2:3, 3:2 via defaultImageOptions()
match statement — any other ratio gets silently dropped to null.

ExtendedGeminiProvider adds 9:16, 16:9, 4:3, 3:4, 4:5, 5:4, 21:9 so
Image::of()->size('9:16') now works for Reels/Stories/TikTok/Shorts
and size('16:9') for X/LinkedIn/YouTube landscape content.

Gemini's native API supports all these ratios; the SDK restriction was
purely in the provider's match statement.
2026-04-16 09:50:36 -03:00
Paulo Castellano
b2bf5c2059 feat: complete AI assistant with custom services and brand config
Baseline snapshot of custom AI implementation before Laravel AI SDK migration.

Includes:
- Custom services: GeminiTextGenerationService, TextGenerationService (OpenAI), ImageGenerationService, AudioGenerationService, VideoGenerationService
- IntentDetector for content moderation via keyword matching
- AI enums: Intent, Orientation, UsageType
- Blade prompt templates: system.blade.php, image.blade.php, video.blade.php
- AiMessage with content_html accessor (markdown rendering)
- AiUsageLog for monthly quota tracking per account
- PostAssistantController with regex-based [GENERATE_*] parsing
- WritingAssistantTab with markdown rendering, add-to-post, attachments
- Workspace brand fields (name, description, tone, voice_notes) in system prompt
- Session state block injected into prompts (thread counts, quota remaining)
- AttachmentCollector pattern will replace the regex approach in Phase 2
- Post comments with replies, emoji reactions, real-time via Echo
- Assets page with Unsplash + Giphy integrations
2026-04-16 09:25:08 -03:00
Paulo Castellano
762fa77583 feat: add AI generation services and usage tracking 2026-04-15 21:06:06 -03:00
Paulo Castellano
91e0930182 feat: add ai_messages table and model 2026-04-15 21:01:26 -03:00
Paulo Castellano
7f491b2c2c feat: add post_comments table, model, and factory 2026-04-15 20:08:16 -03:00
Paulo Castellano
2da15df96c feat: introduce Account entity as billing owner and refactor architecture
- Create Account model as Cashier Billable entity (stripe, plan, subscription)
- Account owns workspaces and has an owner_id (User)
- User belongs to one Account via account_id
- Workspace belongs to Account via account_id, no longer has billing fields
- Remove Brand model entirely (workspaces serve as grouping)
- Rename brand_limit to workspace_limit in plans
- Workspace roles simplified: admin/member/viewer (owner via Account)
- Invites now belong to Account with workspaces JSON array
- Pennant features scope changed from Workspace to Account
- EnsureSubscribed middleware checks Account subscription
- All controllers updated: BillingController, OnboardingController,
  WorkspaceInviteController, SocialController, StripeEventListener
- Frontend: extract GoogleAuthButton component, create WorkspaceRole
  enum for type-safe role checks, fix all views for new architecture
- All 1101 tests passing
2026-04-14 22:22:04 -03:00
Paulo Castellano
c867faa2ee fix: address code review issues for billing migration 2026-04-14 18:44:47 -03:00
Paulo Castellano
1ff2f93c37 feat: move Billable from User to Workspace with plan support 2026-04-14 18:22:36 -03:00
Paulo Castellano
c107ce3949 feat: create brands with CRUD, policy, and tests 2026-04-14 18:04:41 -03:00
Paulo Castellano
d235e702cf feat: create plans table, model, and slug enum 2026-04-14 17:53:12 -03:00
Paulo Castellano
843b3991ec chore: posthog, ui, features and more 2026-03-30 21:18:07 -03:00
Paulo Castellano
aa7ca8ae21 refactor: notification preferences, header slots, calendar layout, UI polish
Notification preferences:
- Create notification_preferences table (post_published, post_failed,
  account_disconnected booleans per user)
- NotificationPreferenceController with firstOrCreate on first visit
- SendNotification job respects email preferences before sending
- Settings page with toggle switches, i18n in 3 languages
- 8 new tests for preferences (controller + wantsEmailFor + job integration)

Post published notification:
- PostPublished mail + maizzle template
- Notify owner on successful publish via SendNotification job
- PostPublished type added to notification enum

Header & Layout:
- Rename AppSidebarHeader to AppHeader with left/center/right slots
- showSidebarTrigger prop to hide sidebar toggle
- Calendar: controls in header (left: nav, center: date, right: tabs + new post)
- Fixed header with scrollable content (flex h-screen pattern)
- fullWidth pages use overflow-y-auto (fixes month view scroll)

UI improvements:
- Action buttons moved to header-right: posts, hashtags, labels
- Settings breadcrumbs: "Settings > Profile" pattern
- Calendar: remove duplicate New Post button from day view
- Remove size="sm" from Schedule/Publish buttons
- Remove bg-background from header (inherits from SidebarInset)
- Add Cancel button to labels and hashtags create/edit dialogs
- Add common.cancel i18n key
- Clean up orphaned Calendar breadcrumbs
- Fix SocialAccountsGrid buttons to use shadcn Button ghost

All 753 tests passing.
2026-03-30 18:18:17 -03:00
Paulo Castellano
09e37f2879 feat: notification system with SendNotification job, dialog UI, tests
Backend:
- Create notifications table (user_id, workspace_id, type, channel,
  title, body, data JSON, read_at, archived_at)
- Create Notification model with Type enum (post_failed,
  account_disconnected, invite_received, member_joined, member_removed)
  and Channel enum (email, in_app, both)
- Create SendNotification job: isolated from publish flow, handles
  saving in-app notification and sending email independently
- NotificationController: index (excludes archived, scoped to workspace),
  markAsRead, markAllAsRead, archiveAll
- Integrate with PublishToSocialPlatform (post failed/partial)
- Integrate with VerifyWorkspaceConnections (batch disconnection)
- Integrate with SocialAccount::markAsDisconnected (single disconnection)
- All use SendNotification::dispatch() instead of direct Mail::to()

Frontend:
- NotificationBell component in sidebar footer with unread badge
- Dialog with notification list, mark as read, mark all read, archive all
- Click navigates to relevant page (post edit, accounts)
- i18n for notifications UI (en, es, pt-BR)

Tests:
- 8 tests for NotificationController (auth, CRUD, workspace scoping)
- 4 tests for SendNotification job (channels, email, data storage)

All 745 tests passing.
2026-03-30 16:47:03 -03:00
Paulo Castellano
56b8c92e72 refactor: settings redesign, Spanish translations, language system, strict_types
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.
2026-03-30 00:20:43 -03:00
Paulo Castellano
fabce14aad feat: implement MCP server with tools, Post API tests, auth middleware
- Create TryPostServer MCP server with 17 tools:
  Post (List, Get, Create, Delete), Hashtag (List, Create, Update, Delete),
  Label (List, Create, Update, Delete), Workspace (Get),
  ApiKey (List, Create, Delete)
- Create AuthenticateMcpToken middleware (logs in workspace owner)
- Register mcp.auth middleware alias in bootstrap/app.php
- Create routes/ai.php with mcp.trypost.test subdomain
- Add PostApiTest with 6 tests (list, show, create, delete, isolation)
- Fix PostApiTest assertions for pagination/resource wrapping
- 704 tests passing, frontend build passing
2026-03-29 20:30:36 -03:00
Paulo Castellano
039ed02b42 feat: rate limiting, cleanup duplicates, ApiKey app tests, bootstrap hardening
- Add API rate limiting (60/min per workspace) in AppServiceProvider
- Add throttle:api middleware to all API routes
- Add rate limit exception rendering for JSON responses
- Add middleware priority list (AuthenticateApiToken before ThrottleRequests)
- Add ApiKeyControllerTest (6 tests for App dashboard CRUD)
- Remove duplicate test files (Controllers/ dir duplicates)
- Remove old controllers and routes/settings.php
- Add routes/mcp.php placeholder
- Add API Keys nav item to settings layout
- Fix LazilyRefreshDatabase conflict in API tests
- 698 tests passing
2026-03-29 20:14:23 -03:00
Paulo Castellano
a6517f4b5b feat: Introduce a self-hosted mode, refactor email notifications to use Mailable classes with new templates, and update the project README. 2026-01-18 17:48:12 -03:00
Paulo Castellano
af32361e89 chore: Update and add various project dependencies. 2026-01-18 16:46:27 -03:00
Paulo Castellano
adfba2755e feat: Add Pinterest integration and user language preferences
Pinterest Integration:
- Add Pinterest OAuth controller and routes
- Add PinterestPublisher service with support for pins, video pins, and carousels
- Add PinterestPreview component with board selector and content type options
- Add Pinterest content types enum (Pin, VideoPin, Carousel)
- Add Pinterest to Platform enum with proper configuration
- Support sandbox mode via PINTEREST_SANDBOX env variable
- Pass platform-specific data (boards) through PlatformPreview

Language Feature:
- Add languages table with migration
- Add Language model and seeder (en-US, pt-BR)
- Add LanguageCombobox component for profile settings
- Set default language (en-US) on user registration
- Add language_id foreign key to users table

UI Improvements:
- Refactor PlatformPreview to support contentTypeOptions, meta, and platformData props
- Move content type and board selectors into platform-specific preview components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 13:10:01 -03:00
Paulo Castellano
7042bcb5ae feat: Implement soft deletes for workspace hashtags and labels, and integrate content_type into the initial post platforms table creation. 2026-01-17 14:44:37 -03:00
Paulo Castellano
7c00c3387e feat: Implement user onboarding, subscription management, and refactor social integrations with new UI components and mail templates. 2026-01-16 23:46:30 -03:00
Paulo Castellano
7867fcce02 feat: Implement real-time post status updates and configurable social platform enabling/disabling. 2026-01-16 12:36:52 -03:00
Paulo Castellano
7c80d717b1 chore: first commit 2026-01-15 14:24:39 -03:00
Paulo Castellano
6890147aa6 chore: first commit 2026-01-14 22:13:44 -03:00