From 3cf08ccb588f2dab2f62a8ed56ff4801cfd26687 Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski Date: Thu, 24 Sep 2026 12:21:30 +0200 Subject: [PATCH 1/5] fix: work on InvoiceShelf 2.3.0 and later InvoiceShelf 2.3.0 moved to Pinia 3, which dropped the defineStore({ id, ... }) form the store used, and stopped exposing window.axios, so the settings page rendered blank. The store now passes its id as the first argument and uses the host's HTTP client. Saving the customer portal settings with an empty page title failed with a server error, since company settings cannot be empty; a blank title is now left out. Releases are built against InvoiceShelf's 2.x branch with its toolchain instead of the default branch, which is now 3.x. The README says the module is for 2.x only and adds the cache step after installing. --- .github/workflows/release.yaml | 17 +++++++--- CHANGELOG.md | 14 ++++++++ README.md | 34 ++++++++++++++----- Resources/scripts/stores/white-label.js | 11 +++--- Resources/scripts/views/WhiteLabelSetting.vue | 6 +++- package.json | 2 +- 6 files changed, 64 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 159dec1..0e0225b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -13,15 +13,18 @@ jobs: runs-on: [ubuntu-latest] steps: - - name: Checkout InvoiceShelf + # The module is for InvoiceShelf 2.x and bundles host code from it, so it is + # built against the 2.x branch, not the default branch (3.x). + - name: Checkout InvoiceShelf 2.x uses: actions/checkout@v4 with: repository: InvoiceShelf/InvoiceShelf + ref: 2.x - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.2 + php-version: 8.4 extensions: bcmath, curl, dom, gd, imagick, json, libxml, mbstring, pcntl, pdo, pdo_mysql, zip coverage: none @@ -30,13 +33,17 @@ jobs: with: composer-options: --no-dev + - name: Set up pnpm + uses: pnpm/action-setup@v4 + - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 24 - - name: Install and build InvoiceShelf node dependencies - run: npm install && npm run build + # The module build resolves pinia, axios and host components from here. + - name: Install InvoiceShelf node dependencies + run: pnpm install --frozen-lockfile - name: Checkout WhiteLabel module uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b34843..267a072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [1.1.4](https://github.com/InvoiceShelf/module-whitelabel/compare/v1.1.3...v1.1.4) (2026-09-24) + + +### Bug Fixes + +* work on InvoiceShelf 2.3.0 and later: the store uses Pinia 3's `defineStore(id, options)` form and the host's HTTP client instead of the removed `window.axios` +* saving the customer portal settings with an empty page title no longer fails with a server error +* build releases against InvoiceShelf's `2.x` branch with its toolchain (PHP 8.4, Node 24, pnpm) + + +### Documentation + +* say the module is for InvoiceShelf 2.x only, add a compatibility table and the cache step after installing + ### [1.1.3](https://github.com/InvoiceShelf/module-whitelabel/compare/v1.1.2...v1.1.3) (2025-02-25) diff --git a/README.md b/README.md index daa40b1..aee377b 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,17 @@ Adds ability to customise your InvoiceShelf instance with logo and brand color. +> [!IMPORTANT] +> This module is for **InvoiceShelf 2.x only**. On 2.3.0 and later it needs version 1.1.4 or newer; +> 1.1.3 and older show a blank settings page there. It does not run on InvoiceShelf 3.x. + ![Preview of Invoice Shelf, White Label Settings page](./preview.png) ## Table of Contents - [Invoice Shelf - White Label Module](#invoice-shelf---white-label-module) - [Table of Contents](#table-of-contents) + - [Compatibility](#compatibility) - [Installation](#installation) - [Update](#update) - [Hard Stops](#hard-stops) @@ -17,25 +22,36 @@ Adds ability to customise your InvoiceShelf instance with logo and brand color. - [Troubleshooting](#troubleshooting) - [Copyright](#copyright) +## Compatibility + +| InvoiceShelf | White Label module | +| --- | --- | +| 2.3.0 and later | 1.1.4 or newer | +| 1.3.0 to 2.2.x | 1.1.0 to 1.1.3 | +| 3.x | not supported | + ## Installation 1. Download the zip with pre-built module from the [latest release](https://github.com/InvoiceShelf/module-whitelabel/releases/latest) in this repo. Alternatively you can build from source, for that follow the steps in the [development](#development) section. 2. Create `/Modules/` dir in your server InvoiceShelf project root. **NOTE:** make sure it's capitalised like in example, it's not a typo. 3. Upload the `WhiteLabel.zip` into the newly created `/Modules/` dir. 4. Unzip it. -5. In your server InvoiceShelf project `/` root dir, run `php artisan install:module WhiteLabel ` e.g. `php artisan install:module WhiteLabel 1.1.1`, please consult with `package.json` for correct release version number inside the release artifact. -6. (optional) If you have any issues with your installed module, try clearing your browser's `cache`, `cookies`, and/or `site data`. +5. In your server InvoiceShelf project `/` root dir, run `php artisan install:module WhiteLabel ` e.g. `php artisan install:module WhiteLabel 1.1.4`, please consult with `package.json` for correct release version number inside the release artifact. +6. Run `php artisan optimize:clear`, or restart the container if you run InvoiceShelf in Docker. The + Docker image caches its routes when it starts, so the module's pages return 404 until then. +7. (optional) If you have any issues with your installed module, try clearing your browser's `cache`, `cookies`, and/or `site data`. ## Update 1. Download the zip with pre-built module from the [latest release](https://github.com/InvoiceShelf/module-whitelabel/releases/latest) in this repo. Alternatively you can build from source, for that follow the steps in the [development](#development) section. -2. Upload the `WhiteLabel.zip` into the newly created `/Modules/` dir. -3. Remove old `WhiteLabel/` module folder. -4. Unzip it and visit the site. +2. Upload the `WhiteLabel.zip` into the `/Modules/` dir. +3. Remove the old `WhiteLabel/` module folder. +4. Unzip it, run `php artisan optimize:clear` (or restart the container in Docker) and visit the site. ### Hard Stops - InvoiceShelf >= 1.3.0 (upgrade this module to 1.1.0) +- InvoiceShelf >= 2.3.0 (upgrade this module to 1.1.4) ## Development @@ -44,15 +60,15 @@ This is a step by step guide on how to get started, with development. ### Prerequisites 1. nvm/fnm (optional) -2. Node JS: `20 LTS` -3. `yarn` +2. Node JS: `24` (what InvoiceShelf 2.x requires) +3. `pnpm` for InvoiceShelf and `yarn` for this module ### Steps -1. Clone the [InvoiceShelf repo](https://github.com/InvoiceShelf/InvoiceShelf) - needed for the admin components. In the future it's likely to be just a node package and you wont need this step. +1. Clone the [InvoiceShelf repo](https://github.com/InvoiceShelf/InvoiceShelf) and check out the `2.x` branch - needed for the admin components. In the future it's likely to be just a node package and you wont need this step. 2. Create `Modules/` dir, in the root dir. 3. Clone this repo into `WhiteLabel/` dir inside `/Modules/`. -4. Initialise `node_modules` by running `yarn` inside `/` of the InvoiceShelf repo. +4. Initialise `node_modules` by running `pnpm install` inside `/` of the InvoiceShelf repo. 5. Initialise `node_modules` by running `yarn` inside `/Modules/WhiteLabel/` of this repo. 6. Make the changes you wish to make. 7. Run `yarn build`, new `/Modules/WhiteLabel/dist/` dir will be created with built `css` and `js` bundles. diff --git a/Resources/scripts/stores/white-label.js b/Resources/scripts/stores/white-label.js index a463ceb..8deab23 100644 --- a/Resources/scripts/stores/white-label.js +++ b/Resources/scripts/stores/white-label.js @@ -1,14 +1,17 @@ -// import axios from 'axios' import { useNotificationStore } from '@/scripts/stores/notification' +// InvoiceShelf 2.3.0 stopped exposing window.axios; its HTTP client (auth +// token, company header, credentials) is bundled from the host instead. +import http from '@/scripts/http' const { defineStore } = window.pinia -export const useWhiteLabelStore = defineStore({ - id: 'white-label', +// The id is the first argument: Pinia 3, which InvoiceShelf ships from 2.3.0, +// dropped the defineStore({ id, ... }) form. +export const useWhiteLabelStore = defineStore('white-label', { actions: { updateLogo(data) { const notificationStore = useNotificationStore(true) return new Promise((resolve, reject) => { - window.axios + http .post('/api/m/white-label/upload-logos', data) .then((response) => { resolve(response) diff --git a/Resources/scripts/views/WhiteLabelSetting.vue b/Resources/scripts/views/WhiteLabelSetting.vue index e8ec5d3..d7ccf40 100644 --- a/Resources/scripts/views/WhiteLabelSetting.vue +++ b/Resources/scripts/views/WhiteLabelSetting.vue @@ -262,7 +262,11 @@ async function saveCustomerPortalSettings() { data: { settings: { ...customerPortalSettings, - customer_portal_page_title: customerPortalTitle.value + // Company settings cannot be empty (the host stores '' as null into a + // NOT NULL column), so a blank title is left out rather than sent. + ...(customerPortalTitle.value + ? { customer_portal_page_title: customerPortalTitle.value } + : {}), }, }, message: 'settings.preferences.updated_message', diff --git a/package.json b/package.json index 8371bca..7ed5823 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.1.3", + "version": "1.1.4", "scripts": { "dev": "vite", "build": "vite build", From 4f9ec908738fea2e15ed15e2adf5118c21cf8db8 Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski Date: Thu, 24 Sep 2026 12:23:57 +0200 Subject: [PATCH 2/5] fix: themes apply again on InvoiceShelf 2.3.0 and later InvoiceShelf 2.3.0 moved to Tailwind 4, which reads the primary colour variables as colours. The themes set them to bare r, g, b triplets, so a selected theme left the portal without its primary colour. They are now full rgb() colours, as the host's own themes are. --- CHANGELOG.md | 1 + Resources/sass/themes.scss | 360 +++++++++++++++++++------------------ 2 files changed, 182 insertions(+), 179 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 267a072..2d1a2cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. See [standa * work on InvoiceShelf 2.3.0 and later: the store uses Pinia 3's `defineStore(id, options)` form and the host's HTTP client instead of the removed `window.axios` * saving the customer portal settings with an empty page title no longer fails with a server error +* themes apply again on InvoiceShelf 2.3.0 and later, which reads the colour variables as full colours (Tailwind 4) rather than r, g, b triplets * build releases against InvoiceShelf's `2.x` branch with its toolchain (PHP 8.4, Node 24, pnpm) diff --git a/Resources/sass/themes.scss b/Resources/sass/themes.scss index 014d5cd..1847fb7 100644 --- a/Resources/sass/themes.scss +++ b/Resources/sass/themes.scss @@ -1,233 +1,235 @@ +// Full colours, not bare r, g, b triplets: InvoiceShelf 2.3.0 moved to Tailwind 4, +// which reads these variables as colours directly. .theme-invoiceShelf { - --color-primary-50: 247, 246, 253; - --color-primary-100: 238, 238, 251; - --color-primary-200: 213, 212, 245; - --color-primary-300: 188, 185, 239; - --color-primary-400: 138, 133, 228; - --color-primary-500: 88, 81, 216; - --color-primary-600: 79, 73, 194; - --color-primary-700: 53, 49, 130; - --color-primary-800: 40, 36, 97; - --color-primary-900: 26, 24, 65; + --color-primary-50: rgb(247, 246, 253); + --color-primary-100: rgb(238, 238, 251); + --color-primary-200: rgb(213, 212, 245); + --color-primary-300: rgb(188, 185, 239); + --color-primary-400: rgb(138, 133, 228); + --color-primary-500: rgb(88, 81, 216); + --color-primary-600: rgb(79, 73, 194); + --color-primary-700: rgb(53, 49, 130); + --color-primary-800: rgb(40, 36, 97); + --color-primary-900: rgb(26, 24, 65); } .theme-red { - --color-primary-50: 254, 242, 242; - --color-primary-100: 254, 226, 226; - --color-primary-200: 254, 202, 202; - --color-primary-300: 252, 165, 165; - --color-primary-400: 248, 113, 113; - --color-primary-500: 239, 68, 68; - --color-primary-600: 220, 38, 38; - --color-primary-700: 185, 28, 28; - --color-primary-800: 153, 27, 27; - --color-primary-900: 127, 29, 29; + --color-primary-50: rgb(254, 242, 242); + --color-primary-100: rgb(254, 226, 226); + --color-primary-200: rgb(254, 202, 202); + --color-primary-300: rgb(252, 165, 165); + --color-primary-400: rgb(248, 113, 113); + --color-primary-500: rgb(239, 68, 68); + --color-primary-600: rgb(220, 38, 38); + --color-primary-700: rgb(185, 28, 28); + --color-primary-800: rgb(153, 27, 27); + --color-primary-900: rgb(127, 29, 29); } .theme-orange { - --color-primary-50: 255, 247, 237; - --color-primary-100: 255, 237, 213; - --color-primary-200: 254, 215, 170; - --color-primary-300: 253, 186, 116; - --color-primary-400: 251, 146, 60; - --color-primary-500: 249, 115, 22; - --color-primary-600: 234, 88, 12; - --color-primary-700: 194, 65, 12; - --color-primary-800: 154, 52, 18; - --color-primary-900: 124, 45, 18; + --color-primary-50: rgb(255, 247, 237); + --color-primary-100: rgb(255, 237, 213); + --color-primary-200: rgb(254, 215, 170); + --color-primary-300: rgb(253, 186, 116); + --color-primary-400: rgb(251, 146, 60); + --color-primary-500: rgb(249, 115, 22); + --color-primary-600: rgb(234, 88, 12); + --color-primary-700: rgb(194, 65, 12); + --color-primary-800: rgb(154, 52, 18); + --color-primary-900: rgb(124, 45, 18); } .theme-amber { - --color-primary-50: 255, 251, 235; - --color-primary-100: 254, 243, 199; - --color-primary-200: 253, 230, 138; - --color-primary-300: 252, 211, 77; - --color-primary-400: 251, 191, 36; - --color-primary-500: 245, 158, 11; - --color-primary-600: 217, 119, 6; - --color-primary-700: 180, 83, 9; - --color-primary-800: 146, 64, 14; - --color-primary-900: 120, 53, 15; + --color-primary-50: rgb(255, 251, 235); + --color-primary-100: rgb(254, 243, 199); + --color-primary-200: rgb(253, 230, 138); + --color-primary-300: rgb(252, 211, 77); + --color-primary-400: rgb(251, 191, 36); + --color-primary-500: rgb(245, 158, 11); + --color-primary-600: rgb(217, 119, 6); + --color-primary-700: rgb(180, 83, 9); + --color-primary-800: rgb(146, 64, 14); + --color-primary-900: rgb(120, 53, 15); } .theme-yellow { - --color-primary-50: 254, 252, 232; - --color-primary-100: 254, 249, 195; - --color-primary-200: 254, 240, 138; - --color-primary-300: 253, 224, 71; - --color-primary-400: 250, 204, 21; - --color-primary-500: 234, 179, 8; - --color-primary-600: 202, 138, 4; - --color-primary-700: 161, 98, 7; - --color-primary-800: 133, 77, 14; - --color-primary-900: 113, 63, 18; + --color-primary-50: rgb(254, 252, 232); + --color-primary-100: rgb(254, 249, 195); + --color-primary-200: rgb(254, 240, 138); + --color-primary-300: rgb(253, 224, 71); + --color-primary-400: rgb(250, 204, 21); + --color-primary-500: rgb(234, 179, 8); + --color-primary-600: rgb(202, 138, 4); + --color-primary-700: rgb(161, 98, 7); + --color-primary-800: rgb(133, 77, 14); + --color-primary-900: rgb(113, 63, 18); } // .theme-lime { -// --color-primary-50: 247, 254, 231; -// --color-primary-100: 236, 252, 203; -// --color-primary-200: 217, 249, 157; -// --color-primary-300: 190, 242, 100; -// --color-primary-400: 163, 230, 53; -// --color-primary-500: 132, 204, 22; -// --color-primary-600: 101, 163, 13; -// --color-primary-700: 77, 124, 15; -// --color-primary-800: 63, 98, 18; -// --color-primary-900: 54, 83, 20; +// --color-primary-50: rgb(247, 254, 231); +// --color-primary-100: rgb(236, 252, 203); +// --color-primary-200: rgb(217, 249, 157); +// --color-primary-300: rgb(190, 242, 100); +// --color-primary-400: rgb(163, 230, 53); +// --color-primary-500: rgb(132, 204, 22); +// --color-primary-600: rgb(101, 163, 13); +// --color-primary-700: rgb(77, 124, 15); +// --color-primary-800: rgb(63, 98, 18); +// --color-primary-900: rgb(54, 83, 20); // } .theme-green { - --color-primary-50: 240, 253, 244; - --color-primary-100: 220, 252, 231; - --color-primary-200: 187, 247, 208; - --color-primary-300: 134, 239, 172; - --color-primary-400: 74, 222, 128; - --color-primary-500: 34, 197, 94; - --color-primary-600: 22, 163, 74; - --color-primary-700: 21, 128, 61; - --color-primary-800: 22, 101, 52; - --color-primary-900: 20, 83, 45; + --color-primary-50: rgb(240, 253, 244); + --color-primary-100: rgb(220, 252, 231); + --color-primary-200: rgb(187, 247, 208); + --color-primary-300: rgb(134, 239, 172); + --color-primary-400: rgb(74, 222, 128); + --color-primary-500: rgb(34, 197, 94); + --color-primary-600: rgb(22, 163, 74); + --color-primary-700: rgb(21, 128, 61); + --color-primary-800: rgb(22, 101, 52); + --color-primary-900: rgb(20, 83, 45); } .theme-emerald { - --color-primary-50: 236, 253, 245; - --color-primary-100: 209, 250, 229; - --color-primary-200: 167, 243, 208; - --color-primary-300: 110, 231, 183; - --color-primary-400: 52, 211, 153; - --color-primary-500: 16, 185, 129; - --color-primary-600: 5, 150, 105; - --color-primary-700: 4, 120, 87; - --color-primary-800: 6, 95, 70; - --color-primary-900: 6, 78, 59; + --color-primary-50: rgb(236, 253, 245); + --color-primary-100: rgb(209, 250, 229); + --color-primary-200: rgb(167, 243, 208); + --color-primary-300: rgb(110, 231, 183); + --color-primary-400: rgb(52, 211, 153); + --color-primary-500: rgb(16, 185, 129); + --color-primary-600: rgb(5, 150, 105); + --color-primary-700: rgb(4, 120, 87); + --color-primary-800: rgb(6, 95, 70); + --color-primary-900: rgb(6, 78, 59); } .theme-teal { - --color-primary-50: 240, 253, 250; - --color-primary-100: 204, 251, 241; - --color-primary-200: 153, 246, 228; - --color-primary-300: 94, 234, 212; - --color-primary-400: 45, 212, 191; - --color-primary-500: 20, 184, 166; - --color-primary-600: 13, 148, 136; - --color-primary-700: 15, 118, 110; - --color-primary-800: 17, 94, 89; - --color-primary-900: 19, 78, 74; + --color-primary-50: rgb(240, 253, 250); + --color-primary-100: rgb(204, 251, 241); + --color-primary-200: rgb(153, 246, 228); + --color-primary-300: rgb(94, 234, 212); + --color-primary-400: rgb(45, 212, 191); + --color-primary-500: rgb(20, 184, 166); + --color-primary-600: rgb(13, 148, 136); + --color-primary-700: rgb(15, 118, 110); + --color-primary-800: rgb(17, 94, 89); + --color-primary-900: rgb(19, 78, 74); } .theme-cyan { - --color-primary-50: 236, 254, 255; - --color-primary-100: 207, 250, 254; - --color-primary-200: 165, 243, 252; - --color-primary-300: 103, 232, 249; - --color-primary-400: 34, 211, 238; - --color-primary-500: 6, 182, 212; - --color-primary-600: 8, 145, 178; - --color-primary-700: 14, 116, 144; - --color-primary-800: 21, 94, 117; - --color-primary-900: 22, 78, 99; + --color-primary-50: rgb(236, 254, 255); + --color-primary-100: rgb(207, 250, 254); + --color-primary-200: rgb(165, 243, 252); + --color-primary-300: rgb(103, 232, 249); + --color-primary-400: rgb(34, 211, 238); + --color-primary-500: rgb(6, 182, 212); + --color-primary-600: rgb(8, 145, 178); + --color-primary-700: rgb(14, 116, 144); + --color-primary-800: rgb(21, 94, 117); + --color-primary-900: rgb(22, 78, 99); } .theme-sky { - --color-primary-50: 240, 249, 255; - --color-primary-100: 224, 242, 254; - --color-primary-200: 186, 230, 253; - --color-primary-300: 125, 211, 252; - --color-primary-400: 56, 189, 248; - --color-primary-500: 14, 165, 233; - --color-primary-600: 2, 132, 199; - --color-primary-700: 3, 105, 161; - --color-primary-800: 7, 89, 133; - --color-primary-900: 12, 74, 110; + --color-primary-50: rgb(240, 249, 255); + --color-primary-100: rgb(224, 242, 254); + --color-primary-200: rgb(186, 230, 253); + --color-primary-300: rgb(125, 211, 252); + --color-primary-400: rgb(56, 189, 248); + --color-primary-500: rgb(14, 165, 233); + --color-primary-600: rgb(2, 132, 199); + --color-primary-700: rgb(3, 105, 161); + --color-primary-800: rgb(7, 89, 133); + --color-primary-900: rgb(12, 74, 110); } .theme-blue { - --color-primary-50: 239, 246, 255; - --color-primary-100: 219, 234, 254; - --color-primary-200: 191, 219, 254; - --color-primary-300: 147, 197, 253; - --color-primary-400: 96, 165, 250; - --color-primary-500: 59, 130, 246; - --color-primary-600: 37, 99, 235; - --color-primary-700: 29, 78, 216; - --color-primary-800: 30, 64, 175; - --color-primary-900: 30, 58, 138; + --color-primary-50: rgb(239, 246, 255); + --color-primary-100: rgb(219, 234, 254); + --color-primary-200: rgb(191, 219, 254); + --color-primary-300: rgb(147, 197, 253); + --color-primary-400: rgb(96, 165, 250); + --color-primary-500: rgb(59, 130, 246); + --color-primary-600: rgb(37, 99, 235); + --color-primary-700: rgb(29, 78, 216); + --color-primary-800: rgb(30, 64, 175); + --color-primary-900: rgb(30, 58, 138); } // .theme-indigo { -// --color-primary-50: 238, 242, 255; -// --color-primary-100: 224, 231, 255; -// --color-primary-200: 199, 210, 254; -// --color-primary-300: 165, 180, 252; -// --color-primary-400: 129, 140, 248; -// --color-primary-500: 99, 102, 241; -// --color-primary-600: 79, 70, 229; -// --color-primary-700: 67, 56, 202; -// --color-primary-800: 55, 48, 163; -// --color-primary-900: 49, 46, 129; +// --color-primary-50: rgb(238, 242, 255); +// --color-primary-100: rgb(224, 231, 255); +// --color-primary-200: rgb(199, 210, 254); +// --color-primary-300: rgb(165, 180, 252); +// --color-primary-400: rgb(129, 140, 248); +// --color-primary-500: rgb(99, 102, 241); +// --color-primary-600: rgb(79, 70, 229); +// --color-primary-700: rgb(67, 56, 202); +// --color-primary-800: rgb(55, 48, 163); +// --color-primary-900: rgb(49, 46, 129); // } .theme-violet { - --color-primary-50: 245, 243, 255; - --color-primary-100: 237, 233, 254; - --color-primary-200: 221, 214, 254; - --color-primary-300: 196, 181, 253; - --color-primary-400: 167, 139, 250; - --color-primary-500: 139, 92, 246; - --color-primary-600: 124, 58, 237; - --color-primary-700: 109, 40, 217; - --color-primary-800: 91, 33, 182; - --color-primary-900: 76, 29, 149; + --color-primary-50: rgb(245, 243, 255); + --color-primary-100: rgb(237, 233, 254); + --color-primary-200: rgb(221, 214, 254); + --color-primary-300: rgb(196, 181, 253); + --color-primary-400: rgb(167, 139, 250); + --color-primary-500: rgb(139, 92, 246); + --color-primary-600: rgb(124, 58, 237); + --color-primary-700: rgb(109, 40, 217); + --color-primary-800: rgb(91, 33, 182); + --color-primary-900: rgb(76, 29, 149); } .theme-purple { - --color-primary-50: 250, 245, 255; - --color-primary-100: 243, 232, 255; - --color-primary-200: 233, 213, 255; - --color-primary-300: 216, 180, 254; - --color-primary-400: 192, 132, 252; - --color-primary-500: 168, 85, 247; - --color-primary-600: 147, 51, 234; - --color-primary-700: 126, 34, 206; - --color-primary-800: 107, 33, 168; + --color-primary-50: rgb(250, 245, 255); + --color-primary-100: rgb(243, 232, 255); + --color-primary-200: rgb(233, 213, 255); + --color-primary-300: rgb(216, 180, 254); + --color-primary-400: rgb(192, 132, 252); + --color-primary-500: rgb(168, 85, 247); + --color-primary-600: rgb(147, 51, 234); + --color-primary-700: rgb(126, 34, 206); + --color-primary-800: rgb(107, 33, 168); --color-primary-900: 88, 28 135; } .theme-fuchsia { - --color-primary-50: 253, 244, 255; - --color-primary-100: 250, 232, 255; - --color-primary-200: 245, 208, 254; - --color-primary-300: 240, 171, 252; - --color-primary-400: 232, 121, 249; - --color-primary-500: 217, 70, 239; - --color-primary-600: 192, 38, 211; - --color-primary-700: 162, 28, 175; - --color-primary-800: 134, 25, 143; - --color-primary-900: 112, 26, 117; + --color-primary-50: rgb(253, 244, 255); + --color-primary-100: rgb(250, 232, 255); + --color-primary-200: rgb(245, 208, 254); + --color-primary-300: rgb(240, 171, 252); + --color-primary-400: rgb(232, 121, 249); + --color-primary-500: rgb(217, 70, 239); + --color-primary-600: rgb(192, 38, 211); + --color-primary-700: rgb(162, 28, 175); + --color-primary-800: rgb(134, 25, 143); + --color-primary-900: rgb(112, 26, 117); } .theme-pink { - --color-primary-50: 253, 242, 248; - --color-primary-100: 252, 231, 243; - --color-primary-200: 251, 207, 232; - --color-primary-300: 249, 168, 212; - --color-primary-400: 244, 114, 182; - --color-primary-500: 236, 72, 153; - --color-primary-600: 219, 39, 119; - --color-primary-700: 190, 24, 93; - --color-primary-800: 157, 23, 77; - --color-primary-900: 131, 24, 67; + --color-primary-50: rgb(253, 242, 248); + --color-primary-100: rgb(252, 231, 243); + --color-primary-200: rgb(251, 207, 232); + --color-primary-300: rgb(249, 168, 212); + --color-primary-400: rgb(244, 114, 182); + --color-primary-500: rgb(236, 72, 153); + --color-primary-600: rgb(219, 39, 119); + --color-primary-700: rgb(190, 24, 93); + --color-primary-800: rgb(157, 23, 77); + --color-primary-900: rgb(131, 24, 67); } .theme-rose { - --color-primary-50: 255, 241, 242; - --color-primary-100: 255, 228, 230; - --color-primary-200: 254, 205, 211; - --color-primary-300: 253, 164, 175; - --color-primary-400: 251, 113, 133; - --color-primary-500: 244, 63, 94; - --color-primary-600: 225, 29, 72; - --color-primary-700: 190, 18, 60; - --color-primary-800: 159, 18, 57; - --color-primary-900: 136, 19, 55; + --color-primary-50: rgb(255, 241, 242); + --color-primary-100: rgb(255, 228, 230); + --color-primary-200: rgb(254, 205, 211); + --color-primary-300: rgb(253, 164, 175); + --color-primary-400: rgb(251, 113, 133); + --color-primary-500: rgb(244, 63, 94); + --color-primary-600: rgb(225, 29, 72); + --color-primary-700: rgb(190, 18, 60); + --color-primary-800: rgb(159, 18, 57); + --color-primary-900: rgb(136, 19, 55); } From c9a72680569e60da652c143dbf09df898234c1f7 Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski Date: Thu, 24 Sep 2026 12:42:43 +0200 Subject: [PATCH 3/5] fix: stop overriding the app's layout on InvoiceShelf 2.3.0 and later InvoiceShelf 2.3.0 moved to Tailwind 4, which keeps its rules in cascade layers, and CSS outside any layer beats every layered rule. The module's unlayered utilities (.hidden, .flex, ...) overrode the app's responsive ones, hiding the sidebar, the header controls and the settings menu. The built stylesheet now goes into the app's utilities layer, declared in the app's order, so the app's own rule wins wherever both define a class. --- CHANGELOG.md | 1 + vite.config.js | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d1a2cb..3389b45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes * work on InvoiceShelf 2.3.0 and later: the store uses Pinia 3's `defineStore(id, options)` form and the host's HTTP client instead of the removed `window.axios` +* the module's stylesheet no longer breaks the app's layout on InvoiceShelf 2.3.0 and later (missing sidebar and header controls): it now sits in the app's Tailwind 4 `utilities` cascade layer instead of outside any layer * saving the customer portal settings with an empty page title no longer fails with a server error * themes apply again on InvoiceShelf 2.3.0 and later, which reads the colour variables as full colours (Tailwind 4) rather than r, g, b triplets * build releases against InvoiceShelf's `2.x` branch with its toolchain (PHP 8.4, Node 24, pnpm) diff --git a/vite.config.js b/vite.config.js index 8ba702a..7b36453 100644 --- a/vite.config.js +++ b/vite.config.js @@ -4,6 +4,28 @@ const { defineConfig } = require('vite') import vue from '@vitejs/plugin-vue' import { viteExternalsPlugin } from 'vite-plugin-externals' +// InvoiceShelf 2.3.0 and later style the app with Tailwind 4, which keeps its +// rules in cascade layers, and CSS outside any layer beats every layered rule. +// Left unlayered, this module's utilities (.hidden, .flex, ...) overrode the +// app's responsive ones and hid the sidebar. The built stylesheet goes into the +// app's `utilities` layer instead, declared in the app's order first, so the +// app's own rule wins wherever both define a class. Hosts before 2.3.0 have no +// layers, and their rules keep winning there too. +const inHostUtilitiesLayer = () => ({ + name: 'in-host-utilities-layer', + closeBundle() { + const fs = require('fs') + const file = path.resolve(__dirname, 'dist/style.css') + + if (!fs.existsSync(file)) { + return + } + + const css = fs.readFileSync(file, 'utf8').replace(/^@charset "[^"]*";\s*/, '') + fs.writeFileSync(file, `@layer theme, base, components, utilities;\n@layer utilities {\n${css}\n}\n`) + }, +}) + module.exports = defineConfig({ build: { lib: { @@ -24,5 +46,6 @@ module.exports = defineConfig({ viteExternalsPlugin({ vue: 'Vue', }), + inHostUtilitiesLayer(), ], }) From 3824f6b216f80a4755389c47e45e80c443876df3 Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski Date: Thu, 24 Sep 2026 12:54:48 +0200 Subject: [PATCH 4/5] style: apply Pint Formatting only: imports for classes named in docblocks, sorted imports and trailing commas. The code has not matched Pint's Laravel preset, which the style check runs, since the module was imported. --- Config/config.php | 2 +- Database/Seeders/WhiteLabelDatabaseSeeder.php | 13 +++++-------- Helpers/VersionHelper.php | 16 +++++++++------- Http/Controllers/UploadLogoController.php | 4 ++-- Http/Controllers/WhiteLabelController.php | 17 +++++++++++------ Listeners/ModuleDisabledListener.php | 1 - Providers/WhiteLabelServiceProvider.php | 6 +++--- 7 files changed, 31 insertions(+), 28 deletions(-) diff --git a/Config/config.php b/Config/config.php index 40022f7..87bf112 100644 --- a/Config/config.php +++ b/Config/config.php @@ -1,5 +1,5 @@ 'WhiteLabel' + 'name' => 'WhiteLabel', ]; diff --git a/Database/Seeders/WhiteLabelDatabaseSeeder.php b/Database/Seeders/WhiteLabelDatabaseSeeder.php index 9f68d6f..9a150c7 100644 --- a/Database/Seeders/WhiteLabelDatabaseSeeder.php +++ b/Database/Seeders/WhiteLabelDatabaseSeeder.php @@ -6,7 +6,6 @@ use App\Models\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; use Modules\WhiteLabel\Helpers\VersionHelper; if (VersionHelper::checkAppVersion('<', '2.0.0')) { @@ -14,8 +13,6 @@ VersionHelper::aliasClass('InvoiceShelf\Models\CompanySetting', 'App\Models\CompanySetting'); } - - class WhiteLabelDatabaseSeeder extends Seeder { /** @@ -32,11 +29,11 @@ public function run() /** * check if customer portal page title entry exists */ - if (!CompanySetting::getSetting('customer_portal_page_title', $company_id)) { - CompanySetting::setSettings( - ['customer_portal_page_title' => ''], - $company_id - ); + if (! CompanySetting::getSetting('customer_portal_page_title', $company_id)) { + CompanySetting::setSettings( + ['customer_portal_page_title' => ''], + $company_id + ); } } diff --git a/Helpers/VersionHelper.php b/Helpers/VersionHelper.php index 421cbd8..bb7acd8 100644 --- a/Helpers/VersionHelper.php +++ b/Helpers/VersionHelper.php @@ -1,4 +1,5 @@ * @copyright 2024 Rihards Simanovics * @license GNU GPLv3 + * * @version GIT: + * * @link https://invoiceshelf.com */ @@ -33,9 +36,8 @@ class VersionHelper * This method checks the application version against a specified version using a comparison operator. * It temporarily aliases the Setting class to access the version information. * - * @param string $operator The comparison operator (e.g., '<', '>='). - * @param string $version The version to compare against. - * + * @param string $operator The comparison operator (e.g., '<', '>='). + * @param string $version The version to compare against. * @return bool True if the comparison is valid, false otherwise. */ public static function checkAppVersion($operator, $version) @@ -49,12 +51,12 @@ public static function checkAppVersion($operator, $version) /** * Alias old namespace classes to new ones if necessary. * - * @param string $oldClass The old class name with namespace. - * @param string $newClass The new class name with namespace. + * @param string $oldClass The old class name with namespace. + * @param string $newClass The new class name with namespace. */ public static function aliasClass($newClass, $oldClass) { - if (!class_exists($oldClass) && class_exists($newClass)) { + if (! class_exists($oldClass) && class_exists($newClass)) { class_alias($newClass, $oldClass); } } diff --git a/Http/Controllers/UploadLogoController.php b/Http/Controllers/UploadLogoController.php index 1af2c10..7aa9135 100644 --- a/Http/Controllers/UploadLogoController.php +++ b/Http/Controllers/UploadLogoController.php @@ -28,7 +28,7 @@ public function uploadLogos(Request $request) $customerPortalLogoUrl = 'whitelabel/customer_portal_logo/'.$imageName; $settings = [ - 'customer_portal_logo' => $customerPortalLogoUrl + 'customer_portal_logo' => $customerPortalLogoUrl, ]; CompanySetting::setSettings($settings, $request->header('company')); @@ -56,7 +56,7 @@ public function uploadLogos(Request $request) 'success' => true, 'customerPortalLogoUrl' => $customerPortalLogoUrl, 'adminPortalLogoUrl' => $adminPortalLogoUrl, - 'loginPageLogoUrl' => $loginPageLogoUrl + 'loginPageLogoUrl' => $loginPageLogoUrl, ], 200); } } diff --git a/Http/Controllers/WhiteLabelController.php b/Http/Controllers/WhiteLabelController.php index 79ff3bd..7ab0df6 100644 --- a/Http/Controllers/WhiteLabelController.php +++ b/Http/Controllers/WhiteLabelController.php @@ -10,6 +10,7 @@ class WhiteLabelController extends Controller { /** * Display a listing of the resource. + * * @return Renderable */ public function index() @@ -19,6 +20,7 @@ public function index() /** * Show the form for creating a new resource. + * * @return Renderable */ public function create() @@ -28,7 +30,7 @@ public function create() /** * Store a newly created resource in storage. - * @param Request $request + * * @return Renderable */ public function store(Request $request) @@ -38,7 +40,8 @@ public function store(Request $request) /** * Show the specified resource. - * @param int $id + * + * @param int $id * @return Renderable */ public function show($id) @@ -48,7 +51,8 @@ public function show($id) /** * Show the form for editing the specified resource. - * @param int $id + * + * @param int $id * @return Renderable */ public function edit($id) @@ -58,8 +62,8 @@ public function edit($id) /** * Update the specified resource in storage. - * @param Request $request - * @param int $id + * + * @param int $id * @return Renderable */ public function update(Request $request, $id) @@ -69,7 +73,8 @@ public function update(Request $request, $id) /** * Remove the specified resource from storage. - * @param int $id + * + * @param int $id * @return Renderable */ public function destroy($id) diff --git a/Listeners/ModuleDisabledListener.php b/Listeners/ModuleDisabledListener.php index a87022b..db66e00 100644 --- a/Listeners/ModuleDisabledListener.php +++ b/Listeners/ModuleDisabledListener.php @@ -28,7 +28,6 @@ public function __construct() /** * Handle the event. * - * @param ModuleDisabledEvent $event * @return void */ public function handle(ModuleDisabledEvent $event) diff --git a/Providers/WhiteLabelServiceProvider.php b/Providers/WhiteLabelServiceProvider.php index 5a016f7..3295f8f 100644 --- a/Providers/WhiteLabelServiceProvider.php +++ b/Providers/WhiteLabelServiceProvider.php @@ -5,8 +5,8 @@ use App\Events\ModuleDisabledEvent; use App\Services\Module\ModuleFacade; use Illuminate\Support\ServiceProvider; -use Modules\WhiteLabel\Listeners\ModuleDisabledListener; use Modules\WhiteLabel\Helpers\VersionHelper; +use Modules\WhiteLabel\Listeners\ModuleDisabledListener; if (VersionHelper::checkAppVersion('<', '2.0.0')) { VersionHelper::aliasClass('InvoiceShelf\Events\ModuleDisabledEvent', 'App\Events\ModuleDisabledEvent'); @@ -80,7 +80,7 @@ public function registerViews() $sourcePath = module_path($this->moduleName, 'Resources/views'); $this->publishes([ - $sourcePath => $viewPath + $sourcePath => $viewPath, ], ['views', $this->moduleNameLower.'-module-views']); $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); @@ -134,7 +134,7 @@ public function registerMenu() 'icon' => 'TagIcon', 'owner_only' => true, 'ability' => '', - 'model' => '' + 'model' => '', ]; \Menu::make('setting_menu', function ($menu) use ($data) { From 5ff61e949e2a0d755d46ab7454bece0ca99f5d23 Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski Date: Thu, 24 Sep 2026 12:54:48 +0200 Subject: [PATCH 5/5] ci: run the style check for real, drop the jobs copied from the app The check workflow was InvoiceShelf's own: it ran pint from a vendor directory the module never installs, then php artisan test and a make dist of InvoiceShelf.zip, which a module has none of, so it failed on every push. It now installs Pint 1.29.0 through setup-php and checks the code style; release.yaml builds the module's zip. --- .github/workflows/check.yaml | 108 ++++------------------------------- 1 file changed, 10 insertions(+), 98 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index f0ba489..e4316ad 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -31,113 +31,25 @@ jobs: with: access_token: ${{ github.token }} + # A copy of InvoiceShelf's own workflow used to run here: `pint` from a + # vendor directory the module never installs, `php artisan test` and a + # `make dist` of InvoiceShelf.zip, none of which a module has. It is now the + # style check alone; release.yaml builds the module's zip. php_syntax_errors: name: 1️⃣ PHP Code Style errors runs-on: ubuntu-latest needs: - kill_previous steps: - - name: Set up PHP + - name: Set up PHP with Pint uses: shivammathur/setup-php@v2 with: - php-version: 8.2 + php-version: 8.4 + tools: pint:1.29.0 + coverage: none - name: Checkout code uses: actions/checkout@v4 - - name: Install dependencies - uses: ramsey/composer-install@v2 - - - name: Check source code for syntax errors - run: ./vendor/bin/pint --test - - tests: - name: 2️⃣ PHP ${{ matrix.php-version }} Tests - needs: - - php_syntax_errors - runs-on: ubuntu-latest - strategy: - matrix: - php-version: - - 8.2 - - 8.3 - env: - extensions: bcmath, curl, dom, gd, imagick, json, libxml, mbstring, pcntl, pdo, pdo_mysql, zip - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup PHP Action - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-version }} - extensions: ${{ env.extensions }} - coverage: xdebug - tools: pecl, composer - - - name: Install Composer dependencies - uses: ramsey/composer-install@v2 - - - name: Use Node.js 20 - uses: actions/setup-node@v3 - with: - node-version: 20 - - - name: Install - run: npm install - - - name: Compile Front-end - run: npm run build - - - name: Apply tests ${{ matrix.php-version }} - run: php artisan test - - createReleaseFile: - name: 3️⃣ Build / Upload - Release File - if: github.ref_type == 'tag' - needs: - - tests - runs-on: ubuntu-latest - env: - extensions: bcmath, curl, dom, gd, imagick, json, libxml, mbstring, pcntl, pdo, pdo_mysql, zip - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.2 - extensions: ${{ env.extensions }} - coverage: none - - - name: Install Composer dependencies - uses: ramsey/composer-install@v2 - with: - composer-options: --no-dev - - - name: Use Node.js 20 - uses: actions/setup-node@v3 - with: - node-version: 20 - - - name: Install - run: npm install - - - name: Compile Front-end - run: npm run build - - - name: Build Dist - run: | - make clean dist - - - name: Upload package - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ github.token }} - file: InvoiceShelf.zip - asset_name: InvoiceShelf.zip - tag: ${{ github.ref }} - overwrite: true + - name: Check code style + run: pint --test