Commit graph

571 commits

Author SHA1 Message Date
Paulo Castellano
ade899c010
Merge pull request #59 from trypostit/fix/facebook-multi-image-form-encoding
fix(facebook): multi-image posts publish as text-only
2026-05-21 21:17:29 -03:00
Paulo Castellano
eed1019301 fix(facebook): send Graph API requests as form-urlencoded
Multi-image posts were published as text-only because Laravel sent
attached_media as JSON; Facebook accepts message but drops attachments.
Use asForm() for all FacebookPublisher Graph API calls.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 21:07:03 -03:00
Paulo Castellano
277093e52e
Merge pull request #58 from trypostit/fix/npm-audit-55
fix(security): resolve npm audit vulnerabilities
2026-05-21 20:37:32 -03:00
Paulo Castellano
a26aaafb65 fix(security): resolve npm audit in maizzle email toolchain
Apply npm audit fix to the separate maizzle package lockfile (14 vulnerabilities).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 20:29:32 -03:00
Paulo Castellano
c2134505e1 fix(security): resolve npm audit vulnerabilities
Bump transitive dependencies via npm audit fix to address 20 reported
vulnerabilities (including critical protobufjs). Closes #55.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 20:28:05 -03:00
Paulo Castellano
7a598615c3
Merge pull request #57 from trypostit/feat/ai-image-regenerate
feat: regenerate AI post images with brand palette
2026-05-21 20:25:27 -03:00
Paulo Castellano
0e204342a1 refactor(posts): standardize MediaItem type imports across components
- Added consistent imports of the MediaItem type across various components, including FacebookSettings, InstagramSettings, LinkedInSettings, PinterestSettings, PreviewTab, InstagramPreview, and PlatformPreview.
- This change enhances code organization and ensures uniformity in type definitions throughout the project.
2026-05-21 20:23:20 -03:00
Paulo Castellano
b980212225
Merge branch 'main' into feat/ai-image-regenerate 2026-05-21 20:23:02 -03:00
Paulo Castellano
666402c59f feat(billing): add configuration for trial signup card requirement
Introduced a new environment variable `REQUIRE_CARD_FOR_TRIAL` to control whether a credit card is required for trial signups. Updated the billing configuration to reflect this setting, allowing for more flexible trial management.
2026-05-21 20:19:45 -03:00
Paulo Castellano
e91e0e4c29
Merge branch 'main' into feat/ai-image-regenerate 2026-05-21 20:16:16 -03:00
Paulo Castellano
c18c1b9c0d
Merge pull request #56 from trypostit/fix/require-card-for-trial
fix(billing): require card-backed trial again
2026-05-21 20:12:18 -03:00
Paulo Castellano
2fff7f66f3 fix(billing): update default trial days to 8 in cashier config
Increase the default trial duration from 7 to 8 days in the cashier configuration to better align with user feedback and improve trial experience.
2026-05-21 20:04:16 -03:00
Paulo Castellano
91a144142f feat(posts): introduce change_mode for flexible image and text regeneration
- Replaced the `regenerate_image` boolean with a `change_mode` string parameter in the PostImageRegenerator schema, allowing for more granular control over regeneration options.
- Updated methods in RegeneratePostMediaImage to handle the new `change_mode` values: `image_only`, `text_only`, and `both`.
- Enhanced documentation and comments to clarify the new regeneration logic.
- Added tests to verify correct behavior for different regeneration modes, ensuring that text and image changes are handled appropriately.
2026-05-21 20:00:41 -03:00
Paulo Castellano
33e53f5bca feat(posts): add regenerate_image option for background image handling
- Introduced a new `regenerate_image` boolean parameter in the PostImageRegenerator schema to control background image regeneration.
- Updated relevant methods in RegeneratePostMediaImage and TemplateImageGenerator to utilize the new parameter for reusing existing backgrounds when appropriate.
- Enhanced documentation and comments to clarify the usage of the `regenerate_image` option.
- Added tests to ensure correct behavior when reusing background images.
2026-05-21 19:52:12 -03:00
Paulo Castellano
8a9ff81a96 refactor(posts): simplify AiRegenerateImageDialog logic and enhance type safety
- Replaced custom regeneration logic with a composable for improved organization and reusability.
- Streamlined state management and error handling, enhancing clarity and maintainability.
- Removed unused imports and interfaces, reducing code complexity and improving readability.
- Updated computed properties for better form submission validation and user experience.
2026-05-21 19:40:42 -03:00
Paulo Castellano
72c9c93a85 refactor(posts): replace PostStatusGuard with PostStatusRules for editing and deletion checks
- Removed the PostStatusGuard class and replaced its usage with the new PostStatusRules utility across multiple controllers and actions, enhancing code organization and maintainability.
- Updated error message handling to utilize the centralized method in PostStatusRules, ensuring consistency in user feedback.
- Deleted associated tests for PostStatusGuard, reflecting the removal of the class.
2026-05-21 19:32:42 -03:00
Paulo Castellano
7854596579 refactor(posts): centralize post editing status checks with PostStatusGuard
- Replaced direct status checks in multiple controllers and actions with the PostStatusGuard utility, improving code readability and maintainability.
- Updated error messages to utilize a centralized method for consistency across the application.
- Removed the BrandImagePalette class, consolidating color resolution logic into the AiImageClient for better organization and type safety.
2026-05-21 19:27:19 -03:00
Paulo Castellano
4d8452dbc8 refactor(posts): enhance regeneration dialog logic and type safety
- Improved type definitions for regeneration status and event handling, increasing clarity and maintainability.
- Streamlined the regeneration result handling by introducing a dedicated function to manage success and error states.
- Added computed properties to optimize form submission logic, ensuring better user experience and validation.
- Refactored the subscription logic to improve readability and error handling during media regeneration.
2026-05-21 19:12:50 -03:00
Paulo Castellano
a008555589 refactor(posts): streamline media item handling and improve type definitions
- Refactored the media item interface by moving it to a dedicated type file, enhancing code organization and reusability across components.
- Updated various components to import the new MediaItem type, ensuring consistency and reducing redundancy in type definitions.
- Improved the RegeneratePostMediaImage job by optimizing the media regeneration process and enhancing error handling for better reliability.
2026-05-21 19:08:46 -03:00
Paulo Castellano
98632404a9 chore: update AI agent guidelines and refactor prompt handling
- Added guidelines for AI agents to avoid embedding prompts in PHP and to store them in Blade files under `resources/views/prompts/`.
- Updated `PostImageRegenerator` to render prompts using Blade instead of heredocs, improving maintainability and consistency with project standards.
- Enhanced documentation in `CLAUDE.md` and `.cursor/rules` files to reflect these changes.
2026-05-21 18:53:37 -03:00
Paulo Castellano
560393db2a feat: regenerate AI post images with brand palette in editor
Let users adjust AI-generated slides in place via async job and Echo, while applying workspace brand, background, and text colors to image prompts. Autofill swaps site text/background colors for the image palette, and regeneration is blocked on finalized posts with safer job cleanup.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 16:27:55 -03:00
Paulo Castellano
e8fcbfa230 refactor(billing): make trial toggle fixed in trypost config
Keep the trial card requirement switch as a plain boolean in config/trypost.php so changing the app flow is a single in-repo config edit without env wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 10:09:20 -03:00
Paulo Castellano
3611e882b4 feat(billing): make trial card requirement configurable
Add a trypost config toggle to switch between card-required checkout trials and no-card signup trials, and wire signup, checkout, access gating, UI copy, and tests to both modes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 10:02:38 -03:00
Paulo Castellano
a8f999f45f chore(billing): surface trial copy and rename signup test
Show the checkout-based trial duration again on the subscribe page and rename the signup billing test file so it matches the current no-trial-at-signup behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 09:57:46 -03:00
Paulo Castellano
26c738c0ba fix(billing): require card-backed trial again
Revert the no-card signup trial flow so access depends on a Stripe subscription trial started at checkout, preventing app access before a payment method is collected.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 09:49:44 -03:00
Paulo Castellano
a4e1757535
Merge pull request #54 from trypostit/chore/cursor-rules
chore(cursor): add Cursor project rules
2026-05-19 19:28:16 -03:00
Paulo Castellano
4c9243ccb9 chore(cursor): add Cursor rules for Laravel and frontend stacks
Ship .cursor/rules MDC files covering project context, Laravel patterns,
Boost tooling, PHP style, Inertia v3/pagination, Vue/TypeScript, Pest, and Dusk.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 19:26:49 -03:00
Paulo Castellano
5f40b7bd6a
Merge pull request #43 from trypostit/feat/posthog-onboarding-properties
feat(posthog): keep social_accounts_count and posts_count fresh on account group
2026-05-19 19:25:58 -03:00
Paulo Castellano
da0d5f2a42 fix(posthog): simplify user identification and account usage sync
Refactor the user identification process by removing unnecessary type casting for user ID and account ID. This change enhances code readability and maintains functionality for syncing account usage.
2026-05-19 19:20:56 -03:00
Paulo Castellano
ccd78e3c8d fix(posthog): harden usage sync after deletes and workspace removal
Use Account::postsCountCacheKey everywhere, avoid findOrFail when syncing
post deletes, dispatch SyncAccountUsage after DeleteWorkspace when enabled,
and add coverage for the new paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 19:01:53 -03:00
Paulo Castellano
a659a9eadf
Merge branch 'main' into feat/posthog-onboarding-properties 2026-05-19 17:00:34 -03:00
Paulo Castellano
ac1a362f2a
Merge pull request #41 from trypostit/fix/facebook-empty-message
fix(facebook): empty-message rejection + state consistency + no re-publish on terminal
2026-05-19 17:00:16 -03:00
Paulo Castellano
35051350c6 refactor(posts): encode scheduled_at for API via date helper
Parse datetime-local values in the user timezone before converting to UTC,
matching formatUtcForDateTimeLocalInput. Fix ESLint import order in usePostCompliance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 16:42:42 -03:00
Paulo Castellano
df35d1b671 feat(posts): add validation message for past date selection in PickTimePopover
- Introduced a new translation key for future date selection prompts in English, Spanish, and Portuguese.
- Implemented logic to disable the confirm button and display a warning message when a past date is selected in the PickTimePopover component.
- Updated date formatting utility to handle UTC dates for HTML datetime-local inputs.
2026-05-19 16:26:40 -03:00
Paulo Castellano
1d61904bc8 refactor(posts): collapse TikTok/Pinterest meta rules into PLATFORM_META_RULES map 2026-05-19 15:36:51 -03:00
Paulo Castellano
3580d2eaf6 refactor(posts): flatten togglePlatform with snapToCompatibleVariant helper
Pull the variant-snap branch out of togglePlatform into a named
helper with guard clauses. togglePlatform now reads top-down:
lock check → deselect short-circuit → snap → select.
2026-05-19 15:15:13 -03:00
Paulo Castellano
2584257ee6 refactor(posts): extract Edit.vue validation logic into usePostCompliance
Pulls ~200 lines of media/platform/length compliance and tooltip
derivation into composables/usePostCompliance.ts. Edit.vue is now
focused on state, save logic and event handlers; PLATFORM_VARIANTS
and the per-content-type media-rule check are exposed from the
composable for togglePlatform.
2026-05-19 15:11:58 -03:00
Paulo Castellano
cad89a0a97 revert(posts): drop redundant router.visit in Edit.vue Echo handler
The backend edit() controller already redirects posts in terminal
states to /show; Inertia follows that redirect on partial reload.
2026-05-19 14:57:38 -03:00
Paulo Castellano
e4f8833608 fix(posts): allow deleting Failed posts + misc terminal-state polish
- Add separate canDelete predicate in Index.vue that includes Failed —
  the previous EDITABLE_STATUSES gating hid the delete button for
  failed posts even though the backend allows deleting them.
- Edit.vue Echo handler navigates to /show when an in-page real-time
  status update transitions the post into a read-only state, instead
  of leaving the user on a stuck readonly editor.
- FacebookPublisher: stop using empty() for content checks (treats
  literal "0" as empty) — compare explicitly against null and "".
- Update terminal-state error messages in API + MCP update tool to
  reflect the broadened guard (no longer Published-only). Adjust the
  matching test assertion.
2026-05-19 14:54:04 -03:00
Paulo Castellano
31db72638b fix(posts): align Index/Calendar routing with Failed→show redirect
- Drop 'failed' from EDITABLE_STATUSES in Index.vue and Calendar.vue
  so failed posts link directly to /show instead of /edit (which now
  redirects).
- Replace remaining 'scheduled'/'publishing' literals in
  PostEditorHeader.vue with the PostStatus enum.
- Include Failed in PostEditorHeader's READONLY_STATUSES to match
  Edit.vue.
- Split PLATFORM_VARIANTS by Platform key (Platform.LinkedInPage and
  Platform.InstagramFacebook were missing; page content types were
  grouped under the wrong personal-LinkedIn key).
2026-05-19 14:38:05 -03:00
Paulo Castellano
2902506f35 refactor(posts): replace status string literals with PostStatus enum
Introduce resources/js/types/post.ts mirroring App\Enums\Post\Status
and App\Enums\PostPlatform\Status. Replace inline string comparisons
across Edit.vue, Show.vue, PostEditorHeader.vue and ScheduleTab.vue.
2026-05-19 14:27:14 -03:00
Paulo Castellano
99d1770c6c fix(post): edit redirect loop + frontend validations
- PostController@edit was redirecting Failed→show while show was
  redirecting Failed→edit, producing ERR_TOO_MANY_REDIRECTS. Failed
  posts now render in show.
- New universal `hasContentOrMedia` rule in Edit.vue blocks publishing
  when both text and media are empty (closes the hole where empty posts
  could reach the publish button).
- Unified `PLATFORM_VARIANTS` to include Facebook, Instagram and
  LinkedIn variants. togglePlatform snaps to a compatible variant when
  reselecting a platform whose current content_type is incompatible
  with the attached media (fixes the case where Reel+image left the
  tile permanently blocked).
- platformIssues suppresses the issue on deselected tiles when a
  compatible variant exists, so the tile remains clickable and the
  snap can recover state.
- Use ContentType enum in place of string literals.
2026-05-19 14:18:13 -03:00
Paulo Castellano
0c955e885f test(post): cover all 4 terminal statuses on MCP update + API update
UpdatePost::execute returns Finalized for Published, PartiallyPublished,
Failed, and Publishing — the four "terminal" states the PR introduced.
The existing tests for UpdatePostTool (MCP) and Api/PostController only
exercised the Published path. Convert both to a Pest dataset over all
four statuses so a future regression that lets one state slip through
the check fails loudly.

Same shape already used for PublishPostTool and App/PostController.
2026-05-19 13:19:01 -03:00
Paulo Castellano
d8f836a569 chore(i18n): drop orphan cannot_edit_published key + gitignore compiled JSONs
- Remove cannot_edit_published from en/es/pt-BR posts.php — its sole
  caller (App/PostController) was deleted in the previous commit
  along with PostAction::AlreadyPublished.
- Add lang/php_*.json to .gitignore. They are build artifacts emitted
  by laravel-vue-i18n/vite from the lang/{locale}/*.php sources and
  shouldn't have been tracked (regenerated on every npm run build /
  composer dev). Untracking now also means the orphan key disappears
  from the compiled bundles on the next build without a manual edit.
2026-05-19 13:12:13 -03:00
Paulo Castellano
edec58af81 refactor(post): remove now-dead PostAction::AlreadyPublished
UpdatePost::execute used to return AlreadyPublished for the Published
short-circuit. This PR widened the short-circuit to four terminal
statuses and consolidated them under PostAction::Finalized — so the
old enum case stopped being emitted, and every caller already had a
defensive in_array([AlreadyPublished, Finalized], ...).

Audit before removal: nothing emits AlreadyPublished anymore (only
UpdatePost::execute returns Actions, and it returns Finalized for
the whole terminal set), no test references the case, and no string
'already_published' exists elsewhere in app/resources/tests/lang.

- Drop the enum case
- Simplify the three in_array checks to a direct === Finalized
- Delete the dead App/PostController branch that flashed the old
  cannot_edit_published message (its successor branch with
  cannot_edit_finalized stays). The old i18n key is left in lang/
  for now — orphan but harmless, can ressuscitate if a similar
  flash is added back.
2026-05-19 13:06:13 -03:00
Paulo Castellano
27f27b6683 fix(mcp): publish-post-tool blocks terminal states, not only Published
The earlier rename of UpdatePost's terminal short-circuit from
AlreadyPublished → Finalized was applied to UpdatePostTool and
Api/PostController but missed PublishPostTool. Result: calling
publish-post-tool against a post in Published / PartiallyPublished /
Failed / Publishing returned the unchanged post wrapped as a success
response — the MCP client thought it had republished.

The check now accepts both action enums so the existing string
behavior (clear error) is preserved, and a Pest dataset covers all
four terminal statuses.
2026-05-19 12:59:40 -03:00
Paulo Castellano
3f6032c152 fix(facebook): empty-message rejection + state consistency + no re-publish on terminal
Production incident: a customer's Facebook Page post failed with 'The post
is empty. Please enter a message to share.' (error code 197) and ended up
with a contradictory DB state (status=published + error_message=set).

Three independent bugs were uncovered:

A. FacebookPublisher sends 'message'/'description' as null when the user
   posts media without text. Graph API requires the key be omitted, not
   null. Fixed in publishSingleImagePost, publishMultiImagePost,
   publishVideoPost, publishReel.

B. markAsPublished/markAsFailed leak stale fields across transitions
   (a published row could retain error_message from a prior failure,
   vice-versa). Both transitions now explicitly clear the opposite
   side's fields.

C. status='failed' was editable in the UI and the backend, so users
   were re-clicking Publish, generating duplicate failure emails and
   the contradictory state from bug B. The frontend isReadOnly check
   and the UpdatePost backend guard now treat Published/PartiallyPublished/
   Failed/Publishing as terminal. To retry, the user duplicates the post.

11 new tests guarantee these can't regress silently: FB payload shape
per content type, PostPlatform field-clearing on transitions, and the
terminal-status block at the controller level.
2026-05-19 12:47:18 -03:00
Paulo Castellano
19f3b02941
Merge pull request #53 from trypostit/fix/digitalocean-spaces-env-naming
feat(storage): DigitalOcean Spaces disk + env documentation
2026-05-19 12:39:24 -03:00
Paulo Castellano
91334104a0 feat(storage): DigitalOcean Spaces disk + env documentation
Adds 'spaces' to config/filesystems.php so self-hosters can switch
FILESYSTEM_DISK=spaces without bolting onto the generic s3 disk. Env
vars follow the existing R2/AWS convention (SPACES_ACCESS_KEY_ID /
SPACES_SECRET_ACCESS_KEY), and use_path_style_endpoint is omitted so
the SDK defaults to virtual-hosted style — the canonical request shape
for DO Spaces.

Also documents the five env vars in .env.example. Docs in the
trypost-docs repo describe the matching FILESYSTEM_DISK=spaces stanza.
2026-05-19 12:38:29 -03:00
Paulo Castellano
eae702e620
Merge pull request #47 from albertvisuals/main
Fix: JSON columns with default('[]') fail on MySQL 8
2026-05-19 12:26:05 -03:00