Commit Graph

5 Commits

Author SHA1 Message Date
Darko Gjorgjijoski
31a2a66127 refactor(modules): move Modules into Company Settings as Module Configuration
The per-company Modules management page moves off its own top-level sidebar slot (which sat in the Admin group alongside Members/Reports/Settings) and into a new Module Configuration entry inside Company Settings, alongside Tax Types, Payment Modes, Mail Configuration, etc. That's where every other 'configure how the company behaves' surface lives — the Modules page is a configuration surface, not a primary working area.

The label is deliberately 'Module Configuration' rather than 'Module Settings' because the latter collides with the existing per-module ModuleSettingsModal concept (the modal that opens when a user clicks an installed module's gear icon). Keeping the two names distinct means 'Module Configuration' unambiguously refers to the list of installed modules, and 'Module Settings' continues to mean the per-module schema form.

CompanyModulesIndexView is stripped of its standalone BasePage / BasePageHeader / BaseBreadcrumb wrappers — as a child of SettingsLayoutView it would have rendered a double header — and re-wrapped in BaseSettingCard, matching TaxTypesView and every other settings-child view. The module grid tightens from lg:grid-cols-2 xl:grid-cols-3 down to lg:grid-cols-2 since the settings sidebar eats 240px of horizontal real estate.

Routes consolidate: features/company/modules/routes.ts is deleted; the new settings.modules child route lives inside the settings routes file directly, alongside the rest. Top-level redirects are kept for the legacy /admin/modules and /admin/modules/:slug/settings URLs so existing bookmarks still resolve. ModuleRoutesConfigTest is re-pointed at settings/routes.ts and asserts the settings.modules route is owner-only.

Module-contributed sidebar entries (those registered via Registry::registerMenu()) are NOT moved. Modules that want top-level navigation visibility keep it; only the meta management page moves. This mirrors WordPress/Discourse conventions where plugin pages stay in the main navigation but the 'Plugins' admin screen itself lives under Settings.
2026-04-11 06:30:00 +02:00
Darko Gjorgjijoski
345bfde306 feat(modules): translated display names and inline settings modal
CompanyModulesController attaches a translated display_name to each module before returning the list. ModuleSettingsController gains a translateSchema() helper that resolves section titles and field labels against the host app's i18n store before sending the schema to the frontend, so module authors can keep their 'my_module::settings.field' keys and users still see localized strings.

Per-module settings now open in an inline ModuleSettingsModal rather than routing to a standalone page. The modal reuses BaseSchemaForm for rendering, so the whole interaction takes place in-context next to the module card the user clicked — no navigation, no loss of place.

CompanyModuleCard displays the translated display_name instead of the raw slug and emits open-settings with the module payload; the parent view hands that to the modal store.
2026-04-10 21:30:00 +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
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