2026-01-21 23:50:57 +00:00
< ? php
return [
'title' => 'Settings' ,
'description' => 'Manage your profile and account settings' ,
2026-05-03 18:54:26 +00:00
'hub' => [
'title' => 'Settings' ,
'description' => 'Choose what you want to manage.' ,
'profile' => [
'title' => 'Profile' ,
'description' => 'Update your personal info, password, and notification preferences.' ,
],
'workspace' => [
'title' => 'Workspace' ,
'description' => 'Configure your workspace, brand, members, and API keys.' ,
],
'account' => [
'title' => 'Account' ,
'description' => 'Manage your account info, usage, and billing.' ,
],
],
2026-01-21 23:50:57 +00:00
'nav' => [
'profile' => 'Profile' ,
2026-05-03 20:42:44 +00:00
'authentication' => 'Authentication' ,
2026-01-21 23:50:57 +00:00
'workspace' => 'Workspace' ,
'members' => 'Members' ,
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 21:18:17 +00:00
'notifications' => 'Notifications' ,
2026-01-21 23:50:57 +00:00
'billing' => 'Billing' ,
],
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 21:18:17 +00:00
'notifications' => [
'title' => 'Notification preferences' ,
'heading' => 'Email notifications' ,
'description' => 'Choose which email notifications you want to receive' ,
'post_published' => 'Post published' ,
'post_published_description' => 'Receive an email when your post is published successfully' ,
'post_failed' => 'Post failed' ,
'post_failed_description' => 'Receive an email when your post fails to publish' ,
'account_disconnected' => 'Account disconnected' ,
'account_disconnected_description' => 'Receive an email when a social account is disconnected' ,
'save' => 'Save preferences' ,
],
2026-01-21 23:50:57 +00:00
'profile' => [
'title' => 'Profile settings' ,
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 03:20:43 +00:00
'photo_heading' => 'Profile photo' ,
'photo_description' => 'Upload a profile photo' ,
2026-01-21 23:50:57 +00:00
'heading' => 'Profile information' ,
'description' => 'Update your name and email address' ,
'avatar' => 'Avatar' ,
'name' => 'Name' ,
'name_placeholder' => 'Full name' ,
'email' => 'Email address' ,
'email_placeholder' => 'Email address' ,
'email_unverified' => 'Your email address is unverified.' ,
'resend_verification' => 'Click here to resend the verification email.' ,
'verification_sent' => 'A new verification link has been sent to your email address.' ,
'save' => 'Save' ,
],
2026-05-03 20:42:44 +00:00
'authentication' => [
'title' => 'Authentication' ,
'page_title' => 'Authentication settings' ,
'sessions' => [
'title' => 'Active sessions' ,
'description' => 'If you notice anything suspicious, sign out of other devices.' ,
'unknown_browser' => 'Unknown browser' ,
'unknown_ip' => 'Unknown IP' ,
'on' => 'on' ,
'active_now' => 'Active now' ,
'log_out_others' => 'Log out other devices' ,
'modal_title' => 'Log out other devices' ,
'modal_description_password' => 'Enter your current password to confirm you want to log out other browser sessions.' ,
'modal_description_email' => 'Type your email address to confirm you want to log out other browser sessions.' ,
'password_placeholder' => 'Current password' ,
'email_placeholder' => 'Your account email' ,
'cancel' => 'Cancel' ,
'submit' => 'Log out other devices' ,
'email_mismatch' => 'The email address does not match your account.' ,
'flash_logged_out' => 'You have been logged out from other devices.' ,
],
'password' => [
'update_title' => 'Update password' ,
'set_title' => 'Set a password' ,
'update_description' => 'Ensure your account is using a long, random password to stay secure.' ,
'set_description' => 'Add a password so you can sign in without a connected provider.' ,
'current_password' => 'Current password' ,
'new_password' => 'New password' ,
'confirm_password' => 'Confirm password' ,
'save' => 'Save password' ,
'set' => 'Set password' ,
],
'providers' => [
'title' => 'Connected accounts' ,
'description' => 'Sign in faster with these connected providers.' ,
'connected' => 'Connected' ,
'not_connected' => 'Not connected' ,
'connect' => 'Connect' ,
'disconnect' => 'Disconnect' ,
'flash_disconnected' => ':provider disconnected successfully.' ,
2026-05-04 22:22:09 +00:00
'flash_connected' => ':provider connected successfully.' ,
'flash_already_linked' => 'That :provider account is already linked to another user.' ,
2026-05-03 20:42:44 +00:00
'flash_cannot_disconnect' => 'You cannot disconnect your only sign-in method. Set a password or connect another provider first.' ,
],
2026-01-21 23:50:57 +00:00
],
'delete_account' => [
'heading' => 'Delete account' ,
'description' => 'Delete your account and all of its resources' ,
'warning' => 'Warning' ,
'warning_message' => 'Please proceed with caution, this cannot be undone.' ,
'button' => 'Delete account' ,
'modal_title' => 'Are you sure you want to delete your account?' ,
2026-05-03 20:42:44 +00:00
'modal_description_password' => 'Once your account is deleted, all of its resources and data will also be permanently deleted. Please enter your password to confirm.' ,
'modal_description_email' => 'Once your account is deleted, all of its resources and data will also be permanently deleted. Please type your email address :email to confirm.' ,
2026-01-21 23:50:57 +00:00
'password' => 'Password' ,
'password_placeholder' => 'Password' ,
2026-05-03 20:42:44 +00:00
'email_placeholder' => 'Your account email' ,
'email_mismatch' => 'The email address does not match your account.' ,
2026-01-21 23:50:57 +00:00
'cancel' => 'Cancel' ,
'confirm' => 'Delete account' ,
],
'workspace' => [
feat: @mentions in comments, AI Action layer + MCP tools, settings tabs
Mentions in post comments
- @mention autocomplete (workspace members, current user excluded) with
marker syntax @[uuid] persisted, display names rendered via CommentBody
chips; live edit replaces markers with names and converts back on save.
- NotifyMentions action with workspace-scoped membership check, dedupes
same user, only newly-added mentions on update.
- Email + in-app via SendNotification job, respecting per-user
notification_preferences.mentioned_in_comment.
- Heartbeat-based presence (Cache, 60s TTL, 30s ping) so online recipients
get only the in-app notification — no email noise.
- Real-time bell on workspace.{id}.user.{id} private channel
(NotificationCreated event), scoped channel name avoids client-side
filtering and lays out a convention for future workspace channels.
- Mailable localized via lang/{en,es,pt-BR}/mail.php; Maizzle source
template for the email is committed and built into resources/views/mail.
AI generation refactor (Action layer + MCP)
- Extracted Actions/Ai/Generate{Image,Video} with QuotaExhaustedException
so agent tools and MCP tools share a single domain entry point.
- Mcp/Tools/Ai/Generate{Image,Video}Tool registered in TryPostServer; both
return MediaResource payloads.
- Orientation::imageApiSize maps non-OpenAI ratios to 1:1/2:3/3:2.
- config/ai.php is now the single source of truth driven by env, removing
the trypost.ai shim. Default text/image providers flipped to OpenAI.
Settings/UX
- /settings/workspace split into shadcn Tabs (Workspace / Brand / Users)
with three components.
- /assets and the in-editor MediaPicker open the ImagePreviewDialog
lightbox on image click while preserving action button behaviour.
- Comments tab landed via ?tab=comments&comment=<id> from notification
click (scroll-to + temporary highlight).
- Mention autocomplete popover flips above when near the viewport bottom.
- Real social platform PNGs replace Tabler brand glyphs in schedule
pills and post list, with hover tooltip carrying display_name + handle.
Bug fixes
- AcceptInvite: controller now passes workspace + role payload that the
Vue page expects; login/register CTAs preselect the invite email.
- WorkspaceInvite mailable: stopped referencing nonexistent
$invite->workspace and $invite->role; column added to the migration,
Invite model casts role to WorkspaceRole, CreateInvite persists it.
- PostCommentCreated: added broadcastAs so .PostCommentCreated actually
matches the Echo listener; payload now includes mentioned_users so
receivers render the chip correctly without a refetch.
- Preview components for X/Pinterest/Threads/Bluesky/LinkedIn/Mastodon/
TikTok/YouTube switched from item.type === 'image' to
!isVideoMedia(item) so media without a persisted type still renders.
- UpdatePostRequest now accepts media.*.{type,mime_type,size,...} so the
posts.media JSON keeps the metadata that the previews need.
- Removed throttle:6,1 from social connect routes (was 429ing legitimate
OAuth retries).
- Used MediaType enum cases instead of literal 'image'/'video' strings
when creating media rows.
Tests
- MentionParser unit tests, NotifyMentions feature tests including
online/offline channel selection and preference gating, MCP AI tool
happy paths, MentionedInComment mailable rendering, AcceptInvite +
search-members + index mentioned_users path. 1229 passing.
2026-05-01 23:59:03 +00:00
'tabs' => [
'workspace' => 'Workspace' ,
'brand' => 'Brand' ,
2026-05-02 15:22:42 +00:00
'users' => 'Members' ,
'api_keys' => 'API Keys' ,
feat: @mentions in comments, AI Action layer + MCP tools, settings tabs
Mentions in post comments
- @mention autocomplete (workspace members, current user excluded) with
marker syntax @[uuid] persisted, display names rendered via CommentBody
chips; live edit replaces markers with names and converts back on save.
- NotifyMentions action with workspace-scoped membership check, dedupes
same user, only newly-added mentions on update.
- Email + in-app via SendNotification job, respecting per-user
notification_preferences.mentioned_in_comment.
- Heartbeat-based presence (Cache, 60s TTL, 30s ping) so online recipients
get only the in-app notification — no email noise.
- Real-time bell on workspace.{id}.user.{id} private channel
(NotificationCreated event), scoped channel name avoids client-side
filtering and lays out a convention for future workspace channels.
- Mailable localized via lang/{en,es,pt-BR}/mail.php; Maizzle source
template for the email is committed and built into resources/views/mail.
AI generation refactor (Action layer + MCP)
- Extracted Actions/Ai/Generate{Image,Video} with QuotaExhaustedException
so agent tools and MCP tools share a single domain entry point.
- Mcp/Tools/Ai/Generate{Image,Video}Tool registered in TryPostServer; both
return MediaResource payloads.
- Orientation::imageApiSize maps non-OpenAI ratios to 1:1/2:3/3:2.
- config/ai.php is now the single source of truth driven by env, removing
the trypost.ai shim. Default text/image providers flipped to OpenAI.
Settings/UX
- /settings/workspace split into shadcn Tabs (Workspace / Brand / Users)
with three components.
- /assets and the in-editor MediaPicker open the ImagePreviewDialog
lightbox on image click while preserving action button behaviour.
- Comments tab landed via ?tab=comments&comment=<id> from notification
click (scroll-to + temporary highlight).
- Mention autocomplete popover flips above when near the viewport bottom.
- Real social platform PNGs replace Tabler brand glyphs in schedule
pills and post list, with hover tooltip carrying display_name + handle.
Bug fixes
- AcceptInvite: controller now passes workspace + role payload that the
Vue page expects; login/register CTAs preselect the invite email.
- WorkspaceInvite mailable: stopped referencing nonexistent
$invite->workspace and $invite->role; column added to the migration,
Invite model casts role to WorkspaceRole, CreateInvite persists it.
- PostCommentCreated: added broadcastAs so .PostCommentCreated actually
matches the Echo listener; payload now includes mentioned_users so
receivers render the chip correctly without a refetch.
- Preview components for X/Pinterest/Threads/Bluesky/LinkedIn/Mastodon/
TikTok/YouTube switched from item.type === 'image' to
!isVideoMedia(item) so media without a persisted type still renders.
- UpdatePostRequest now accepts media.*.{type,mime_type,size,...} so the
posts.media JSON keeps the metadata that the previews need.
- Removed throttle:6,1 from social connect routes (was 429ing legitimate
OAuth retries).
- Used MediaType enum cases instead of literal 'image'/'video' strings
when creating media rows.
Tests
- MentionParser unit tests, NotifyMentions feature tests including
online/offline channel selection and preference gating, MCP AI tool
happy paths, MentionedInComment mailable rendering, AcceptInvite +
search-members + index mentioned_users path. 1229 passing.
2026-05-01 23:59:03 +00:00
],
2026-01-21 23:50:57 +00:00
'title' => 'Workspace settings' ,
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 03:20:43 +00:00
'logo_heading' => 'Workspace logo' ,
'logo_description' => 'Upload a logo for your workspace' ,
'heading' => 'Workspace name' ,
2026-04-17 02:05:51 +00:00
'description' => 'Update your workspace name' ,
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 03:20:43 +00:00
'members_heading' => 'Members' ,
'members_description' => 'Manage workspace members and invitations' ,
2026-01-21 23:50:57 +00:00
'name' => 'Name' ,
'name_placeholder' => 'My Workspace' ,
'save' => 'Save' ,
],
2026-04-16 00:28:40 +00:00
'brand' => [
'title' => 'Brand' ,
'description' => 'Configure your brand identity for AI-generated content.' ,
2026-05-08 16:38:30 +00:00
'name' => 'Workspace name' ,
'name_placeholder' => 'My brand' ,
2026-04-16 00:28:40 +00:00
'website' => 'Website' ,
'website_placeholder' => 'https://yourbrand.com' ,
'brand_description' => 'Description' ,
'brand_description_placeholder' => 'Tell us about your brand, what you do, and who your audience is...' ,
2026-06-12 20:09:39 +00:00
'voice' => 'Brand voice' ,
'voice_description' => 'Pick the traits that define how your content sounds.' ,
'voice_group' => [
'pov' => 'Point of view' ,
'formality' => 'Formality' ,
'energy' => 'Energy' ,
'humor' => 'Humor' ,
'attitude' => 'Attitude' ,
'warmth' => 'Warmth' ,
'confidence' => 'Confidence' ,
'style' => 'Style' ,
],
'voice_trait' => [
'first_person' => 'First person' ,
'second_person' => 'Second person (you)' ,
'third_person' => 'Third person' ,
'formal' => 'Formal' ,
'balanced' => 'Balanced' ,
'casual' => 'Casual' ,
'calm' => 'Calm' ,
'moderate' => 'Moderate' ,
'enthusiastic' => 'Enthusiastic' ,
'vibrant' => 'Vibrant' ,
'serious' => 'Serious' ,
'dry' => 'Dry / subtle' ,
'witty' => 'Witty' ,
'playful' => 'Playful' ,
'respectful' => 'Respectful' ,
'even_handed' => 'Even-handed' ,
'bold' => 'Bold' ,
'provocative' => 'Provocative' ,
'neutral' => 'Neutral' ,
'friendly' => 'Friendly' ,
'empathetic' => 'Empathetic' ,
'humble' => 'Humble' ,
'confident' => 'Confident' ,
'assertive' => 'Assertive' ,
'direct' => 'Direct' ,
'concise' => 'Concise' ,
'transparent' => 'Transparent' ,
'no_hype' => 'No hype' ,
'practical' => 'Practical' ,
'data_driven' => 'Data-driven' ,
'storytelling' => 'Storytelling' ,
'inspirational' => 'Inspirational' ,
'educational' => 'Educational' ,
'technical' => 'Technical' ,
'minimalist' => 'Few emojis' ,
],
2026-05-02 16:30:59 +00:00
'brand_color' => 'Brand color' ,
'background_color' => 'Background color' ,
'text_color' => 'Text color' ,
2026-05-03 12:36:50 +00:00
'font' => 'Font' ,
2026-05-08 16:38:30 +00:00
'image_style' => 'Image style' ,
'image_style_description' => 'Visual style applied when generating slide and cover images for AI posts.' ,
'image_style_cinematic' => 'Cinematic' ,
'image_style_illustration' => 'Illustration' ,
'image_style_isometric_3d' => 'Isometric' ,
'image_style_cartoon' => 'Cartoon' ,
'image_style_typographic' => 'Typographic' ,
'image_style_infographic' => 'Infographic' ,
'image_style_minimalist' => 'Minimalist' ,
'image_style_mockup' => 'Mockup' ,
feat: add workspace content_language setting for AI generation
Users can now pick the language AI uses for captions, hashtags,
descriptions, AND on-image / on-screen text — independent of the
app UI locale. A user with pt-BR UI who writes for an English
audience gets English content, and vice versa.
- New content_language column on workspaces (added to the existing
create_workspaces_table migration since we're pre-production),
defaulting to 'en'. Fillable on the model.
- Dropdown in Settings → Workspace → Brand, side-by-side with Tone.
Options: English, Português (Brasil), Español. Short helper copy
explains what the setting controls.
- Validation: sometimes|string|in:en,pt-BR,es — optional on update
so existing callers that don't post the field keep the current
value (DB default handles fresh workspaces).
- System prompt now uses $content_language (sourced from the
workspace) instead of app()->getLocale(). The rule was also
strengthened: the AI must always write in this language regardless
of what language the user types instructions in, and must instruct
image/video tools to render on-media text in this language too.
- Image and video Blade prompts received a new line forcing any text
rendered inside the generated media to use $content_language.
- GenerateImage tool and VideoGenerationService pass the workspace's
content_language into their respective prompt templates.
Previously the caption would be in Portuguese (since it followed the
user's input language) but the image could render with English text
because there was no explicit constraint. Now the whole pipeline is
aligned to one per-workspace language.
2026-04-16 13:37:03 +00:00
'content_language' => 'Content language' ,
'content_language_description' => 'Language used for AI-generated captions, hashtags, and any text inside generated images or videos.' ,
2026-07-03 21:17:38 +00:00
'font_placeholder' => 'Select a font…' ,
'font_search' => 'Search font…' ,
'font_empty' => 'No fonts match.' ,
'language_placeholder' => 'Select a language…' ,
'language_search' => 'Search language…' ,
'language_empty' => 'No language found.' ,
2026-04-16 00:28:40 +00:00
],
2026-01-21 23:50:57 +00:00
'members' => [
'title' => 'Members' ,
'heading' => 'Team members' ,
'description' => 'Manage members and invites for this workspace' ,
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 03:20:43 +00:00
'cancel' => 'Cancel' ,
'remove' => 'Remove' ,
2026-06-22 17:24:19 +00:00
'make_role' => 'Make :role' ,
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 03:20:43 +00:00
2026-01-21 23:50:57 +00:00
'invite' => [
'title' => 'Invite Member' ,
'description' => 'Send an email invite to add collaborators' ,
'email' => 'Email' ,
'email_placeholder' => 'collaborator@email.com' ,
'role' => 'Role' ,
'role_placeholder' => 'Select a role' ,
'submit' => 'Send Invite' ,
],
'pending' => [
'title' => 'Pending Invites' ,
'description' => 'Invites awaiting acceptance' ,
'empty' => 'No pending invites' ,
],
'list' => [
'title' => 'Members' ,
'description' => 'People with access to this workspace' ,
'empty' => 'No members besides the owner' ,
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 03:20:43 +00:00
],
'remove_modal' => [
'title' => 'Remove member' ,
'description' => 'Are you sure you want to remove this member from the workspace? They will lose access to all workspace resources.' ,
'action' => 'Remove member' ,
],
'cancel_invite_modal' => [
'title' => 'Cancel invitation' ,
'description' => 'Are you sure you want to cancel this invitation?' ,
'action' => 'Cancel invitation' ,
2026-01-21 23:50:57 +00:00
],
'roles' => [
'owner' => 'Owner' ,
'admin' => 'Admin' ,
'member' => 'Member' ,
feat: @mentions in comments, AI Action layer + MCP tools, settings tabs
Mentions in post comments
- @mention autocomplete (workspace members, current user excluded) with
marker syntax @[uuid] persisted, display names rendered via CommentBody
chips; live edit replaces markers with names and converts back on save.
- NotifyMentions action with workspace-scoped membership check, dedupes
same user, only newly-added mentions on update.
- Email + in-app via SendNotification job, respecting per-user
notification_preferences.mentioned_in_comment.
- Heartbeat-based presence (Cache, 60s TTL, 30s ping) so online recipients
get only the in-app notification — no email noise.
- Real-time bell on workspace.{id}.user.{id} private channel
(NotificationCreated event), scoped channel name avoids client-side
filtering and lays out a convention for future workspace channels.
- Mailable localized via lang/{en,es,pt-BR}/mail.php; Maizzle source
template for the email is committed and built into resources/views/mail.
AI generation refactor (Action layer + MCP)
- Extracted Actions/Ai/Generate{Image,Video} with QuotaExhaustedException
so agent tools and MCP tools share a single domain entry point.
- Mcp/Tools/Ai/Generate{Image,Video}Tool registered in TryPostServer; both
return MediaResource payloads.
- Orientation::imageApiSize maps non-OpenAI ratios to 1:1/2:3/3:2.
- config/ai.php is now the single source of truth driven by env, removing
the trypost.ai shim. Default text/image providers flipped to OpenAI.
Settings/UX
- /settings/workspace split into shadcn Tabs (Workspace / Brand / Users)
with three components.
- /assets and the in-editor MediaPicker open the ImagePreviewDialog
lightbox on image click while preserving action button behaviour.
- Comments tab landed via ?tab=comments&comment=<id> from notification
click (scroll-to + temporary highlight).
- Mention autocomplete popover flips above when near the viewport bottom.
- Real social platform PNGs replace Tabler brand glyphs in schedule
pills and post list, with hover tooltip carrying display_name + handle.
Bug fixes
- AcceptInvite: controller now passes workspace + role payload that the
Vue page expects; login/register CTAs preselect the invite email.
- WorkspaceInvite mailable: stopped referencing nonexistent
$invite->workspace and $invite->role; column added to the migration,
Invite model casts role to WorkspaceRole, CreateInvite persists it.
- PostCommentCreated: added broadcastAs so .PostCommentCreated actually
matches the Echo listener; payload now includes mentioned_users so
receivers render the chip correctly without a refetch.
- Preview components for X/Pinterest/Threads/Bluesky/LinkedIn/Mastodon/
TikTok/YouTube switched from item.type === 'image' to
!isVideoMedia(item) so media without a persisted type still renders.
- UpdatePostRequest now accepts media.*.{type,mime_type,size,...} so the
posts.media JSON keeps the metadata that the previews need.
- Removed throttle:6,1 from social connect routes (was 429ing legitimate
OAuth retries).
- Used MediaType enum cases instead of literal 'image'/'video' strings
when creating media rows.
Tests
- MentionParser unit tests, NotifyMentions feature tests including
online/offline channel selection and preference gating, MCP AI tool
happy paths, MentionedInComment mailable rendering, AcceptInvite +
search-members + index mentioned_users path. 1229 passing.
2026-05-01 23:59:03 +00:00
'viewer' => 'Viewer' ,
2026-01-21 23:50:57 +00:00
],
2026-01-22 01:08:18 +00:00
'flash' => [
'invite_sent' => 'Invite sent successfully!' ,
'invite_deleted' => 'Invite deleted.' ,
'member_removed' => 'Member removed successfully.' ,
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 14:53:42 +00:00
'role_updated' => 'Member role updated.' ,
2026-01-22 01:08:18 +00:00
'wrong_email' => 'This invite is for a different email address.' ,
'already_member' => 'You are already a member of this workspace.' ,
'invite_accepted' => 'Welcome! You are now a member of the workspace.' ,
'invite_declined' => 'Invite declined.' ,
],
],
feat: redesign billing, onboarding, sidebar, and settings architecture
- Sidebar reorganized: Workspace group (connections, hashtags, labels,
API keys, settings) and Account group (settings, usage, billing)
- Account group only visible to owner and hidden in self-hosted mode
- Onboarding simplified: role -> account (connect socials) -> completed
-> redirect to /subscribe. Removed Subscription setup step.
- Subscribe page redesigned with 4 plan cards, monthly/yearly toggle,
trial info, and per-plan features list
- Billing page redesigned following Sendkit layout (sections with
sidebar labels)
- Processing page uses usePoll with immediate watch for subscription
activation
- Cancel URL redirects directly to /subscribe
- Account settings page with name and billing_email (syncs with Stripe)
- Usage page with ring meters for all plan limits
- Settings layout tabs only for user pages (profile, password,
notifications). Workspace/API keys/billing are standalone pages.
- GoogleAuthButton extracted as reusable component
- WorkspaceRole TypeScript enum for type-safe role checks in frontend
- Trial period changed to 7 days
- Fixed onboarding loop when user confirms email
- All 1101 tests passing
2026-04-15 03:33:38 +00:00
'account' => [
2026-05-03 18:54:26 +00:00
'tabs' => [
'account' => 'Account' ,
'usage' => 'Usage' ,
'billing' => 'Billing' ,
],
feat: redesign billing, onboarding, sidebar, and settings architecture
- Sidebar reorganized: Workspace group (connections, hashtags, labels,
API keys, settings) and Account group (settings, usage, billing)
- Account group only visible to owner and hidden in self-hosted mode
- Onboarding simplified: role -> account (connect socials) -> completed
-> redirect to /subscribe. Removed Subscription setup step.
- Subscribe page redesigned with 4 plan cards, monthly/yearly toggle,
trial info, and per-plan features list
- Billing page redesigned following Sendkit layout (sections with
sidebar labels)
- Processing page uses usePoll with immediate watch for subscription
activation
- Cancel URL redirects directly to /subscribe
- Account settings page with name and billing_email (syncs with Stripe)
- Usage page with ring meters for all plan limits
- Settings layout tabs only for user pages (profile, password,
notifications). Workspace/API keys/billing are standalone pages.
- GoogleAuthButton extracted as reusable component
- WorkspaceRole TypeScript enum for type-safe role checks in frontend
- Trial period changed to 7 days
- Fixed onboarding loop when user confirms email
- All 1101 tests passing
2026-04-15 03:33:38 +00:00
'title' => 'Account Settings' ,
'description' => 'Manage your account name and billing email' ,
'name' => 'Account Name' ,
'name_placeholder' => 'My Company' ,
'billing_email' => 'Billing Email' ,
'billing_email_placeholder' => 'billing@company.com' ,
'billing_email_hint' => 'This email will be used for invoices and billing communications from Stripe.' ,
'submit' => 'Save' ,
],
2026-01-22 01:08:18 +00:00
'flash' => [
feat: redesign billing, onboarding, sidebar, and settings architecture
- Sidebar reorganized: Workspace group (connections, hashtags, labels,
API keys, settings) and Account group (settings, usage, billing)
- Account group only visible to owner and hidden in self-hosted mode
- Onboarding simplified: role -> account (connect socials) -> completed
-> redirect to /subscribe. Removed Subscription setup step.
- Subscribe page redesigned with 4 plan cards, monthly/yearly toggle,
trial info, and per-plan features list
- Billing page redesigned following Sendkit layout (sections with
sidebar labels)
- Processing page uses usePoll with immediate watch for subscription
activation
- Cancel URL redirects directly to /subscribe
- Account settings page with name and billing_email (syncs with Stripe)
- Usage page with ring meters for all plan limits
- Settings layout tabs only for user pages (profile, password,
notifications). Workspace/API keys/billing are standalone pages.
- GoogleAuthButton extracted as reusable component
- WorkspaceRole TypeScript enum for type-safe role checks in frontend
- Trial period changed to 7 days
- Fixed onboarding loop when user confirms email
- All 1101 tests passing
2026-04-15 03:33:38 +00:00
'account_updated' => 'Account updated successfully!' ,
2026-01-22 01:08:18 +00:00
'profile_updated' => 'Profile updated successfully!' ,
'password_updated' => 'Password updated successfully!' ,
'workspace_updated' => 'Settings updated successfully!' ,
2026-03-30 02:01:08 +00:00
'photo_updated' => 'Photo updated successfully!' ,
'photo_deleted' => 'Photo removed successfully!' ,
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 03:20:43 +00:00
'logo_updated' => 'Logo uploaded successfully!' ,
'logo_deleted' => 'Logo removed successfully!' ,
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 21:18:17 +00:00
'notifications_updated' => 'Notification preferences updated!' ,
2026-01-21 23:50:57 +00:00
],
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 17:06:10 +00:00
'api_keys' => [
'title' => 'API Keys' ,
'page_title' => 'API Keys' ,
'heading' => 'API Keys' ,
'description' => 'Manage API keys for programmatic access to your workspace.' ,
'create' => 'Create API Key' ,
'copy' => 'Copy' ,
'new_token_message' => 'Your new API key has been created. Copy it now — you won\'t be able to see it again.' ,
'table' => [
'name' => 'Name' ,
'key' => 'Key' ,
'status' => 'Status' ,
'expires' => 'Expires' ,
'last_used' => 'Last Used' ,
'never' => 'Never' ,
],
'actions' => [
'copy_id' => 'Copy API Key ID' ,
'copy_id_success' => 'API Key ID copied to clipboard' ,
'delete' => 'Delete' ,
],
'empty' => [
'title' => 'No API keys yet' ,
'description' => 'Create an API key to access your workspace programmatically.' ,
],
'delete_modal' => [
'title' => 'Delete API key' ,
'description' => 'Are you sure you want to delete this API key? Any applications using this key will lose access immediately.' ,
'action' => 'Delete API key' ,
],
'create_dialog' => [
'title' => 'Create API Key' ,
'description' => 'Create a new API key for programmatic access to your workspace.' ,
'name' => 'Name' ,
'name_placeholder' => 'e.g. Production API Key' ,
'expires' => 'Expiration date (optional)' ,
'expires_placeholder' => 'No expiration' ,
'submit' => 'Create' ,
'cancel' => 'Cancel' ,
],
'flash' => [
'created' => 'API key created successfully!' ,
'deleted' => 'API key deleted successfully!' ,
],
],
2026-01-21 23:50:57 +00:00
];