Commit Graph

235 Commits

Author SHA1 Message Date
Darko Gjorgjijoski
3d79fe1abc feat(modules): redesign admin marketplace cards and detail view
ModuleCard moves badges to the top-right, shows a cover placeholder when art is missing, and drops the rating/pricing chrome that was never populated by the marketplace. ModuleDetailView splits into a hero row (cover + module info, two-thirds width) plus a sticky action card on the right (one-third) so install/update/purchase buttons stay visible when scrolling long descriptions.

ModuleIndexView promotes the marketplace API token form to a persistent card at the top of the page and adds an authenticated/premium status pill so super-admins can see whether the current token unlocks premium listings. The tabs and empty state were reorganized so 'installed' and 'marketplace' feel like peers.

The admin modules store tracks marketplace auth status, adds checkApiToken() and setApiToken() methods, and unifies the install-request shape into ModuleInstallPayload so both the free and paid install buttons route through the same code path.
2026-04-10 19:00:00 +02:00
Darko Gjorgjijoski
23d1476870 refactor(modules): marketplace install flow with checksum validation
Rewires module installation to use slug + version + checksum_sha256 instead of the opaque module identifier. ModuleInstaller splits marketplace token handling out of install() into helpers, adopts structured error responses, and validates the downloaded archive's SHA-256 against the marketplace manifest before unpacking.

ModuleResource is simplified to accept an already-loaded installed-module instance rather than fetching it from state, exposes access_tier and checksum fields, and drops the auto-disable-on-unpurchased side effect that was bleeding write logic into a read resource. UnzipUpdateRequest accepts a nullable module with a conditional module_name field so the same endpoint serves both app and module updates.

ModulesPolicy::manageModules now short-circuits for super-admins so administration flows (token validation, store state) are not blocked on a company-scoped ability. Two new feature tests cover both the authorization bypass and ModuleResource serialization.
2026-04-10 17:30:00 +02:00
Darko Gjorgjijoski
42ce99eeba Show common currencies first in dropdowns and default to USD in install wizard
Currency dropdowns now display the most-traded currencies (USD, EUR, GBP,
JPY, CAD, AUD, CHF, CNY, INR, BRL) at the top, followed by the rest
alphabetically. The install wizard defaults to USD instead of EUR and
formats currency names as "USD - US Dollar" for consistency with the
rest of the app.
2026-04-10 15:55:46 +02:00
Darko Gjorgjijoski
9174254165 Refactor install wizard and mail configuration 2026-04-09 10:06:27 +02:00
Darko Gjorgjijoski
7743c2e126 feat(modules): dynamic sidebar group rendering active modules
The sidebar gains a new section that lists each currently-activated module
as a direct shortcut to its settings page. This is the always-visible
companion to the company-context Active Modules index — both surface the
same set of modules, but the index is the catalog landing page and the
sidebar group is the per-module quick access.

- BootstrapController returns module_menu populated from
  \InvoiceShelf\Modules\Registry::allMenu(), but only on the company-context
  branch — not on the super-admin branch (lines 53-69), since super admins
  don't see the dynamic group. Because nwidart only boots service providers
  for currently-activated modules, the registry naturally contains only
  active modules at request time, no extra filtering needed.

- bootstrap.service.ts BootstrapResponse type extended with
  module_menu?: ModuleMenuItem[]; new ModuleMenuItem interface
  (title/link/icon) — shaped distinctly from MenuItem because module entries
  use namespaced i18n keys and don't carry group/ability metadata.

- global.store.ts exposes a moduleMenu ref + a hasActiveModules computed.

- SiteSidebar.vue appends a new "Modules" section after the existing
  menuGroups output, in both the mobile (Dialog) and desktop branches. The
  section is hidden when hasActiveModules is false. Uses the
  modules.sidebar.section_title i18n key added in the previous commit.
2026-04-09 00:29:56 +02:00
Darko Gjorgjijoski
e6eeacb6d4 feat(modules): company-context module surfaces and schema-driven settings
Adds the read-only company "Active Modules" index page (lists every
instance-activated module with a Settings shortcut) and the schema-driven
settings framework (generic BaseSchemaForm.vue renderer + per-company
persistence in CompanySetting). Bundled because they share the same
routes/api.php edit and the index page's Settings button targets the
settings page.

Backend:

- CompanyModulesController::index() returns every Module::enabled = true row
  with a kebab-case slug (via Str::kebab()) and a has_settings flag computed
  from \InvoiceShelf\Modules\Registry::settingsFor(). nwidart stores module
  names in PascalCase ("HelloWorld") but URLs and registry keys use kebab
  ("hello-world") — the controller normalizes so module authors can call
  Registry::registerSettings('hello-world') naturally without thinking
  about the storage format.

- ModuleSettingsController::show(\$slug) returns the registered Schema +
  per-company values from CompanySetting (defaults flow through when nothing
  has been saved yet). update(\$slug) builds Laravel validator rules from
  the Schema's per-field rules arrays — with type-rule fallbacks for
  switch -> boolean, number -> numeric, multiselect -> array — silently
  drops unknown keys, and persists via CompanySetting::setSettings() under
  the module.{slug}.{key} prefix. Activation is instance-global, but
  settings are per-company: two companies on the same instance can
  configure the same activated module differently.

- routes/api.php mounts GET /api/v1/company-modules at the root of the
  company API group and GET/PUT /api/v1/modules/{slug}/settings inside the
  existing modules prefix.

Frontend:

- BaseSchemaForm.vue is the central new component — a generic schema-driven
  form renderer that maps schema fields to BaseInput / BaseTextarea /
  BaseSwitch / BaseMultiselect by type, and builds Vuelidate rules
  dynamically from each field's rules array (supports required, email, url,
  numeric, min:N, max:N). New fields are added by extending the type ->
  component map.

- CompanyModulesIndexView.vue fetches /company-modules and renders a card
  grid (with empty/loading states); CompanyModuleCard.vue is the per-row
  component with the Settings button. ModuleSettingsView.vue fetches
  /modules/{slug}/settings, hands {schema, values} to BaseSchemaForm, and
  posts back on submit.

- Company-context routes.ts is rebuilt after the previous commit relocated
  the marketplace browser away. It now declares modules.index +
  modules.settings, both gated by manage-module ability.

- New api/services/{companyModules,moduleSettings}.service.ts thin clients.

- lang/en.json adds modules.index.{description,empty_title,empty_description},
  modules.settings.{title,open,saved,not_found,none}, and
  modules.sidebar.section_title. The sidebar key is added here even though
  the dynamic sidebar rendering lands in the next commit — keeping all i18n
  additions in one file edit avoids hunk-splitting lang/en.json.
2026-04-09 00:29:36 +02:00
Darko Gjorgjijoski
84725b2dfa feat(modules): relocate marketplace browser to super-admin context
The module marketplace browser UI (ModuleIndexView, ModuleDetailView,
ModuleCard, the four-step installer store) was filed under
features/company/modules/ only by historical accident — it's authorized via
the manage modules ability (super-admin-only) and conceptually belongs in the
admin context, not the company context.

- Move features/company/modules/{store.ts, views/ModuleIndexView.vue,
  views/ModuleDetailView.vue, components/ModuleCard.vue} to
  features/admin/modules/.
- Update hardcoded /admin/modules/... paths in the moved files to
  /admin/administration/modules/... so the breadcrumbs and ModuleCard
  navigation target the new admin-context routes.
- Tighten the four-step installer's silent catch {} blocks in the moved
  store.ts: errors were being swallowed, now they dispatch through the
  global notification store instead.
- New features/admin/modules/routes.ts declares admin.modules.index +
  admin.modules.view as children of /admin/administration with
  meta.isSuperAdmin: true.
- features/admin/{index,routes}.ts re-export and mount the relocated routes.
- config/invoiceshelf.php gains a new AdminModules entry in admin_menu
  pointing at /admin/administration/modules with super_admin_only: true.
- The dev-gated navigation.modules entry in main_menu is replaced (not
  deleted) with a non-gated entry pointing at the new company-context
  Active Modules index page that lands in the next commit. The
  ability is set to manage modules so non-owners can't see it.

The new company-context Active Modules index, schema-driven settings page,
and dynamic sidebar group are introduced in subsequent commits.
2026-04-09 00:28:59 +02:00
Darko Gjorgjijoski
999ff3e977 Auto-update invoice due date when invoice date changes
Port of master's 241ec092 (Feat: Automatically set due date when invoice date is changed). The original commit was a JavaScript Options-API watcher in the deleted v1 InvoiceCreate.vue; v3.0's equivalent is a Composition-API TypeScript view at resources/scripts/features/company/invoices/views/InvoiceCreateView.vue, so this is a re-implementation rather than a cherry-pick.

Behaviour: if the company setting invoice_set_due_date_automatically is 'YES', a watcher on invoiceStore.newInvoice.invoice_date recomputes the due date as invoice_date + invoice_due_date_days whenever the invoice date changes. A second watcher on due_date tracks whether the user has manually edited it; if the manual value is still valid (>= the new invoice date) it is left alone, otherwise the auto value takes over. An isAutoUpdatingDueDate guard avoids a feedback loop when the watcher writes back to the store.

Uses moment for the date math, matching the original master commit and several other v3.0 features (reports, CreateCustomFields) that already import moment. companyStore is newly imported in this view to read selectedCompanySettings.
2026-04-07 17:33:03 +02:00
Darko Gjorgjijoski
6fdf10b2b1 Rebuild auth pages on the project design system
Rewrites resources/scripts/layouts/AuthLayout.vue from scratch using only the @theme tokens defined in themes.css and registered via @theme inline in invoiceshelf.css. The new layout is a centered card on the existing bg-glass-gradient utility, using the same visual vocabulary as BaseCard (bg-surface, rounded-xl, border-line-default, shadow-sm) so the auth pages read as a smaller, simpler version of the admin's existing card pattern. Both light and dark mode work automatically because every color references a theme token rather than a hardcoded hex/rgb.

Drops the previous attempt's hardcoded #0a0e1a / #fbbf24 / #f5efe5 palette, the imported Google Fonts (Fraunces / Manrope / JetBrains Mono — replaced with the project default Poppins via font-base), the local --ink / --brass / --cream CSS variables that ignored [data-theme=dark], and the :deep() overrides that forced BaseInput / BaseButton into a custom underline style. The form components now render in the auth card identically to how they render anywhere else in the admin — same components, same theme tokens, no overrides.

Removes four legacy SVG decorations from the original two-panel design: LoginPlanetCrater, LoginBackground, LoginBackgroundOverlay, LoginBottomVector. The page now has no decorative imagery — the bg-glass-gradient utility carries the visual mood.

Adds w-full justify-center to the four auth-form submit buttons (LoginView, ForgotPasswordView, ResetPasswordView, RegisterWithInvitationView) so they fill the auth card width with their labels centered. Done at the call site rather than via :deep() so BaseButton stays untouched and the rest of the admin keeps its inline button style. Route-aware heading/subheading copy is preserved for all four auth views, and the four window.* admin customization hooks (login_page_logo, login_page_heading, login_page_description, copyright_text) still work.
2026-04-07 14:18:34 +02:00
Darko Gjorgjijoski
71388ec6a5 Rename resources/scripts-v2 to resources/scripts and drop @v2 alias
Now that the legacy v1 frontend (commit 064bdf53) is gone, the v2 directory is the only frontend and the v2 suffix is just noise. Renames resources/scripts-v2 to resources/scripts via git mv (so git records the move as renames, preserving blame and log --follow), then bulk-rewrites the 152 files that imported via @v2/... to use @/scripts/... instead. The existing @ alias (resources/) covers the new path with no extra config needed.

Drops the now-unused @v2 alias from vite.config.js and points the laravel-vite-plugin entry at resources/scripts/main.ts. Updates the only blade reference (resources/views/app.blade.php) to match. The package.json test script (eslint ./resources/scripts) automatically targets the right place after the rename without any edit.

Verified: npm run build exits clean and the Vite warning lines now reference resources/scripts/plugins/i18n.ts, confirming every import resolved through the new path. git log --follow on any moved file walks back through its scripts-v2 history.
2026-04-07 12:50:16 +02:00
Darko Gjorgjijoski
064bdf5395 Delete legacy v1 frontend (resources/scripts)
The resources/scripts/ directory was the original Vue 2 / Pinia v1 admin and customer-portal SPA. It has been fully orphaned for some time — vite.config.js has zero entry points pointing at it and the only blade @vite() reference in resources/views/app.blade.php loads scripts-v2/main.ts. The directory was pure dead code.

Removes 424 .vue / .js / store / router / helper files (~2.7 MB) so that resources/scripts-v2/ can be renamed back to resources/scripts/ in a follow-up commit, dropping the v2 suffix now that there is no v1 left.
2026-04-07 12:48:15 +02:00
Darko Gjorgjijoski
53a0e1491d Refine sidebar collapse toggle to bottom toolbar
Move collapse button from header into a sticky bottom toolbar in
the sidebar. Chevron double-left/right icon aligned to sidebar edge.
Toolbar area uses mt-auto with border-t separator and glass backdrop,
ready for additional action buttons in the future.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 04:00:00 +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
9c8e4ae558 Add glass UI with backdrop blur and fix primary button colors
Apply glassmorphism to sidebar, cards, tables, modals, dropdowns,
and dialogs with semi-transparent backgrounds, backdrop-blur, and
white/15 borders. Add subtle gradient body background for the blur
to work against. Add dedicated btn-primary color tokens so primary
buttons stay bold in dark mode instead of using the brightened text
palette. Use shadow-sm to avoid heavy halos in light mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:30:00 +02:00
Darko Gjorgjijoski
7e15eb7c7a Make settings sidebar sticky and consistent across all settings pages
Add gap-8 to user settings (was missing), and sticky top-20
self-start to all three settings sidebars (company, admin, user
profile) so the menu stays fixed while the content area scrolls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:22:00 +02:00
Darko Gjorgjijoski
a10d4d2de9 Theme scrollbars for dark mode
Add global webkit and Firefox scrollbar styling using semantic
color tokens. Fix component scrollbar classes in GlobalSearchBar
and CompanySwitcher from hardcoded gray to theme-aware colors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:15:00 +02:00
Darko Gjorgjijoski
249b2759b6 Fix header gradient too light in dark mode
Add dedicated header-from/header-to color tokens that are independent
of the primary palette dark mode overrides. Dark mode header uses a
deeper indigo gradient instead of the brightened primary colors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:08:00 +02:00
Darko Gjorgjijoski
d7f88d3103 Fix + button icon color in header to white
The bulk sed migration changed the PlusIcon from text-gray-600 to
text-body, but it sits on the gradient header and should be white.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:00:00 +02:00
Darko Gjorgjijoski
960e6d3517 Polish header search bar with frosted glass styling
Replace solid bg-surface background with bg-white/20 translucent
style matching the + button and company switcher. Use white text
and placeholder with opacity for consistency on the gradient header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:55:00 +02:00
Darko Gjorgjijoski
d36fbbbf27 Add green status indicator for global mail configuration
Show a green check icon with tinted background when company is
using the global mail configuration, replacing the plain gray text.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:42:00 +02:00
Darko Gjorgjijoski
2a3774f437 Soften divider lines across settings pages
Change BaseDivider from text-subtle (which left the hr with a dark
default border) to border-line-light for a gentle themed separator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:35:00 +02:00
Darko Gjorgjijoski
dabdd2b417 Fix status badge colors: SENT to green, PAID more visible
Change SENT status from yellow to green in both invoice and estimate
badges. Make PAID badge more noticeable with stronger green background
(40% opacity) and semibold text. Use consistent text-status-green
token for PAID across all badge components.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:30:00 +02:00
Darko Gjorgjijoski
350068706c Unify form styling across invoice, estimate, and recurring invoice pages
Apply consistent rounded-xl shadow border-line-light to customer
selector, date/number fields card, items table, totals card, editor,
tax popup, and view page sidebars. Wrap right-side basic fields in
card container matching the customer card.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:18:00 +02:00
Darko Gjorgjijoski
88adfe0e50 Add dark mode with CSS custom property theme system
Define 13 semantic color tokens (surface, text, border, hover) with
light/dark values in themes.css. Register with Tailwind via @theme inline.
Migrate all 335 Vue files from hardcoded gray/white classes to semantic
tokens. Add theme toggle (sun/moon/system) in user avatar dropdown.
Replace @tailwindcss/forms with custom form reset using theme vars.
Add status badge and alert tokens for dark mode. Theme-aware chart
grid/labels, skeleton placeholders, and editor. Inline script in
<head> prevents flash of wrong theme on load.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:05:00 +02:00
Darko Gjorgjijoski
7fbe3d85a3 UI: Facelift for v3 2026-04-04 01:52:03 +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
29b3abd317 Add role column to members table and reorder admin settings menu
Display the Bouncer role title in the members list table. Move
Update App to the last position in administration settings menu.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 01:00:00 +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
c3a59a46db Add frontend handling for users without a company
Make setSelectedCompany null-safe and clear stale localStorage.
Conditionally initialize company store state in bootstrap. Add
router guard to redirect no-company users to NoCompanyView while
allowing super admins through. Hide sidebar when no company.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:36:00 +02:00
Darko Gjorgjijoski
c49c130e9e Remove Add Member button and Create page — members are added via invitation only 2026-04-04 00:00:53 +02:00
Darko Gjorgjijoski
03afb98452 Fix logout not clearing auth token and company from localStorage
After logout, the old auth.token and selectedCompany stayed in
localStorage. On next login, the http interceptor sent the stale
token in the Authorization header, causing all API calls to fail
with 401/419 even though the new session was valid.
2026-04-03 23:49:47 +02:00
Darko Gjorgjijoski
acce67f514 Fix CSRF token mismatch after logout by refreshing cookie
After logout invalidates the session, the SPA still holds the old CSRF
cookie. Subsequent login attempts succeed but bootstrap/API calls fail
with CSRF mismatch, causing redirect back to login. Fix: fetch a fresh
CSRF cookie via /sanctum/csrf-cookie after logout completes.
2026-04-03 23:46:07 +02:00
Darko Gjorgjijoski
8e966965f5 Add logo to invitation registration page 2026-04-03 23:41:27 +02:00
Darko Gjorgjijoski
c1994887ef Support invitations for unregistered users
When inviting an email without an InvoiceShelf account, the email now
links to a registration page (/register?invitation={token}) instead of
login. After registering, the invitation is auto-accepted.

Backend:
- InvitationRegistrationController: public details() and register()
  endpoints. Registration validates token + email match, creates account,
  auto-accepts invitation, returns Sanctum token.
- AuthController: login now accepts optional invitation_token param to
  auto-accept invitation for existing users clicking the email link.
- CompanyInvitationMail: conditional URL based on user existence.
- Web route for /invitations/{token}/decline (email decline link).

Frontend:
- RegisterWithInvitation.vue: fetches invitation details, shows company
  name + role, registration form with pre-filled email.
- Router: /register route added.

Tests: 3 new tests (invitation details, register + accept, email mismatch).
2026-04-03 23:26:58 +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
735eef6e9b Fix password update sending name and email to satisfy ProfileRequest validation 2026-04-03 17:42:14 +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
55416bc633 Improve copyright handling 2026-04-03 13:34:33 +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
751bd4a1c8 Upgrade ChartJS from v2 to v4 (#603) 2026-04-02 20:02:49 +02:00
Darko Gjorgjijoski
5014a75fbc Upgrade eslint tooling to v10 and fix linting bugs (#601)
- Upgrade eslint 9→10, eslint-config-prettier 9→10, eslint-plugin-vue 9→10
- Upgrade @types/node 20→24
- Migrate from legacy .eslintrc.mjs to flat config eslint.config.mjs
- Remove --ext flag from npm test script (dropped in eslint 10)
- Fix vue/no-ref-as-operand: add missing .value to ref assignments (5 files)
- Fix vue/return-in-computed-property: add default returns (2 files)
- Fix vue/no-side-effects-in-computed-properties: move mutation to watcher
- Fix vue/no-dupe-keys: remove ref shadowing prop in DomPDFDriver
- Fix vue/no-deprecated-slot-attribute: migrate to v-slot syntax (3 files)
- Fix vue/require-valid-default-prop: use factory function for array default
- Fix vue/no-unused-vars: remove unused slot destructure
- Disable vue/no-mutating-props (false positive for Pinia store props)
2026-04-02 17:33:18 +02:00
Darko Gjorgjijoski
f623cd0179 Upgrade vue-router from v4 to v5 (#598)
- Migrate beforeEach navigation guard from next() callback to return-based
  API, preparing for vue-router v6 where next() is removed
2026-04-02 16:47:16 +02:00
Darko Gjorgjijoski
d75a957183 Upgrade Tiptap from v2 to v3 (#597)
- Upgrade @tiptap/core, starter-kit, vue-3, pm, extension-text-align to v3
- Remove @tiptap/extension-link (now bundled in StarterKit v3)
- Move Link config into StarterKit.configure()
2026-04-02 16:35:43 +02:00
Darko Gjorgjijoski
3ceb08bc31 Upgrade Pinia from v2 to v3 (#596)
Migrate all 37 store definitions from the deprecated object-with-id
signature to the string-id-first signature required by Pinia 3:

  defineStore({ id: 'name', ... }) → defineStore('name', { ... })
2026-04-02 16:12:11 +02:00
Darko Gjorgjijoski
ad5a7e51b9 Upgrade to Vite 8 and Tailwind CSS 4 (#595)
- Vite 6 → 8 (Rolldown bundler), laravel-vite-plugin 1 → 3, @vitejs/plugin-vue 5 → 6
- Tailwind CSS 3 → 4 with CSS-based config (@theme, @plugin, @utility)
- Add @tailwindcss/vite plugin, remove postcss/autoprefixer/sass
- Convert SCSS files to plain CSS (resources/sass → resources/css)
- Migrate tailwind.config.js to CSS @theme directives
- Rename deprecated utility classes (shadow-sm→shadow-xs, outline-none→outline-hidden,
  rounded-sm→rounded-xs, bg-gradient-to→bg-linear-to, ring→ring-3)
- Migrate opacity utilities to color modifiers (bg-opacity, text-opacity,
  border-opacity, ring-opacity → color/N syntax)
- Update primary color CSS vars to full rgb() values for TW4 color-mix()
- Fix border-l color specificity for sidebar navigation (TW4 default border
  color changed from gray-200 to currentColor)
- Fix invalid border color classes (border-grey-light, border-modal-bg, border--200)
- Add @reference directive for @apply in Vue component style blocks
- Convert Vue component <style lang="scss"> blocks to plain CSS
2026-04-02 15:59:15 +02:00
Darko Gjorgjijoski
691178857f Add HTTP client wrapper and upgrade Axios to v1 (#594)
* refactor: add HTTP client wrapper and upgrade axios to v1

Introduce a thin HTTP wrapper (resources/scripts/http) that centralizes
axios configuration, interceptors, and auth header injection. All 43
files now import from the wrapper instead of axios directly, making
future library swaps a single-file change. Upgrade axios from 0.30.0
to 1.14.0.

* fix: restore window.Ls assignment removed during axios refactor

company.js uses window.Ls.set() to persist selected company,
which broke after the axios plugin (that set window.Ls) was deleted.
2026-04-02 15:08:23 +02:00
Darko Gjorgjijoski
a38f09cf7b Installer reliability improvements (#593)
* docs: add CLAUDE.md for Claude Code guidance

* fix: handle missing settings table in installation middlewares

RedirectIfInstalled crashed with "no such table: settings" when the
database_created marker file existed but the database was empty.
Changed to use isDbCreated() which verifies actual tables, and added
try-catch around Setting queries in both middlewares.

* feat: pre-select database driver from env in installation wizard

The database step now reads DB_CONNECTION from the environment and
pre-selects the matching driver on load, including correct defaults
for hostname and port.

* feat: pre-select mail driver and config from env in installation wizard

The email step now fetches the current mail configuration on load
instead of hardcoding the driver to 'mail'. SMTP fields fall back
to Laravel config values from the environment.

* refactor: remove file-based DB marker in favor of direct DB checks

The database_created marker file was a second source of truth that
could drift out of sync with the actual database. InstallUtils now
checks the database directly via Schema::hasTable which is cached
per-request and handles all error cases gracefully.
2026-04-02 14:48:08 +02:00
mchev
3bae2c282c Merge pull request #576 from csalzano/fix/remote-disk-backup-listing
Fix remote disk backups never appear in backup listing
2026-03-26 09:16:13 +01:00