Commit graph

18 commits

Author SHA1 Message Date
Paulo Castellano
d8149ef058
Remove the automations module (#333)
* Remove the automations module

Drops the visual workflow builder end to end: actions, node runners,
commands, jobs, models, observers, policy, resources, requests, routes,
broadcast channel, scheduler entries, Horizon supervisor, Vue pages and
components, canvas undo/redo history, CodeEditor, translations, factories
and tests.

A new migration rewrites posts.created_via = 'automation' to 'web' and
drops the five automation tables. The CreatedVia::Automation enum case is
removed. The 2026-08-21 social-account identity migration now skips its
automation node repointing when the automations table no longer exists,
so it stays re-runnable after the drop.

Orphaned dependencies removed: simplepie/simplepie, @vue-flow/*,
codemirror and @codemirror/*.

* Drop the orphaned common.beta translation key

* Remove automation leftovers: ResolvableUrl rule, feed fixtures, useShortcut, nav badge

* Drop the unused chart wrapper and @unovis packages

* Address review: keep the shipped migration untouched, drop the dead previewOnly chain

- Restore 2026_08_21 migration to exactly what production ran; the
  rehearsal test now recreates the automations table it expects instead.
- Mark the drop migration's down() irreversible like its siblings.
- Simplify DropAutomationTablesMigrationTest to the sibling shape.
- Remove previewOnly / aiGenerateVariants: the only caller that set the
  prop was the deleted automation Generate node.
- Run pint over lang/*/common.php after the beta key removal.

* Exercise the drop migration against the real automation tables and their FKs

* Drop DuplicateIdentityRehearsalTest: it re-ran a frozen migration that reads the removed automations table
2026-09-05 11:03:23 -03:00
Paulo Castellano
1af705f3fd
Localize calendar date pickers to the active UI locale (#234)
* Localize calendar date pickers to the active UI locale.

Pass the Inertia locale into Reka calendars, translate a11y labels, and use dayjs LL/LLL for DatePicker display text.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Localize remaining dayjs date displays across the UI.

Route list/calendar/preview timestamps through localized LL/LLL helpers so formats follow the active UI locale instead of English or Portuguese-locked patterns.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Use scheduled-or-now timestamps in platform previews.

Drive preview labels from the post schedule when set, localize Discord without dayjs calendar(), and expose a single formatPreviewPostedAt helper.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix preview schedule wiring and Discord/Facebook timestamp labels.

Restore hasPickedTime from any saved scheduled_at, keep time on non-today Discord labels, and use a localized just-now fallback for unschedled Facebook previews.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix API key expiry day shift and tighten calendar range titles.

Format date-only expiry in UTC calendar days, and use compact dayjs titles for calendar day/week headers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Replace frontend date source greps with Inertia API key assertions.

Drop the Vue/TS file scanner and assert expiry calendar days through ApiKeyController props instead.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Localize calendar week titles with day-first dayjs formats.

Avoid English month-day order in week headers and chart axis labels so locales like pt-BR keep natural day-first dates.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Split preview timestamp formatting into named platform helpers.

Replace the style-switch formatPreviewPostedAt with clear per-platform helpers so call sites read as formatDiscordPreview / formatXPreview / etc.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 21:02:34 -03:00
Paulo Castellano
36ccc570e0 Rewrite the image cropper as a movable/resizable selection box
Replace the move-image-behind-a-fixed-frame model with a selection box over the fully visible (contain-fit) image: drag to move, corner handles to resize, locked to 1:1 for the square avatar/logo output. The default selection is inset so the crop outline is always visible, handles sit inside the frame so they are never clipped, and wheel/pinch zoom is swallowed. Reset drag state on open/close, guard pointer capture and multi-touch, and fail-safe the canvas encode. Update the crop copy in all 15 locales to match the selection model.
2026-07-04 09:56:31 -03:00
Paulo Castellano
2549a82e9b Address review: cropper mime/zoom/error fixes, harden browser test
- Output a canvas-encodable mime (jpeg/png/webp, else png) and keep the File's
  name/extension in sync, so non-encodable input (gif/svg/heic) no longer ships
  PNG bytes mislabeled as the original type.
- Clamp zoom to a maximum (8x cover) so scrolling in can't collapse the crop to
  a sub-pixel region.
- Handle undecodable/zero-dimension images (@error + naturalWidth guard) with a
  crop_error message instead of a permanently-disabled Save.
- Replace the str_contains(static::class) Vite heuristic with a dedicated
  BrowserTestCase ($fakesVite = false).
- The browser test now decodes the dispatched blob and asserts a 512x512 image,
  and uses route(..., absolute: false) instead of a hardcoded path.
- Remove tests/Browser/ProbeTest.php (committed debug scratch).
2026-07-03 22:07:23 -03:00
Paulo Castellano
c6369a6ddb Crop the avatar/logo before upload with a dependency-free cropper
Selecting an avatar or workspace logo now opens a crop dialog (drag + zoom)
before uploading, so the image is framed the way it renders. The crop is
performed client-side and the resized 512x512 result is what gets uploaded.

This reworks the idea from #131 without its cropper dependency: vue-advanced-cropper
was last released ~2 years ago and we did not want an unmaintained package for
something this load-bearing. What we need is narrow (fixed 1:1, a circle/square
mask, fixed-size output), so a small canvas-based cropper covers it:

- imageCrop.ts: pure transform math (cover-fit, clamp, zoom, viewport->source).
- ImageCropperDialog.vue: CSS-transform preview, pointer drag, wheel/button zoom,
  a ResizeObserver to measure the modal (no requestAnimationFrame timing hacks),
  and a canvas toBlob only on save.
- PhotoUpload.vue: opens the cropper on file select; the mask shape follows the
  display shape (round avatar / square logo) instead of always being round.
- crop_* strings added to all 15 locales.

Also installs Pest browser testing (pest-plugin-browser + Playwright) and adds a
browser test for the crop flow. TestCase only calls withoutVite() for non-browser
tests, since browser tests need the real Vite assets to boot the SPA. The Pest
browser server does not parse multipart uploads, so the test asserts the crop
dispatches the correct upload request; endpoint persistence stays covered by
ProfileUpdateTest.
2026-07-03 21:46:19 -03:00
Paulo Castellano
519175f9fd Translate the sidebar Beta badge and place it at the row end for RTL
Move the nav badge from the physical right-2 to the logical end-2 so it
sits at the end of the row in both directions (right in LTR, left in RTL).

Replace the hardcoded 'Beta' string with a global common.beta translation
key across all 15 locales.
2026-07-03 18:22:19 -03:00
Paulo Castellano
0619f2a0f4 refactor: address code review for per-workspace pricing
Bug fix
- Surface the localized "network already connected" message on the
  Facebook/Instagram/InstagramFacebook/LinkedInPage/Threads/YouTube OAuth
  callbacks: catch NetworkAlreadyConnectedException before the generic catch
  so the conflict no longer falls through to a generic error + Log::error.

Scope / dead code
- Remove the orphaned BillingController::checkout() + app.billing.checkout route
  (onboarding starts checkout directly); delete the now-dead DiscordWidget and
  useFeatureAccess composable; prune orphaned i18n keys left by removing the
  plan picker / upgrade dialog / count limits (billing.subscribe.*,
  accounts.limit_reached, workspaces.limit_reached, common.discord.*).
- Drop the unused `plan` prop from the usage page and the unused `label` from
  the onboarding persona payload (labels come from i18n); remove
  Persona::options()/label().

Conventions
- declare(strict_types=1) on the two new migrations.
- Extract autofill validation into AutofillBrandRequest (FormRequest).
- Drop the unused $plan param from AccountPolicy::swapPlan.
- CreateUser: drop the stale config('cashier.trial_days', 7) fallback (now 8).
- Rename LimitEnforcementTest to InvitePermissionTest; use Pest mock() helper in
  WorkspaceQuantitySyncTest; move shared test helpers into Pest.php.
- Memoize BillingCycle window() + subscription lookup.
2026-06-21 21:28:47 -03:00
Paulo Castellano
f41b28c31d feat(discord): global floating Discord widget with PostHog tracking
- Fixed bottom-right Discord button (indies-club style) linking to
  https://trypost.it/discord, mounted globally in AppSidebarLayout
- Captures discord.clicked (placement: floating_widget) via captureEvent
- i18n label + aria-label in common.php (en, es, pt-BR)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:23:03 -03:00
Paulo Castellano
9a692b4608 Enhance automation functionality: Introduce workflow variables and improve node validation
- Added support for workflow variables in automations, allowing users to define reusable values.
- Implemented validation for Generate nodes to ensure intended image counts align with selected accounts.
- Updated automation models and requests to handle new variables, including encryption for sensitive data.
- Enhanced UI to display variables and their management within the automation editor.
- Improved error handling for webhook and HTTP nodes to prevent requests to invalid URLs.
- Refactored various components for better context resolution during automation runs.
2026-06-11 15:47:29 -03:00
Paulo Castellano
b23ab0166e feat(automations): implement automation features and UI enhancements
- Added new automation-related routes and controllers for managing automations.
- Introduced automation nodes in the UI with distinct styles and interactions.
- Updated sidebar to include navigation for automations.
- Enhanced post creation logic to support automation metadata.
- Refactored content type and platform enums into types for better type safety.
- Added localization for automation-related terms in English, Spanish, and Portuguese.
- Improved error handling in various components to accommodate new features.
2026-05-24 09:17:19 -03:00
Paulo Castellano
d15450d2f0 feat: lightbox supports navigation, video, and click-outside-to-close
- Refactor ImagePreviewDialog to expose open(url, type) / openCollection(items, idx)
  via defineExpose; drop external props/state. GalleryBrowser and
  PostEditorComposer now call lightbox.open()/openCollection() instead of
  managing previewItem/previewIndex locally.
- Lightbox closes when clicking the empty space inside the wrapper (between
  image and arrows) using @click.self. Image click also closes; video click
  triggers play/pause without bubbling to close. Arrow buttons keep
  @click.stop so navigation never closes.
- Assets gallery (uploads / Unsplash / Giphy) now opens the lightbox in
  collection mode so the user can navigate the full tab list with arrows
  or keyboard.
- Add 'delete' typed-confirmation on the post-delete dialog (Index +
  Edit pages) and on the asset-delete dialog using the new
  common.confirm_modal.delete_keyword translation (en: 'delete',
  pt-BR: 'deletar', es: 'eliminar').
2026-05-08 19:16:38 -03:00
Paulo Castellano
3ae4a3653a refactor: localize analytics metrics and overhaul UI components and dashboard layouts 2026-05-06 15:39:10 -03:00
Paulo Castellano
acdc78f8bd refactor: standardize UI components, modernize localization structure, and remove legacy appearance management middleware. 2026-05-06 14:26:18 -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
f3605717c7 refactor: unify social analytics, reorganize workspace settings, and implement content validation rules 2026-05-02 12:22:42 -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
c694d966d7 feat: complete i18n audit fix, toast system, UI improvements
i18n (all 3 languages - EN, ES, PT-BR):
- ApiKeys page + CreateApiKeyDialog: 26 strings
- billing/Processing page: 8 strings
- onboarding/Step1 + Step2: 10 strings (new onboarding.php)
- UserMenuContent: 7 strings (Account, Theme, Language, Log out)
- PhotoUpload: 4 strings (Upload, Remove, hint)
- TimezoneCombobox: 3 strings
- DatePicker: 1 string
- workspaces/Create + Index: full i18n (new workspaces.php)

Toast system:
- Unify Toast.vue with Sonner (copied from sendkit)
- Flash messages + JS toasts use same system
- Icon colors via CSS (success/error/warning/info)
- Position bottom-right

UI improvements:
- Posts Edit: move trash/schedule/publish to header-right slot
- Posts Edit: labels/hashtags/datepicker stay in top bar with platforms
- Posts Edit: delete post requires typing DELETE to confirm
- Posts Edit: Labels button with icon+text, always visible
- DatePicker: w-full by default, w-auto via class override
- Fix "Etiquetas" -> "Etiqueta" (remove plural)
- Posts breadcrumbs: /posts shows "Posts > All Posts"
- Calendar breadcrumbs: "Posts > Calendar"
- Edit breadcrumbs: "Posts > Edit Post"
- Remove unused AppHeader.vue and AppHeaderLayout.vue

All 728 tests passing.
2026-03-30 14:06:10 -03:00
Paulo Castellano
f3f72afecd refactor: auth split layout, subscribe redesign, onboarding, i18n, cookie locale
Auth pages:
- Create AuthSplitLayout with animated feature slides (6 slides, 3 languages)
- All auth pages use split layout (form left, visual right)
- Add show/hide password toggle with tooltip on Register
- Legal footer only shown on Register via showLegal prop

Subscribe page:
- Redesign to match auth card pattern (centered, clean)
- Platform icons, feature checklist, dynamic trial days (trialDays - 1)
- Add "Switch workspace" link
- Full i18n (en, es, pt-BR)

Onboarding:
- Rename URLs: step1 -> role, step2 -> connect
- Add enforceStep() to prevent skipping/going back steps
- Redirect /onboarding to /onboarding/role
- Redesign Step2 with AuthSplitLayout and compact platform list
- 21 tests covering all step enforcement scenarios

Workspaces page:
- Redesign with AuthSplitLayout (list with avatars, current badge)

Language system:
- Move locale from DB to cookie (forever, unencrypted, session.domain)
- Create SetLocale middleware (sets cookie if missing, validates against config)
- Rename lang/pt-br to lang/pt-BR
- Add dayjs es locale

Other:
- Copy utils.ts from sendkit (formatNumber, formatMoney, copyToClipboard)
- ConfirmDeleteModal with text confirmation (sendkit pattern)
- i18n for ConfirmDeleteModal internal strings (common.php)
- EmptyState component for posts index
- Exact match for "All" posts in sidebar
- Posts breadcrumbs show current status filter
- DialogFooter buttons aligned left
- API Keys page redesign with Table, DropdownMenu, EmptyState
- Extract CreateApiKeyDialog and InviteMemberDialog to components
- Remove API Keys from sidebar
- DropdownMenuItem destructive variant for Remove action
2026-03-30 11:53:42 -03:00