Commit Graph

117 Commits

Author SHA1 Message Date
Darko Gjorgjijoski
78ed332d06 Add per-user language preference with company default fallback
Existing accounts inherited the company language at creation time and there was no way to change UI language per user. Add a 'Default (Company Language)' entry to the language selector in UserGeneralView, persist the choice through userStore.updateUserSettings and reload the i18n bundle via window.loadLanguage. The 'default' sentinel keeps the user opted in to the company-wide setting.

Bootstrap (global.store) now syncs userForm from current_user data and resolves the active UI language as user > company > 'en'. RegisterController, InvitationRegistrationController and MemberService seed new users with language=default instead of copying the current company setting, so promoting/inviting members no longer leaks the inviter's frozen language.
2026-04-07 04:41:00 +02:00
Darko Gjorgjijoski
ba5c6c39ba Add multilingual PDF font system with Noto Sans and on-demand CJK packages
Bundle Noto Sans (Regular/Bold/Italic/BoldItalic) under resources/static/fonts/ as the default PDF face — it covers Latin, Cyrillic, Greek, Arabic, Thai and Hindi out of the box, replacing the limited DejaVu Sans fallback. Move all @font-face declarations into app.pdf.partials.fonts and include it from every invoice/estimate/payment/report template, dropping per-template font-family hardcodes and the conditional Thai locale include.

Introduce FontService + FontController to download static Noto Sans CJK packages (zh, zh_CN, ja, ko) from life888888/cjk-fonts-ttf on demand. GeneratesPdfTrait::ensureFontsForLocale primes the family before rendering and the partial emits @font-face rules for installed packages so dompdf resolves them through standard CSS — no separate registerFont() instance required. Static TTFs are mandatory because dompdf's PHP-Font-Lib does not parse variable fonts (fvar/gvar tables), which is why Google Fonts' NotoSansTC[wght].ttf rendered empty boxes.

Expose status/install via /api/v1/fonts/status and /api/v1/fonts/{package}/install with matching FONTS_STATUS / FONTS_INSTALL constants in scripts-v2/api/endpoints.ts. Flip DOMPDF_ENABLE_REMOTE default to true for remote asset loading.
2026-04-06 23:32:00 +02:00
Darko Gjorgjijoski
20085cab5d Refactor FileDisk system with per-disk unique names and disk assignments UI
Major changes to the file disk subsystem:

- Each FileDisk now gets a unique Laravel disk name (disk_{id}) instead
  of temp_{driver}, fixing the bug where multiple local disks with
  different roots overwrote each other's config.

- Move disk registration logic from FileDisk model to FileDiskService
  (registerDisk, getDiskName). Model keeps only getDecodedCredentials
  and a deprecated setConfig() wrapper.

- Add Disk Assignments admin UI (File Disk tab) with three purpose
  dropdowns: Media Storage, PDF Storage, Backup Storage. Stored as
  settings (media_disk_id, pdf_disk_id, backup_disk_id).

- Backup tab now uses the assigned backup disk instead of a per-backup
  dropdown. BackupsController refactored to use BackupService which
  centralizes disk resolution. Removed stale 4-second cache.

- Add local_public disk to config/filesystems.php so system disks
  are properly defined.

- Local disk roots stored relative to storage/app/ with hint text
  in the admin modal explaining the convention.

- Fix BaseModal watchEffect -> watch to prevent infinite request
  loops on the File Disk page.

- Fix string/number comparison for disk purpose IDs from settings.

- Add safeguards: prevent deleting disks with files, warn on
  purpose change, prevent deleting system disks.
2026-04-07 02:04:57 +02:00
Darko Gjorgjijoski
9ca998e64a Add Convert to Estimate feature for invoices
New backend endpoint POST /invoices/{id}/convert-to-estimate that
creates a draft estimate from an invoice, copying items, taxes,
custom fields, and financial data. Frontend wired with dropdown
action, store method, and API service call.
2026-04-06 22:57:03 +02:00
Darko Gjorgjijoski
e64529468c Replace deleted_files with manifest-based updater cleanup, add release workflow
- Add manifest.json generation script (scripts/generate-manifest.php)
- Add Updater::cleanStaleFiles() that removes files not in manifest
- Add /api/v1/update/clean endpoint with backward compatibility
- Add configurable update_protected_paths in config/invoiceshelf.php
- Update frontend to use clean step instead of delete step
- Add GitHub Actions release workflow triggered on version tags
- Add .github/release.yml for auto-generated changelog categories
- Update Makefile to include manifest generation and scripts directory
2026-04-06 19:27:33 +02:00
Darko Gjorgjijoski
74b4b2df4e Finalize Typescript restructure 2026-04-06 17:59:15 +02:00
Darko Gjorgjijoski
827c5c8938 Add collapsible sidebar with icon-only mode and tooltips
Desktop sidebar can be toggled between full width (w-56/w-64) and
collapsed icon-only mode (w-16). Collapsed state persists in
localStorage. Icons enlarge to w-6 h-6 when collapsed, labels
hidden, v-tooltip shows item names on hover. Group labels replaced
with thin dividers when collapsed. Toggle button pinned at bottom
with ChevronLeft/Right icon. Content area padding animates smoothly
with transition-all duration-300.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:45:00 +02:00
Darko Gjorgjijoski
7a1e2cd2c3 Rename Notes to Record Notes in company settings menu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:48:00 +02:00
Darko Gjorgjijoski
eb0a588164 Refactor Administration entrypoint
We moved the administration item to the company switcher in the header
2026-04-04 01:36:28 +02:00
Darko Gjorgjijoski
c85051161b Improve NoCompanyView design and fix header for no-company state
Personalize welcome heading with user name, add descriptive subtitle,
improve invitation card styling, remove redundant logout button. Fix
hasCreateAbilities check in header to actually call the function.
Widen company switcher dropdown and improve invitation row layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:48:00 +02:00
Darko Gjorgjijoski
6343b4a17f Add invitation frontend: invite modal, pending invitations, no-company view
Members Index:
- "Invite Member" button opens InviteMemberModal (email + role dropdown)
- Pending invitations section shows below members table with cancel buttons
- Members store gains inviteMember, fetchPendingInvitations, cancelInvitation

CompanySwitcher:
- Shows pending invitations greyed out below active companies
- Each with Accept/Decline mini-buttons
- Accepting refreshes bootstrap and switches to new company

NoCompanyView:
- Standalone page for users with zero accepted companies
- Shows pending invitations with Accept/Decline or "no companies" message
- Route: /admin/no-company

Invitation Pinia store:
- Manages user's own pending invitations (fetchPending, accept, decline)
- Bootstrap populates invitations from API response

Global store:
- Bootstrap action stores pending_invitations from response
2026-04-03 23:20:41 +02:00
Darko Gjorgjijoski
8a6c085288 Rename company-scoped Users to Members throughout
Complete rename across backend and frontend:
- Controller: Company/Users/UsersController -> Company/Members/MembersController
- Service: UserService -> MemberService
- Requests: UserRequest -> MemberRequest, DeleteUserRequest -> DeleteMemberRequest
- API routes: /api/v1/users -> /api/v1/members (company-scoped only)
- Sidebar menu: "Users" -> "Members"
- Frontend: views/users -> views/members, stores/users -> stores/members
- Router: users.index -> members.index, /admin/users -> /admin/members
- i18n: new "members" section with invitation-related keys
- Tests: UserTest -> MemberTest

Admin/super-admin Users (system-wide user management) remains unchanged.
2026-04-03 23:12:30 +02:00
Darko Gjorgjijoski
dee17a1da8 Rename Roles to Company Roles in settings menu 2026-04-03 22:35:50 +02:00
Darko Gjorgjijoski
4bfec37a53 Switch User Settings from horizontal tabs to sidebar layout
Match the Company Settings design pattern: sidebar navigation on desktop
with dropdown on mobile, child routes rendered via RouterView. Each tab
(General, Profile Photo, Security) is now a BaseSettingCard with its own
route under /admin/user-settings/{general,profile-photo,security}.
2026-04-03 17:41:18 +02:00
Darko Gjorgjijoski
1ca915a0a3 Split CompanyController and introduce standalone User Settings page
Backend:
- Extract user profile methods (show, update, uploadAvatar) from
  CompanyController into new UserProfileController
- CompanyController now only handles company concerns (updateCompany,
  uploadCompanyLogo)
- Remove Account Settings from setting_menu config

Frontend:
- New /admin/user-settings page with 3 tabs: General, Profile Photo,
  Security (password change)
- User dropdown now links to /admin/user-settings instead of
  /admin/settings/account-settings
- Settings sidebar defaults to Company Information as first item
- Remove old monolithic AccountSetting.vue
2026-04-03 17:35:41 +02:00
Darko Gjorgjijoski
9432da467e Add super-admin Administration section and restructure global vs company settings
- Add Administration sidebar section (super-admin only) with Companies, Users, and Global Settings pages
- Add super-admin middleware, controllers, and API routes under /api/v1/super-admin/
- Allow super-admins to manage all companies and users across tenants
- Add user impersonation with short-lived tokens, audit logging, and UI banner
- Move system-level settings (Mail, PDF, Backup, Update, File Disk) from per-company to Administration > Global Settings
- Convert save_pdf_to_disk from CompanySetting to global Setting
- Add per-company mail configuration overrides (optional, falls back to global)
- Add CompanyMailConfigService to apply company mail config before sending emails
2026-04-03 10:35:40 +02:00
Darko Gjorgjijoski
11024ddc38 Update source file en.json 2026-03-24 09:30:45 +01:00
Darko Gjorgjijoski
3af0e83d26 New translations en.json (Serbian (Latin)) 2026-03-24 07:44:53 +01:00
Darko Gjorgjijoski
a866a26e9f New translations en.json (Swahili) 2026-03-24 07:44:52 +01:00
Darko Gjorgjijoski
154a4dc076 New translations en.json (Malay) 2026-03-24 07:44:51 +01:00
Darko Gjorgjijoski
7900e020f5 New translations en.json (Hindi) 2026-03-24 07:44:50 +01:00
Darko Gjorgjijoski
974efb36da New translations en.json (Latvian) 2026-03-24 07:44:49 +01:00
Darko Gjorgjijoski
45d84b8b3c New translations en.json (Estonian) 2026-03-24 07:44:48 +01:00
Darko Gjorgjijoski
fdfb46ed79 New translations en.json (Croatian) 2026-03-24 07:44:46 +01:00
Darko Gjorgjijoski
ddcef0fb2c New translations en.json (Thai) 2026-03-24 07:44:45 +01:00
Darko Gjorgjijoski
0254b16556 New translations en.json (Bengali) 2026-03-24 07:44:44 +01:00
Darko Gjorgjijoski
a9a52ca85f New translations en.json (Persian) 2026-03-24 07:44:43 +01:00
Darko Gjorgjijoski
708d880b52 New translations en.json (Indonesian) 2026-03-24 07:44:42 +01:00
Darko Gjorgjijoski
57406989d4 New translations en.json (Portuguese, Brazilian) 2026-03-24 07:44:41 +01:00
Darko Gjorgjijoski
5d7e46b026 New translations en.json (Vietnamese) 2026-03-24 07:44:39 +01:00
Darko Gjorgjijoski
8e034e59f8 New translations en.json (Urdu (Pakistan)) 2026-03-24 07:44:38 +01:00
Darko Gjorgjijoski
43951d4542 New translations en.json (Chinese Traditional) 2026-03-24 07:44:37 +01:00
Darko Gjorgjijoski
3cc0f41d8c New translations en.json (Chinese Simplified) 2026-03-24 07:44:36 +01:00
Darko Gjorgjijoski
5f34ae558c New translations en.json (Ukrainian) 2026-03-24 07:44:35 +01:00
Darko Gjorgjijoski
70d51d580d New translations en.json (Turkish) 2026-03-24 07:44:34 +01:00
Darko Gjorgjijoski
b7929672fb New translations en.json (Swedish) 2026-03-24 07:44:32 +01:00
Darko Gjorgjijoski
bd28849a9f New translations en.json (Albanian) 2026-03-24 07:44:31 +01:00
Darko Gjorgjijoski
4d21aadcd4 New translations en.json (Slovenian) 2026-03-24 07:44:30 +01:00
Darko Gjorgjijoski
373a824fdb New translations en.json (Slovak) 2026-03-24 07:44:29 +01:00
Darko Gjorgjijoski
a5d9a60d5c New translations en.json (Russian) 2026-03-24 07:44:28 +01:00
Darko Gjorgjijoski
2537b53506 New translations en.json (Portuguese) 2026-03-24 07:44:27 +01:00
Darko Gjorgjijoski
bbc2d2088a New translations en.json (Polish) 2026-03-24 07:44:25 +01:00
Darko Gjorgjijoski
5734a06ab3 New translations en.json (Norwegian) 2026-03-24 07:44:24 +01:00
Darko Gjorgjijoski
75a1038245 New translations en.json (Dutch) 2026-03-24 07:44:23 +01:00
Darko Gjorgjijoski
017691b7b1 New translations en.json (Macedonian) 2026-03-24 07:44:22 +01:00
Darko Gjorgjijoski
d4f8a40982 New translations en.json (Lithuanian) 2026-03-24 07:44:21 +01:00
Darko Gjorgjijoski
ebdee1e3f0 New translations en.json (Georgian) 2026-03-24 07:44:20 +01:00
Darko Gjorgjijoski
ce97a474f9 New translations en.json (Japanese) 2026-03-24 07:44:18 +01:00
Darko Gjorgjijoski
4ee3d0d129 New translations en.json (Italian) 2026-03-24 07:44:17 +01:00
Darko Gjorgjijoski
bd5abdec0e New translations en.json (Hungarian) 2026-03-24 07:44:16 +01:00