Compare commits
54 Commits
2.2.1
...
backport-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
525f432ee0 | ||
|
|
f17c7be5f0 | ||
|
|
0e9f18d4d1 | ||
|
|
e22050bc71 | ||
|
|
af9d672574 | ||
|
|
7606f8ece8 | ||
|
|
9b0498a2e5 | ||
|
|
04c7682e73 | ||
|
|
88650c2f3e | ||
|
|
ee76f31138 | ||
|
|
e1af9f56c4 | ||
|
|
fdd860c381 | ||
|
|
834b53ea40 | ||
|
|
5ff051fbb5 | ||
|
|
0d7059fcf6 | ||
|
|
7d9fdb79cc | ||
|
|
3d871604ae | ||
|
|
1adebe85b9 | ||
|
|
defbfc6406 | ||
|
|
25986b7bd5 | ||
|
|
751bd4a1c8 | ||
|
|
0e313b80ca | ||
|
|
5014a75fbc | ||
|
|
9e5b9fdaad | ||
|
|
08dfe62312 | ||
|
|
f623cd0179 | ||
|
|
77fd96d499 | ||
|
|
2e4e19dfc5 | ||
|
|
76c02be219 | ||
|
|
414531524c | ||
|
|
d75a957183 | ||
|
|
63d3a7fc8e | ||
|
|
0be747a483 | ||
|
|
3ceb08bc31 | ||
|
|
ad5a7e51b9 | ||
|
|
691178857f | ||
|
|
a38f09cf7b | ||
|
|
de4ba6bba0 | ||
|
|
375cfc6b18 | ||
|
|
aa88dc340d | ||
|
|
7004bf375e | ||
|
|
80889293bf | ||
|
|
d754c4d29e | ||
|
|
8de32e27d3 | ||
|
|
0f933b6217 | ||
|
|
3bae2c282c | ||
|
|
14b5aaa0c9 | ||
|
|
241ec09220 | ||
|
|
ed7af3fc3c | ||
|
|
aafcf147cf | ||
|
|
3c31baf20d | ||
|
|
a386fd19fc | ||
|
|
e5f6984078 | ||
|
|
d2953e9409 |
@@ -10,7 +10,7 @@ metadata:
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Use `search-docs` for detailed Tailwind CSS v3 patterns and documentation.
|
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
|
||||||
|
|
||||||
## Basic Usage
|
## Basic Usage
|
||||||
|
|
||||||
@@ -18,22 +18,55 @@ Use `search-docs` for detailed Tailwind CSS v3 patterns and documentation.
|
|||||||
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
|
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
|
||||||
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
|
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
|
||||||
|
|
||||||
## Tailwind CSS v3 Specifics
|
## Tailwind CSS v4 Specifics
|
||||||
|
|
||||||
- Always use Tailwind CSS v3 and verify you're using only classes it supports.
|
- Always use Tailwind CSS v4 and avoid deprecated utilities.
|
||||||
- Configuration is done in the `tailwind.config.js` file.
|
- `corePlugins` is not supported in Tailwind v4.
|
||||||
- Import using `@tailwind` directives:
|
|
||||||
|
|
||||||
<!-- v3 Import Syntax -->
|
### CSS-First Configuration
|
||||||
|
|
||||||
|
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
|
||||||
|
|
||||||
|
<!-- CSS-First Config -->
|
||||||
```css
|
```css
|
||||||
@tailwind base;
|
@theme {
|
||||||
@tailwind components;
|
--color-brand: oklch(0.72 0.11 178);
|
||||||
@tailwind utilities;
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Import Syntax
|
||||||
|
|
||||||
|
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
|
||||||
|
|
||||||
|
<!-- v4 Import Syntax -->
|
||||||
|
```diff
|
||||||
|
- @tailwind base;
|
||||||
|
- @tailwind components;
|
||||||
|
- @tailwind utilities;
|
||||||
|
+ @import "tailwindcss";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Replaced Utilities
|
||||||
|
|
||||||
|
Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
|
||||||
|
|
||||||
|
| Deprecated | Replacement |
|
||||||
|
|------------|-------------|
|
||||||
|
| bg-opacity-* | bg-black/* |
|
||||||
|
| text-opacity-* | text-black/* |
|
||||||
|
| border-opacity-* | border-black/* |
|
||||||
|
| divide-opacity-* | divide-black/* |
|
||||||
|
| ring-opacity-* | ring-black/* |
|
||||||
|
| placeholder-opacity-* | placeholder-black/* |
|
||||||
|
| flex-shrink-* | shrink-* |
|
||||||
|
| flex-grow-* | grow-* |
|
||||||
|
| overflow-ellipsis | text-ellipsis |
|
||||||
|
| decoration-slice | box-decoration-slice |
|
||||||
|
| decoration-clone | box-decoration-clone |
|
||||||
|
|
||||||
## Spacing
|
## Spacing
|
||||||
|
|
||||||
When listing items, use gap utilities for spacing; don't use margins.
|
Use `gap` utilities instead of margins for spacing between siblings:
|
||||||
|
|
||||||
<!-- Gap Utilities -->
|
<!-- Gap Utilities -->
|
||||||
```html
|
```html
|
||||||
@@ -77,15 +110,10 @@ If existing pages and components support dark mode, new pages and components mus
|
|||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
1. Check browser for visual rendering
|
|
||||||
2. Test responsive breakpoints
|
|
||||||
3. Verify dark mode if project uses it
|
|
||||||
|
|
||||||
## Common Pitfalls
|
## Common Pitfalls
|
||||||
|
|
||||||
|
- Using deprecated v3 utilities (bg-opacity-*, flex-shrink-*, etc.)
|
||||||
|
- Using `@tailwind` directives instead of `@import "tailwindcss"`
|
||||||
|
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
|
||||||
- Using margins for spacing between siblings instead of gap utilities
|
- Using margins for spacing between siblings instead of gap utilities
|
||||||
- Forgetting to add dark mode variants when the project uses dark mode
|
- Forgetting to add dark mode variants when the project uses dark mode
|
||||||
- Not checking existing project conventions before adding new utilities
|
|
||||||
- Overusing inline styles when Tailwind classes would suffice
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// .eslintrc.js
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
extends: [
|
|
||||||
// add more generic rulesets here, such as:
|
|
||||||
// 'eslint:recommended',
|
|
||||||
"plugin:vue/vue3-recommended",
|
|
||||||
"prettier",
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
// override/add rules settings here, such as:
|
|
||||||
// 'vue/no-unused-vars': 'error'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
18
.github/release.yml
vendored
Normal file
18
.github/release.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
changelog:
|
||||||
|
categories:
|
||||||
|
- title: New Features
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
- feature
|
||||||
|
- title: Bug Fixes
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
- fix
|
||||||
|
- title: Maintenance
|
||||||
|
labels:
|
||||||
|
- chore
|
||||||
|
- dependencies
|
||||||
|
- ci
|
||||||
|
- title: Other Changes
|
||||||
|
labels:
|
||||||
|
- "*"
|
||||||
43
.github/workflows/check.yaml
vendored
43
.github/workflows/check.yaml
vendored
@@ -3,28 +3,41 @@ name: Check
|
|||||||
# Run this workflow every time a new commit pushed to your repository
|
# Run this workflow every time a new commit pushed to your repository
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths-ignore:
|
|
||||||
- '.github/**/*'
|
|
||||||
- '**/*.md'
|
|
||||||
- 'public/build/*.js'
|
|
||||||
- 'public/build/**/*.js'
|
|
||||||
- 'docker/**/*'
|
|
||||||
- 'resources/scripts/**/*'
|
|
||||||
tags-ignore:
|
tags-ignore:
|
||||||
- "*"
|
- "*"
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'translations'
|
- 'translations'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
|
||||||
- '**/*.md'
|
|
||||||
- 'public/build/*.js'
|
|
||||||
- 'public/build/**/*.js'
|
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'translations'
|
- 'translations'
|
||||||
# Allow manually triggering the workflow.
|
# Allow manually triggering the workflow.
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
changes:
|
||||||
|
name: 🔍 Detect changes
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
php: ${{ steps.filter.outputs.php }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check for file changes
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
id: filter
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
php:
|
||||||
|
- 'app/**'
|
||||||
|
- 'config/**'
|
||||||
|
- 'database/**'
|
||||||
|
- 'routes/**'
|
||||||
|
- 'tests/**'
|
||||||
|
- 'composer.json'
|
||||||
|
- 'composer.lock'
|
||||||
|
- 'phpunit.xml'
|
||||||
|
|
||||||
kill_previous:
|
kill_previous:
|
||||||
name: 0️⃣ Kill previous runs
|
name: 0️⃣ Kill previous runs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -41,6 +54,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- kill_previous
|
- kill_previous
|
||||||
|
- changes
|
||||||
|
if: needs.changes.outputs.php == 'true'
|
||||||
steps:
|
steps:
|
||||||
- name: Set up PHP
|
- name: Set up PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
@@ -60,6 +75,8 @@ jobs:
|
|||||||
name: 2️⃣ PHP ${{ matrix.php-version }} Tests
|
name: 2️⃣ PHP ${{ matrix.php-version }} Tests
|
||||||
needs:
|
needs:
|
||||||
- php_syntax_errors
|
- php_syntax_errors
|
||||||
|
- changes
|
||||||
|
if: needs.changes.outputs.php == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -83,10 +100,10 @@ jobs:
|
|||||||
- name: Install Composer dependencies
|
- name: Install Composer dependencies
|
||||||
uses: ramsey/composer-install@v2
|
uses: ramsey/composer-install@v2
|
||||||
|
|
||||||
- name: Use Node.js 20
|
- name: Use Node.js 24
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 24
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|||||||
8
.github/workflows/docker.yaml
vendored
8
.github/workflows/docker.yaml
vendored
@@ -61,10 +61,10 @@ jobs:
|
|||||||
- name: Install Composer dependencies
|
- name: Install Composer dependencies
|
||||||
uses: ramsey/composer-install@v2
|
uses: ramsey/composer-install@v2
|
||||||
|
|
||||||
- name: Use Node.js 20
|
- name: Use Node.js 24
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 24
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: npm install
|
run: npm install
|
||||||
@@ -100,10 +100,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
composer-options: --no-dev
|
composer-options: --no-dev
|
||||||
|
|
||||||
- name: Use Node.js 20
|
- name: Use Node.js 24
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 24
|
||||||
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|||||||
96
.github/workflows/release.yaml
vendored
Normal file
96
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Build & Release
|
||||||
|
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@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: 8.4
|
||||||
|
extensions: ${{ env.extensions }}
|
||||||
|
tools: composer
|
||||||
|
|
||||||
|
- name: Install Composer dependencies
|
||||||
|
run: composer install --no-dev --optimize-autoloader --no-interaction
|
||||||
|
|
||||||
|
- name: Use Node.js 24
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
|
||||||
|
- name: Install npm dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build frontend
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Prepare release directory
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/InvoiceShelf/public
|
||||||
|
|
||||||
|
cp -r app /tmp/InvoiceShelf/
|
||||||
|
cp -r bootstrap /tmp/InvoiceShelf/
|
||||||
|
cp -r config /tmp/InvoiceShelf/
|
||||||
|
cp -r database /tmp/InvoiceShelf/
|
||||||
|
cp -r public/build /tmp/InvoiceShelf/public/
|
||||||
|
cp -r public/favicons /tmp/InvoiceShelf/public/
|
||||||
|
cp public/.htaccess /tmp/InvoiceShelf/public/
|
||||||
|
cp public/index.php /tmp/InvoiceShelf/public/
|
||||||
|
cp public/robots.txt /tmp/InvoiceShelf/public/
|
||||||
|
cp public/web.config /tmp/InvoiceShelf/public/
|
||||||
|
cp -r resources /tmp/InvoiceShelf/
|
||||||
|
cp -r lang /tmp/InvoiceShelf/
|
||||||
|
cp -r routes /tmp/InvoiceShelf/
|
||||||
|
cp -r storage /tmp/InvoiceShelf/
|
||||||
|
cp -r vendor /tmp/InvoiceShelf/ 2>/dev/null || true
|
||||||
|
cp -r scripts /tmp/InvoiceShelf/
|
||||||
|
cp version.md /tmp/InvoiceShelf/
|
||||||
|
cp .env.example /tmp/InvoiceShelf/
|
||||||
|
cp artisan /tmp/InvoiceShelf/
|
||||||
|
cp composer.json /tmp/InvoiceShelf/
|
||||||
|
cp composer.lock /tmp/InvoiceShelf/
|
||||||
|
cp LICENSE /tmp/InvoiceShelf/
|
||||||
|
cp readme.md /tmp/InvoiceShelf/
|
||||||
|
cp SECURITY.md /tmp/InvoiceShelf/
|
||||||
|
cp server.php /tmp/InvoiceShelf/
|
||||||
|
|
||||||
|
# Clean up runtime artifacts
|
||||||
|
find /tmp/InvoiceShelf -wholename '*/[Tt]ests/*' -delete
|
||||||
|
find /tmp/InvoiceShelf -wholename '*/[Tt]est/*' -delete
|
||||||
|
rm -rf /tmp/InvoiceShelf/storage/framework/cache/data/* 2>/dev/null || true
|
||||||
|
rm -f /tmp/InvoiceShelf/storage/framework/sessions/* 2>/dev/null || true
|
||||||
|
rm -f /tmp/InvoiceShelf/storage/framework/views/* 2>/dev/null || true
|
||||||
|
rm -f /tmp/InvoiceShelf/storage/logs/* 2>/dev/null || true
|
||||||
|
touch /tmp/InvoiceShelf/storage/logs/laravel.log
|
||||||
|
|
||||||
|
- name: Generate manifest
|
||||||
|
run: php scripts/generate-manifest.php /tmp/InvoiceShelf
|
||||||
|
|
||||||
|
- name: Create zip
|
||||||
|
working-directory: /tmp
|
||||||
|
run: zip -r InvoiceShelf.zip InvoiceShelf/
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: /tmp/InvoiceShelf.zip
|
||||||
|
generate_release_notes: true
|
||||||
|
make_latest: true
|
||||||
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
24
|
||||||
5909
.phpstorm.meta.php
Normal file
5909
.phpstorm.meta.php
Normal file
File diff suppressed because it is too large
Load Diff
80
CLAUDE.md
Normal file
80
CLAUDE.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
InvoiceShelf is an open-source invoicing and expense tracking application built with Laravel 13 (PHP 8.4) and Vue 3. It supports multi-company tenancy, customer portals, recurring invoices, and PDF generation.
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
### Development
|
||||||
|
```bash
|
||||||
|
composer run dev # Starts PHP server, queue listener, log tail, and Vite dev server concurrently
|
||||||
|
npm run dev # Vite dev server only
|
||||||
|
npm run build # Production frontend build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
```bash
|
||||||
|
php artisan test --compact # Run all tests
|
||||||
|
php artisan test --compact --filter=testName # Run specific test
|
||||||
|
./vendor/bin/pest --stop-on-failure # Run via Pest directly
|
||||||
|
make test # Makefile shortcut
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests use SQLite in-memory DB, configured in `phpunit.xml`. Tests seed via `DatabaseSeeder` + `DemoSeeder` in `beforeEach`. Authenticate with `Sanctum::actingAs()` and set the `company` header.
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
```bash
|
||||||
|
vendor/bin/pint --dirty --format agent # Fix style on modified PHP files
|
||||||
|
vendor/bin/pint --test # Check style without fixing (CI uses this)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Artisan Generators
|
||||||
|
Always use `php artisan make:*` with `--no-interaction` to create new files (models, controllers, migrations, tests, etc.).
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Multi-Tenancy
|
||||||
|
Every major model has a `company_id` foreign key. The `CompanyMiddleware` sets the active company from the `company` request header. Bouncer authorization is scoped to the company level via `DefaultScope` (`app/Bouncer/Scopes/DefaultScope.php`).
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
Three guards: `web` (session), `api` (Sanctum tokens for `/api/v1/`), `customer` (session for customer portal). API routes use `auth:sanctum` middleware; customer portal uses `auth:customer`.
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
- **API**: All endpoints under `/api/v1/` in `routes/api.php`, grouped with `auth:sanctum`, `company`, and `bouncer` middleware
|
||||||
|
- **Web**: `routes/web.php` serves PDF endpoints, auth pages, and catch-all SPA routes (`/admin/{vue?}`, `/{company:slug}/customer/{vue?}`)
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- Entry point: `resources/scripts/main.js`
|
||||||
|
- Vue Router: `resources/scripts/admin/admin-router.js` (admin), `resources/scripts/customer/customer-router.js` (customer portal)
|
||||||
|
- State: Pinia stores in `resources/scripts/admin/stores/`
|
||||||
|
- Path aliases: `@` = `resources/`, `$fonts`, `$images` for static assets
|
||||||
|
- Vite dev server expects `invoiceshelf.test` hostname
|
||||||
|
|
||||||
|
### Backend Patterns
|
||||||
|
- **Authorization**: Silber/Bouncer with policies in `app/Policies/`. Controllers use `$this->authorize()`.
|
||||||
|
- **Validation**: Form Request classes, never inline validation
|
||||||
|
- **API responses**: Eloquent API Resources in `app/Http/Resources/`
|
||||||
|
- **PDF generation**: DomPDF (`GeneratesPdfTrait`) or Gotenberg
|
||||||
|
- **Email**: Mailable classes with `EmailLog` tracking
|
||||||
|
- **File storage**: Spatie MediaLibrary, supports local/S3/Dropbox
|
||||||
|
- **Serial numbers**: `SerialNumberFormatter` service
|
||||||
|
- **Company settings**: `CompanySetting` model (key-value per company)
|
||||||
|
|
||||||
|
### Database
|
||||||
|
Supports MySQL, PostgreSQL, and SQLite. Prefer Eloquent over raw queries. Use `Model::query()` instead of `DB::`. Use eager loading to prevent N+1 queries.
|
||||||
|
|
||||||
|
## Code Conventions
|
||||||
|
|
||||||
|
- PHP: snake_case, constructor property promotion, explicit return types, PHPDoc blocks over inline comments
|
||||||
|
- JS: camelCase
|
||||||
|
- Always check sibling files for patterns before creating new ones
|
||||||
|
- Use `config()` helper, never `env()` outside config files
|
||||||
|
- Every change must have tests (feature tests preferred over unit tests)
|
||||||
|
- Run `vendor/bin/pint --dirty --format agent` after modifying PHP files
|
||||||
|
|
||||||
|
## CI Pipeline
|
||||||
|
|
||||||
|
GitHub Actions (`check.yaml`): runs Pint style check, then builds frontend and runs Pest tests on PHP 8.4.
|
||||||
2
Makefile
2
Makefile
@@ -29,6 +29,7 @@ dist-gen: clean composer npm-build
|
|||||||
@cp -r routes InvoiceShelf
|
@cp -r routes InvoiceShelf
|
||||||
@cp -r storage InvoiceShelf
|
@cp -r storage InvoiceShelf
|
||||||
@cp -r vendor InvoiceShelf 2> /dev/null || true
|
@cp -r vendor InvoiceShelf 2> /dev/null || true
|
||||||
|
@cp -r scripts InvoiceShelf
|
||||||
@cp -r version.md InvoiceShelf
|
@cp -r version.md InvoiceShelf
|
||||||
@cp -r .env.example InvoiceShelf
|
@cp -r .env.example InvoiceShelf
|
||||||
@cp -r artisan InvoiceShelf
|
@cp -r artisan InvoiceShelf
|
||||||
@@ -47,6 +48,7 @@ dist-clean: dist-gen
|
|||||||
@rm InvoiceShelf/storage/framework/sessions/* 2> /dev/null || true
|
@rm InvoiceShelf/storage/framework/sessions/* 2> /dev/null || true
|
||||||
@rm InvoiceShelf/storage/framework/views/* 2> /dev/null || true
|
@rm InvoiceShelf/storage/framework/views/* 2> /dev/null || true
|
||||||
@rm InvoiceShelf/storage/logs/* 2> /dev/null || true
|
@rm InvoiceShelf/storage/logs/* 2> /dev/null || true
|
||||||
|
@php scripts/generate-manifest.php InvoiceShelf
|
||||||
|
|
||||||
dist: dist-clean
|
dist: dist-clean
|
||||||
@zip -r InvoiceShelf.zip InvoiceShelf
|
@zip -r InvoiceShelf.zip InvoiceShelf
|
||||||
|
|||||||
12337
_ide_helper.php
12337
_ide_helper.php
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ class ResetApp extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $description = 'Clean database, database_created and public/storage folder';
|
protected $description = 'Clean database and public/storage folder';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new command instance.
|
* Create a new command instance.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
namespace App\Http\Controllers\V1\Admin\Backup;
|
namespace App\Http\Controllers\V1\Admin\Backup;
|
||||||
|
|
||||||
use App\Jobs\CreateBackupJob;
|
use App\Jobs\CreateBackupJob;
|
||||||
|
use App\Models\FileDisk;
|
||||||
use App\Rules\Backup\PathToZip;
|
use App\Rules\Backup\PathToZip;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -27,6 +28,16 @@ class BackupsController extends ApiController
|
|||||||
$configuredBackupDisks = config('backup.backup.destination.disks');
|
$configuredBackupDisks = config('backup.backup.destination.disks');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if ($request->file_disk_id) {
|
||||||
|
$fileDisk = FileDisk::find($request->file_disk_id);
|
||||||
|
if ($fileDisk) {
|
||||||
|
$fileDisk->setConfig();
|
||||||
|
$prefix = env('DYNAMIC_DISK_PREFIX', 'temp_');
|
||||||
|
config(['backup.backup.destination.disks' => [$prefix.$fileDisk->driver]]);
|
||||||
|
$configuredBackupDisks = config('backup.backup.destination.disks');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name'));
|
$backupDestination = BackupDestination::create(config('filesystems.default'), config('backup.backup.name'));
|
||||||
|
|
||||||
$backups = Cache::remember("backups-{$request->file_disk_id}", now()->addSeconds(4), function () use ($backupDestination) {
|
$backups = Cache::remember("backups-{$request->file_disk_id}", now()->addSeconds(4), function () use ($backupDestination) {
|
||||||
@@ -65,7 +76,10 @@ class BackupsController extends ApiController
|
|||||||
{
|
{
|
||||||
$this->authorize('manage backups');
|
$this->authorize('manage backups');
|
||||||
|
|
||||||
dispatch(new CreateBackupJob($request->all()))->onQueue(config('backup.queue.name'));
|
$data = $request->all();
|
||||||
|
$data['company'] = $request->header('company');
|
||||||
|
|
||||||
|
dispatch(new CreateBackupJob($data))->onQueue(config('backup.queue.name'));
|
||||||
|
|
||||||
return $this->respondSuccess();
|
return $this->respondSuccess();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,10 +61,10 @@ class CompaniesController extends Controller
|
|||||||
$company = Company::find($request->header('company'));
|
$company = Company::find($request->header('company'));
|
||||||
$this->authorize('transfer company ownership', $company);
|
$this->authorize('transfer company ownership', $company);
|
||||||
|
|
||||||
if ($user->hasCompany($company->id)) {
|
if (! $user->hasCompany($company->id)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'User does not belongs to this company.',
|
'message' => 'User does not belong to this company.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,11 @@ class CustomersController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('delete multiple customers');
|
$this->authorize('delete multiple customers');
|
||||||
|
|
||||||
Customer::deleteCustomers($request->ids);
|
$ids = Customer::whereCompany()
|
||||||
|
->whereIn('id', $request->ids)
|
||||||
|
->pluck('id');
|
||||||
|
|
||||||
|
Customer::deleteCustomers($ids);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class CloneEstimateController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, Estimate $estimate)
|
public function __invoke(Request $request, Estimate $estimate)
|
||||||
{
|
{
|
||||||
|
$this->authorize('view', $estimate);
|
||||||
$this->authorize('create', Estimate::class);
|
$this->authorize('create', Estimate::class);
|
||||||
|
|
||||||
$date = Carbon::now();
|
$date = Carbon::now();
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ class EstimatesController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('delete multiple estimates');
|
$this->authorize('delete multiple estimates');
|
||||||
|
|
||||||
Estimate::destroy($request->ids);
|
$ids = Estimate::whereCompany()
|
||||||
|
->whereIn('id', $request->ids)
|
||||||
|
->pluck('id');
|
||||||
|
|
||||||
|
Estimate::destroy($ids);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@@ -81,7 +81,11 @@ class ExpensesController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('delete multiple expenses');
|
$this->authorize('delete multiple expenses');
|
||||||
|
|
||||||
Expense::destroy($request->ids);
|
$ids = Expense::whereCompany()
|
||||||
|
->whereIn('id', $request->ids)
|
||||||
|
->pluck('id');
|
||||||
|
|
||||||
|
Expense::destroy($ids);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class SearchController extends Controller
|
|||||||
->paginate(10);
|
->paginate(10);
|
||||||
|
|
||||||
if ($user->isOwner()) {
|
if ($user->isOwner()) {
|
||||||
$users = User::applyFilters($request->only(['search']))
|
$users = User::whereCompany()
|
||||||
|
->applyFilters($request->only(['search']))
|
||||||
->latest()
|
->latest()
|
||||||
->paginate(10);
|
->paginate(10);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class CloneInvoiceController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, Invoice $invoice)
|
public function __invoke(Request $request, Invoice $invoice)
|
||||||
{
|
{
|
||||||
|
$this->authorize('view', $invoice);
|
||||||
$this->authorize('create', Invoice::class);
|
$this->authorize('create', Invoice::class);
|
||||||
|
|
||||||
$date = Carbon::now();
|
$date = Carbon::now();
|
||||||
|
|||||||
@@ -100,7 +100,11 @@ class InvoicesController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('delete multiple invoices');
|
$this->authorize('delete multiple invoices');
|
||||||
|
|
||||||
Invoice::deleteInvoices($request->ids);
|
$ids = Invoice::whereCompany()
|
||||||
|
->whereIn('id', $request->ids)
|
||||||
|
->pluck('id');
|
||||||
|
|
||||||
|
Invoice::deleteInvoices($ids);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@@ -90,7 +90,11 @@ class ItemsController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('delete multiple items');
|
$this->authorize('delete multiple items');
|
||||||
|
|
||||||
Item::destroy($request->ids);
|
$ids = Item::whereCompany()
|
||||||
|
->whereIn('id', $request->ids)
|
||||||
|
->pluck('id');
|
||||||
|
|
||||||
|
Item::destroy($ids);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@@ -73,7 +73,11 @@ class PaymentsController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('delete multiple payments');
|
$this->authorize('delete multiple payments');
|
||||||
|
|
||||||
Payment::deletePayments($request->ids);
|
$ids = Payment::whereCompany()
|
||||||
|
->whereIn('id', $request->ids)
|
||||||
|
->pluck('id');
|
||||||
|
|
||||||
|
Payment::deletePayments($ids);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@@ -84,7 +84,11 @@ class RecurringInvoiceController extends Controller
|
|||||||
{
|
{
|
||||||
$this->authorize('delete multiple recurring invoices');
|
$this->authorize('delete multiple recurring invoices');
|
||||||
|
|
||||||
RecurringInvoice::deleteRecurringInvoice($request->ids);
|
$ids = RecurringInvoice::whereCompany()
|
||||||
|
->whereIn('id', $request->ids)
|
||||||
|
->pluck('id');
|
||||||
|
|
||||||
|
RecurringInvoice::deleteRecurringInvoice($ids);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ use App\Models\Company;
|
|||||||
use App\Models\CompanySetting;
|
use App\Models\CompanySetting;
|
||||||
use App\Models\Currency;
|
use App\Models\Currency;
|
||||||
use App\Models\Expense;
|
use App\Models\Expense;
|
||||||
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use PDF;
|
|
||||||
|
|
||||||
class ExpensesReportController extends Controller
|
class ExpensesReportController extends Controller
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,7 @@ class ExpensesReportController extends Controller
|
|||||||
* Handle the incoming request.
|
* Handle the incoming request.
|
||||||
*
|
*
|
||||||
* @param string $hash
|
* @param string $hash
|
||||||
* @return JsonResponse
|
* @return View|Response
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, $hash)
|
public function __invoke(Request $request, $hash)
|
||||||
{
|
{
|
||||||
@@ -31,14 +32,26 @@ class ExpensesReportController extends Controller
|
|||||||
|
|
||||||
App::setLocale($locale);
|
App::setLocale($locale);
|
||||||
|
|
||||||
$expenseCategories = Expense::with('category')
|
// Fetch individual expenses (filtered and ordered by date), then group by category
|
||||||
|
$expenses = Expense::with('category')
|
||||||
->whereCompanyId($company->id)
|
->whereCompanyId($company->id)
|
||||||
->applyFilters($request->only(['from_date', 'to_date']))
|
->applyFilters($request->only(['from_date', 'to_date', 'expense_category_id']))
|
||||||
->expensesAttributes()
|
->orderBy('expense_date', 'asc')
|
||||||
->get();
|
->get();
|
||||||
$totalAmount = 0;
|
|
||||||
foreach ($expenseCategories as $category) {
|
$totalAmount = $expenses->sum('base_amount');
|
||||||
$totalAmount += $category->total_amount;
|
|
||||||
|
$grouped = $expenses->groupBy(function ($item) {
|
||||||
|
return $item->category ? $item->category->name : trans('expenses.uncategorized');
|
||||||
|
});
|
||||||
|
|
||||||
|
$expenseGroups = collect();
|
||||||
|
foreach ($grouped as $categoryName => $group) {
|
||||||
|
$expenseGroups->push([
|
||||||
|
'name' => $categoryName,
|
||||||
|
'expenses' => $group,
|
||||||
|
'total' => $group->sum('base_amount'),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
||||||
@@ -62,7 +75,7 @@ class ExpensesReportController extends Controller
|
|||||||
->get();
|
->get();
|
||||||
|
|
||||||
view()->share([
|
view()->share([
|
||||||
'expenseCategories' => $expenseCategories,
|
'expenseGroups' => $expenseGroups,
|
||||||
'colorSettings' => $colorSettings,
|
'colorSettings' => $colorSettings,
|
||||||
'totalExpense' => $totalAmount,
|
'totalExpense' => $totalAmount,
|
||||||
'company' => $company,
|
'company' => $company,
|
||||||
@@ -70,7 +83,7 @@ class ExpensesReportController extends Controller
|
|||||||
'to_date' => $to_date,
|
'to_date' => $to_date,
|
||||||
'currency' => $currency,
|
'currency' => $currency,
|
||||||
]);
|
]);
|
||||||
$pdf = PDF::loadView('app.pdf.reports.expenses');
|
$pdf = Pdf::loadView('app.pdf.reports.expenses');
|
||||||
|
|
||||||
if ($request->has('preview')) {
|
if ($request->has('preview')) {
|
||||||
return view('app.pdf.reports.expenses');
|
return view('app.pdf.reports.expenses');
|
||||||
|
|||||||
@@ -165,11 +165,11 @@ class MailConfigurationController extends Controller
|
|||||||
switch ($driver) {
|
switch ($driver) {
|
||||||
case 'smtp':
|
case 'smtp':
|
||||||
$MailData = array_merge($MailData, [
|
$MailData = array_merge($MailData, [
|
||||||
'mail_host' => $mailSettings['mail_host'] ?? '',
|
'mail_host' => $mailSettings['mail_host'] ?? config('mail.mailers.smtp.host', ''),
|
||||||
'mail_port' => $mailSettings['mail_port'] ?? '',
|
'mail_port' => $mailSettings['mail_port'] ?? config('mail.mailers.smtp.port', ''),
|
||||||
'mail_username' => $mailSettings['mail_username'] ?? '',
|
'mail_username' => $mailSettings['mail_username'] ?? config('mail.mailers.smtp.username', ''),
|
||||||
'mail_password' => $mailSettings['mail_password'] ?? '',
|
'mail_password' => $mailSettings['mail_password'] ?? config('mail.mailers.smtp.password', ''),
|
||||||
'mail_encryption' => $mailSettings['mail_encryption'] ?? 'none',
|
'mail_encryption' => $mailSettings['mail_encryption'] ?? config('mail.mailers.smtp.encryption', 'none'),
|
||||||
'mail_scheme' => $mailSettings['mail_scheme'] ?? '',
|
'mail_scheme' => $mailSettings['mail_scheme'] ?? '',
|
||||||
'mail_url' => $mailSettings['mail_url'] ?? '',
|
'mail_url' => $mailSettings['mail_url'] ?? '',
|
||||||
'mail_timeout' => $mailSettings['mail_timeout'] ?? '',
|
'mail_timeout' => $mailSettings['mail_timeout'] ?? '',
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\V1\Admin\Update;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Space\Updater;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
|
class CleanFilesController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the incoming request.
|
||||||
|
*
|
||||||
|
* Removes any file that does not appear in the release manifest.json,
|
||||||
|
* replacing the legacy hardcoded deleted_files list. Falls back to the
|
||||||
|
* legacy deleteFiles() behaviour when the request still ships a
|
||||||
|
* deleted_files payload (backward compatibility for older release
|
||||||
|
* packages built before the manifest was introduced).
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
if ((! $request->user()) || (! $request->user()->isOwner())) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'You are not allowed to update this app.',
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility: a release package built before the manifest
|
||||||
|
// was introduced may still ship a deleted_files list. Honour it.
|
||||||
|
if (isset($request->deleted_files) && ! empty($request->deleted_files)) {
|
||||||
|
Updater::deleteFiles($request->deleted_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Updater::cleanStaleFiles();
|
||||||
|
|
||||||
|
return response()->json($result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,14 +25,15 @@ class UsersController extends Controller
|
|||||||
|
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
$users = User::applyFilters($request->all())
|
$users = User::whereCompany()
|
||||||
|
->applyFilters($request->all())
|
||||||
->where('id', '<>', $user->id)
|
->where('id', '<>', $user->id)
|
||||||
->latest()
|
->latest()
|
||||||
->paginate($limit);
|
->paginate($limit);
|
||||||
|
|
||||||
return UserResource::collection($users)
|
return UserResource::collection($users)
|
||||||
->additional(['meta' => [
|
->additional(['meta' => [
|
||||||
'user_total_count' => User::count(),
|
'user_total_count' => User::whereCompany()->count(),
|
||||||
]]);
|
]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,8 +48,9 @@ class DatabaseConfigurationController extends Controller
|
|||||||
public function getDatabaseEnvironment(Request $request)
|
public function getDatabaseEnvironment(Request $request)
|
||||||
{
|
{
|
||||||
$databaseData = [];
|
$databaseData = [];
|
||||||
|
$connection = $request->connection ?? config('database.default');
|
||||||
|
|
||||||
switch ($request->connection) {
|
switch ($connection) {
|
||||||
case 'sqlite':
|
case 'sqlite':
|
||||||
$databaseData = [
|
$databaseData = [
|
||||||
'database_connection' => 'sqlite',
|
'database_connection' => 'sqlite',
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers\V1\Installation;
|
namespace App\Http\Controllers\V1\Installation;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Space\InstallUtils;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@@ -16,10 +15,6 @@ class FinishController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __invoke(Request $request)
|
public function __invoke(Request $request)
|
||||||
{
|
{
|
||||||
if (! InstallUtils::createDbMarker()) {
|
|
||||||
\Log::error('Install: Unable to create db marker.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(['success' => true]);
|
return response()->json(['success' => true]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class OnboardingWizardController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function getStep(Request $request)
|
public function getStep(Request $request)
|
||||||
{
|
{
|
||||||
if (! InstallUtils::dbMarkerExists()) {
|
if (! InstallUtils::isDbCreated()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'profile_complete' => 0,
|
'profile_complete' => 0,
|
||||||
'profile_language' => 'en',
|
'profile_language' => 'en',
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ class InstallationMiddleware
|
|||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
if (! InstallUtils::isDbCreated() || Setting::getSetting('profile_complete') !== 'COMPLETED') {
|
try {
|
||||||
|
if (! InstallUtils::isDbCreated() || Setting::getSetting('profile_complete') !== 'COMPLETED') {
|
||||||
|
return redirect('/installation');
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
return redirect('/installation');
|
return redirect('/installation');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,13 @@ class RedirectIfInstalled
|
|||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
if (InstallUtils::dbMarkerExists()) {
|
if (InstallUtils::isDbCreated()) {
|
||||||
if (Setting::getSetting('profile_complete') === 'COMPLETED') {
|
try {
|
||||||
return redirect('login');
|
if (Setting::getSetting('profile_complete') === 'COMPLETED') {
|
||||||
|
return redirect('login');
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Settings table may not exist yet during installation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\FileDisk;
|
use App\Space\BackupConfigurationFactory;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Spatie\Backup\Config\Config;
|
|
||||||
use Spatie\Backup\Tasks\Backup\BackupJobFactory;
|
use Spatie\Backup\Tasks\Backup\BackupJobFactory;
|
||||||
|
|
||||||
class CreateBackupJob implements ShouldQueue
|
class CreateBackupJob implements ShouldQueue
|
||||||
@@ -25,7 +24,7 @@ class CreateBackupJob implements ShouldQueue
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct($data = '')
|
public function __construct($data = [])
|
||||||
{
|
{
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
}
|
}
|
||||||
@@ -35,14 +34,7 @@ class CreateBackupJob implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$fileDisk = FileDisk::find($this->data['file_disk_id']);
|
$config = BackupConfigurationFactory::make($this->data);
|
||||||
$fileDisk->setConfig();
|
|
||||||
|
|
||||||
$prefix = env('DYNAMIC_DISK_PREFIX', 'temp_');
|
|
||||||
|
|
||||||
config(['backup.backup.destination.disks' => [$prefix.$fileDisk->driver]]);
|
|
||||||
|
|
||||||
$config = Config::fromArray(config('backup'));
|
|
||||||
$backupJob = BackupJobFactory::createFromConfig($config);
|
$backupJob = BackupJobFactory::createFromConfig($config);
|
||||||
if (! defined('SIGINT')) {
|
if (! defined('SIGINT')) {
|
||||||
$backupJob->disableSignals();
|
$backupJob->disableSignals();
|
||||||
|
|||||||
@@ -213,6 +213,13 @@ class User extends Authenticatable implements HasMedia
|
|||||||
return $query->where('email', 'LIKE', '%'.$email.'%');
|
return $query->where('email', 'LIKE', '%'.$email.'%');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeWhereCompany($query)
|
||||||
|
{
|
||||||
|
return $query->whereHas('companies', function ($q) {
|
||||||
|
$q->where('company_id', request()->header('company'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function scopePaginateData($query, $limit)
|
public function scopePaginateData($query, $limit)
|
||||||
{
|
{
|
||||||
if ($limit == 'all') {
|
if ($limit == 'all') {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class CustomerPolicy
|
|||||||
*/
|
*/
|
||||||
public function view(User $user, Customer $customer): bool
|
public function view(User $user, Customer $customer): bool
|
||||||
{
|
{
|
||||||
if (BouncerFacade::can('view-customer', $customer)) {
|
if (BouncerFacade::can('view-customer', $customer) && $user->hasCompany($customer->company_id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ class CustomerPolicy
|
|||||||
*/
|
*/
|
||||||
public function update(User $user, Customer $customer): bool
|
public function update(User $user, Customer $customer): bool
|
||||||
{
|
{
|
||||||
if (BouncerFacade::can('edit-customer', $customer)) {
|
if (BouncerFacade::can('edit-customer', $customer) && $user->hasCompany($customer->company_id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ class CustomerPolicy
|
|||||||
*/
|
*/
|
||||||
public function delete(User $user, Customer $customer): bool
|
public function delete(User $user, Customer $customer): bool
|
||||||
{
|
{
|
||||||
if (BouncerFacade::can('delete-customer', $customer)) {
|
if (BouncerFacade::can('delete-customer', $customer) && $user->hasCompany($customer->company_id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ class CustomerPolicy
|
|||||||
*/
|
*/
|
||||||
public function restore(User $user, Customer $customer): bool
|
public function restore(User $user, Customer $customer): bool
|
||||||
{
|
{
|
||||||
if (BouncerFacade::can('delete-customer', $customer)) {
|
if (BouncerFacade::can('delete-customer', $customer) && $user->hasCompany($customer->company_id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ class CustomerPolicy
|
|||||||
*/
|
*/
|
||||||
public function forceDelete(User $user, Customer $customer): bool
|
public function forceDelete(User $user, Customer $customer): bool
|
||||||
{
|
{
|
||||||
if (BouncerFacade::can('delete-customer', $customer)) {
|
if (BouncerFacade::can('delete-customer', $customer) && $user->hasCompany($customer->company_id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
40
app/Space/BackupConfigurationFactory.php
Normal file
40
app/Space/BackupConfigurationFactory.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Space;
|
||||||
|
|
||||||
|
use App\Models\CompanySetting;
|
||||||
|
use App\Models\FileDisk;
|
||||||
|
use Exception;
|
||||||
|
use Spatie\Backup\Config\Config;
|
||||||
|
|
||||||
|
class BackupConfigurationFactory
|
||||||
|
{
|
||||||
|
public static function make($data = []): Config
|
||||||
|
{
|
||||||
|
if (blank($data['company'] ?? null)) {
|
||||||
|
throw new Exception('The Company ID is missig');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blank($data['file_disk_id'] ?? null)) {
|
||||||
|
throw new Exception('No file disk selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileDisk = FileDisk::find($data['file_disk_id']);
|
||||||
|
|
||||||
|
$fileDisk->setConfig();
|
||||||
|
|
||||||
|
$prefix = env('DYNAMIC_DISK_PREFIX', 'temp_');
|
||||||
|
|
||||||
|
config(['backup.backup.destination.disks' => [$prefix.$fileDisk->driver]]);
|
||||||
|
|
||||||
|
$companyNotificationEmail = CompanySetting::getSetting('notification_email', $data['company']);
|
||||||
|
|
||||||
|
if ($companyNotificationEmail) {
|
||||||
|
config(['backup.notifications.mail.to' => $companyNotificationEmail]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = Config::fromArray(config('backup'));
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,6 @@ namespace App\Space;
|
|||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use League\Flysystem\FilesystemException;
|
|
||||||
|
|
||||||
class InstallUtils
|
class InstallUtils
|
||||||
{
|
{
|
||||||
@@ -17,7 +15,7 @@ class InstallUtils
|
|||||||
*/
|
*/
|
||||||
public static function isDbCreated()
|
public static function isDbCreated()
|
||||||
{
|
{
|
||||||
return self::dbMarkerExists() && self::tableExists('users');
|
return self::tableExists('users');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,54 +42,6 @@ class InstallUtils
|
|||||||
return $cache[$table];
|
return $cache[$table];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if database created marker exists
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function dbMarkerExists()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return \Storage::disk('local')->has('database_created');
|
|
||||||
} catch (FilesystemException $e) {
|
|
||||||
Log::error('Unable to verify db marker: '.$e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the database marker
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function createDbMarker()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return \Storage::disk('local')->put('database_created', time());
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('Unable to create db marker: '.$e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the database marker
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function deleteDbMarker()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return \Storage::disk('local')->delete('database_created');
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error('Unable to delete db marker: '.$e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the app version
|
* Set the app version
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -130,6 +130,80 @@ class Updater
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function cleanStaleFiles(): array
|
||||||
|
{
|
||||||
|
$manifestPath = base_path('manifest.json');
|
||||||
|
|
||||||
|
if (! File::exists($manifestPath)) {
|
||||||
|
return ['success' => true, 'cleaned' => 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifest = json_decode(File::get($manifestPath), true);
|
||||||
|
|
||||||
|
if (! is_array($manifest)) {
|
||||||
|
return ['success' => false, 'error' => 'Invalid manifest'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifestLookup = array_flip($manifest);
|
||||||
|
$protectedPaths = config('invoiceshelf.update_protected_paths', []);
|
||||||
|
$cleaned = 0;
|
||||||
|
|
||||||
|
$iterator = new \RecursiveIteratorIterator(
|
||||||
|
new \RecursiveDirectoryIterator(base_path(), \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||||
|
\RecursiveIteratorIterator::CHILD_FIRST
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
$relativePath = substr($file->getPathname(), strlen(base_path()) + 1);
|
||||||
|
|
||||||
|
if (static::isProtectedPath($relativePath, $protectedPaths)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file->isFile() && ! isset($manifestLookup[$relativePath])) {
|
||||||
|
File::delete($file->getPathname());
|
||||||
|
$cleaned++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: remove empty directories
|
||||||
|
$dirIterator = new \RecursiveIteratorIterator(
|
||||||
|
new \RecursiveDirectoryIterator(base_path(), \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||||
|
\RecursiveIteratorIterator::CHILD_FIRST
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($dirIterator as $item) {
|
||||||
|
if (! $item->isDir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relativePath = substr($item->getPathname(), strlen(base_path()) + 1);
|
||||||
|
|
||||||
|
if (static::isProtectedPath($relativePath, $protectedPaths)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries = scandir($item->getPathname());
|
||||||
|
|
||||||
|
if (count($entries) <= 2) {
|
||||||
|
@rmdir($item->getPathname());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['success' => true, 'cleaned' => $cleaned];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function isProtectedPath(string $relativePath, array $protectedPaths): bool
|
||||||
|
{
|
||||||
|
foreach ($protectedPaths as $protected) {
|
||||||
|
if ($relativePath === $protected || str_starts_with($relativePath, $protected.'/')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static function migrateUpdate()
|
public static function migrateUpdate()
|
||||||
{
|
{
|
||||||
Artisan::call('migrate --force');
|
Artisan::call('migrate --force');
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ final class PdfHtmlSanitizer
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Legacy/invalid `</br>` is dropped by libxml and collapses lines in PDF output; normalize to `<br />`.
|
||||||
|
$html = str_replace('</br>', '<br />', $html);
|
||||||
|
|
||||||
$allowedTags = '<br><br/><p><b><strong><i><em><u><ol><ul><li><table><tr><td><th><thead><tbody><tfoot><h1><h2><h3><h4><blockquote>';
|
$allowedTags = '<br><br/><p><b><strong><i><em><u><ol><ul><li><table><tr><td><th><thead><tbody><tfoot><h1><h2><h3><h4><blockquote>';
|
||||||
$html = strip_tags($html, $allowedTags);
|
$html = strip_tags($html, $allowedTags);
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ trait GeneratesPdfTrait
|
|||||||
|
|
||||||
$str = str_replace('<p>', '', $str);
|
$str = str_replace('<p>', '', $str);
|
||||||
|
|
||||||
$str = str_replace('</p>', '</br>', $str);
|
$str = str_replace('</p>', '<br />', $str);
|
||||||
|
|
||||||
return $str;
|
return $str;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,12 @@
|
|||||||
"type": "project",
|
"type": "project",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.4",
|
"php": "^8.4",
|
||||||
"aws/aws-sdk-php": "^3.336",
|
|
||||||
"barryvdh/laravel-dompdf": "^v3.0",
|
"barryvdh/laravel-dompdf": "^v3.0",
|
||||||
"doctrine/dbal": "^4.2",
|
|
||||||
"dragonmantank/cron-expression": "^v3.4",
|
"dragonmantank/cron-expression": "^v3.4",
|
||||||
"gotenberg/gotenberg-php": "^2.8",
|
"gotenberg/gotenberg-php": "^2.8",
|
||||||
"guzzlehttp/guzzle": "^7.9",
|
"guzzlehttp/guzzle": "^7.9",
|
||||||
"hashids/hashids": "^5.0",
|
"hashids/hashids": "^5.0",
|
||||||
"invoiceshelf/modules": "^1.0.0",
|
"invoiceshelf/modules": "^1.0.0",
|
||||||
"jasonmccreary/laravel-test-assertions": "^v2.4",
|
|
||||||
"laravel/framework": "^13.0",
|
"laravel/framework": "^13.0",
|
||||||
"laravel/helpers": "^1.7",
|
"laravel/helpers": "^1.7",
|
||||||
"laravel/sanctum": "^4.0",
|
"laravel/sanctum": "^4.0",
|
||||||
@@ -36,6 +33,7 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-ide-helper": "^3.5",
|
"barryvdh/laravel-ide-helper": "^3.5",
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
"jasonmccreary/laravel-test-assertions": "^2.9",
|
||||||
"laravel/boost": "^2.3",
|
"laravel/boost": "^2.3",
|
||||||
"laravel/pint": "^1.13",
|
"laravel/pint": "^1.13",
|
||||||
"laravel/sail": "^1.41",
|
"laravel/sail": "^1.41",
|
||||||
@@ -83,7 +81,11 @@
|
|||||||
"dev": [
|
"dev": [
|
||||||
"Composer\\Config::disableProcessTimeout",
|
"Composer\\Config::disableProcessTimeout",
|
||||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
||||||
]
|
],
|
||||||
|
"ide-helper": [
|
||||||
|
"@php artisan ide-helper:generate",
|
||||||
|
"@php artisan ide-helper:meta"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"laravel": {
|
"laravel": {
|
||||||
|
|||||||
3915
composer.lock
generated
3915
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,15 @@ return [
|
|||||||
'report' => false,
|
'report' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'public' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/public'),
|
||||||
|
'url' => env('APP_URL').'/storage',
|
||||||
|
'visibility' => 'public',
|
||||||
|
'throw' => false,
|
||||||
|
'report' => false,
|
||||||
|
],
|
||||||
|
|
||||||
's3' => [
|
's3' => [
|
||||||
'driver' => 's3',
|
'driver' => 's3',
|
||||||
'key' => env('AWS_KEY'),
|
'key' => env('AWS_KEY'),
|
||||||
|
|||||||
@@ -47,6 +47,25 @@ return [
|
|||||||
*/
|
*/
|
||||||
'base_url' => 'https://invoiceshelf.com',
|
'base_url' => 'https://invoiceshelf.com',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Paths protected from cleanup during updates.
|
||||||
|
* The updater will never delete files under these prefixes.
|
||||||
|
*/
|
||||||
|
'update_protected_paths' => [
|
||||||
|
'.env',
|
||||||
|
'storage',
|
||||||
|
'vendor',
|
||||||
|
'node_modules',
|
||||||
|
'Modules',
|
||||||
|
'public/storage',
|
||||||
|
'.git',
|
||||||
|
'bootstrap/cache',
|
||||||
|
'manifest.json',
|
||||||
|
'android',
|
||||||
|
'ios',
|
||||||
|
'mobile',
|
||||||
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List of languages supported by InvoiceShelf.
|
* List of languages supported by InvoiceShelf.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use App\Models\CompanySetting;
|
|||||||
use App\Models\Customer;
|
use App\Models\Customer;
|
||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Space\InstallUtils;
|
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
use Silber\Bouncer\BouncerFacade;
|
use Silber\Bouncer\BouncerFacade;
|
||||||
|
|
||||||
@@ -71,8 +70,5 @@ class DemoSeeder extends Seeder
|
|||||||
|
|
||||||
// Mark profile setup as complete
|
// Mark profile setup as complete
|
||||||
Setting::setSetting('profile_complete', 'COMPLETED');
|
Setting::setSetting('profile_complete', 'COMPLETED');
|
||||||
|
|
||||||
// Create installation marker
|
|
||||||
InstallUtils::createDbMarker();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
FROM --platform=$BUILDPLATFORM node AS static_builder
|
FROM --platform=$BUILDPLATFORM node:24 AS static_builder
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
COPY . /var/www/html
|
COPY . /var/www/html
|
||||||
RUN yarn && yarn build
|
RUN yarn && yarn build
|
||||||
|
|
||||||
FROM serversideup/php:8.4-fpm-alpine AS base
|
FROM serversideup/php:8.4-fpm-alpine AS base
|
||||||
USER root
|
USER root
|
||||||
RUN apk add --no-cache sqlite
|
RUN apk add --no-cache bash nano mariadb-client postgresql-client sqlite
|
||||||
RUN install-php-extensions exif
|
RUN install-php-extensions exif
|
||||||
RUN install-php-extensions pgsql
|
RUN install-php-extensions pgsql
|
||||||
RUN install-php-extensions sqlite3
|
RUN install-php-extensions sqlite3
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
FROM --platform=$BUILDPLATFORM node:20 AS static_builder
|
FROM --platform=$BUILDPLATFORM node:24 AS static_builder
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
COPY . /var/www/html
|
COPY . /var/www/html
|
||||||
RUN yarn && yarn build
|
RUN yarn && yarn build
|
||||||
|
|
||||||
FROM serversideup/php:8.4-fpm-nginx-alpine AS base
|
FROM serversideup/php:8.4-fpm-nginx-alpine AS base
|
||||||
USER root
|
USER root
|
||||||
RUN apk add --no-cache bash nano
|
RUN apk add --no-cache bash nano mariadb-client postgresql-client sqlite
|
||||||
RUN install-php-extensions exif
|
RUN install-php-extensions exif
|
||||||
RUN install-php-extensions pgsql
|
RUN install-php-extensions pgsql
|
||||||
RUN install-php-extensions sqlite3
|
RUN install-php-extensions sqlite3
|
||||||
@@ -19,6 +19,10 @@ FROM serversideup/php:8.4-fpm-nginx-alpine AS base
|
|||||||
RUN install-php-extensions intl
|
RUN install-php-extensions intl
|
||||||
RUN install-php-extensions curl
|
RUN install-php-extensions curl
|
||||||
|
|
||||||
|
# Copy entrypoint and inject script, and make sure they are executable
|
||||||
|
COPY --chmod=755 docker/production/inject.sh /inject.sh
|
||||||
|
COPY --chmod=755 docker/production/entrypoint.d/ /etc/entrypoint.d/
|
||||||
|
|
||||||
FROM base AS production
|
FROM base AS production
|
||||||
ENV AUTORUN_ENABLED=true
|
ENV AUTORUN_ENABLED=true
|
||||||
ENV PHP_OPCACHE_ENABLE=1
|
ENV PHP_OPCACHE_ENABLE=1
|
||||||
@@ -36,7 +40,3 @@ FROM base AS production
|
|||||||
COPY --from=static_builder --chown=www-data:www-data /var/www/html/public /var/www/html/public
|
COPY --from=static_builder --chown=www-data:www-data /var/www/html/public /var/www/html/public
|
||||||
COPY --chown=www-data:www-data . /var/www/html
|
COPY --chown=www-data:www-data . /var/www/html
|
||||||
RUN composer install --prefer-dist --no-dev --optimize-autoloader
|
RUN composer install --prefer-dist --no-dev --optimize-autoloader
|
||||||
|
|
||||||
# Copy entrypoint and inject script, and make sure they are executable
|
|
||||||
COPY --chmod=755 docker/production/inject.sh /inject.sh
|
|
||||||
COPY --chmod=755 docker/production/entrypoint.d/ /etc/entrypoint.d/
|
|
||||||
|
|||||||
@@ -33,8 +33,15 @@ if [ "$DB_CONNECTION" = "sqlite" ] || [ -z "$DB_CONNECTION" ]; then
|
|||||||
chown www-data:www-data "$DB_DATABASE"
|
chown www-data:www-data "$DB_DATABASE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "**** Setting up artisan permissions ****"
|
echo "**** Setting up folder permissions ****"
|
||||||
chmod +x artisan
|
chmod +x artisan
|
||||||
|
chown -R www-data:www-data storage bootstrap/cache
|
||||||
|
chmod -R 775 storage bootstrap/cache
|
||||||
|
|
||||||
|
if [ ! -L /var/www/html/public/storage ]; then
|
||||||
|
echo "**** Creating storage symlink (public/storage) ****"
|
||||||
|
./artisan storage:link --force -n || true
|
||||||
|
fi
|
||||||
|
|
||||||
if ! grep -q "APP_KEY" /var/www/html/.env
|
if ! grep -q "APP_KEY" /var/www/html/.env
|
||||||
then
|
then
|
||||||
|
|||||||
13
eslint.config.mjs
Normal file
13
eslint.config.mjs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import pluginVue from 'eslint-plugin-vue'
|
||||||
|
import eslintConfigPrettier from 'eslint-config-prettier'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...pluginVue.configs['flat/recommended'],
|
||||||
|
eslintConfigPrettier,
|
||||||
|
{
|
||||||
|
files: ['resources/scripts/**/*.{js,vue}'],
|
||||||
|
rules: {
|
||||||
|
'vue/no-mutating-props': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -652,6 +652,7 @@
|
|||||||
"currency": "Currency",
|
"currency": "Currency",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"category": "Category",
|
"category": "Category",
|
||||||
|
"uncategorized": "Uncategorized",
|
||||||
"from_date": "From Date",
|
"from_date": "From Date",
|
||||||
"to_date": "To Date",
|
"to_date": "To Date",
|
||||||
"expense_date": "Date",
|
"expense_date": "Date",
|
||||||
@@ -1322,6 +1323,7 @@
|
|||||||
"unzipping_package": "Unzipping Package",
|
"unzipping_package": "Unzipping Package",
|
||||||
"copying_files": "Copying Files",
|
"copying_files": "Copying Files",
|
||||||
"deleting_files": "Deleting Unused files",
|
"deleting_files": "Deleting Unused files",
|
||||||
|
"cleaning_stale_files": "Cleaning stale files",
|
||||||
"running_migrations": "Running Migrations",
|
"running_migrations": "Running Migrations",
|
||||||
"finishing_update": "Finishing Update",
|
"finishing_update": "Finishing Update",
|
||||||
"update_failed": "Update Failed",
|
"update_failed": "Update Failed",
|
||||||
@@ -1649,6 +1651,7 @@
|
|||||||
"pdf_total_tax_label": "TOTAL TAX",
|
"pdf_total_tax_label": "TOTAL TAX",
|
||||||
"pdf_tax_types_label": "Tax Types",
|
"pdf_tax_types_label": "Tax Types",
|
||||||
"pdf_expenses_label": "Expenses",
|
"pdf_expenses_label": "Expenses",
|
||||||
|
"pdf_expense_group_total_label": "Group total:",
|
||||||
"pdf_bill_to": "Bill to,",
|
"pdf_bill_to": "Bill to,",
|
||||||
"pdf_ship_to": "Ship to,",
|
"pdf_ship_to": "Ship to,",
|
||||||
"pdf_received_from": "Received from:",
|
"pdf_received_from": "Received from:",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"yes": "हां",
|
"yes": "हां",
|
||||||
"no": "नहीं",
|
"no": "नहीं",
|
||||||
"sort_by": "इसके अनुसार क्रमबद्ध करें",
|
"sort_by": "इसके अनुसार क्रमबद्ध करें",
|
||||||
"ascending": "आरोही",
|
"ascending": "बढ़ते क्रम में",
|
||||||
"descending": "उतरते",
|
"descending": "उतरते",
|
||||||
"subject": "विषय",
|
"subject": "विषय",
|
||||||
"body": "बॉडी",
|
"body": "बॉडी",
|
||||||
|
|||||||
55
package.json
55
package.json
@@ -1,64 +1,61 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=24"
|
||||||
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"watch": "vite build --watch",
|
"watch": "vite build --watch",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"serve": "vite preview",
|
"serve": "vite preview",
|
||||||
"test": "eslint ./resources/scripts --ext .js,.vue"
|
"test": "eslint ./resources/scripts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rvxlab/tailwind-plugin-ios-full-height": "^1.1.0",
|
|
||||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"@vue/compiler-sfc": "^3.5.13",
|
"@vue/compiler-sfc": "^3.5.13",
|
||||||
"autoprefixer": "^10.4.20",
|
"eslint": "^10.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"eslint-config-prettier": "^10.0.0",
|
||||||
"eslint": "^9.18.0",
|
"eslint-plugin-vue": "^10.0.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
|
||||||
"eslint-plugin-vue": "^9.32.0",
|
|
||||||
"postcss": "^8.4.49",
|
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"sass": "^1.83.1",
|
"tailwind-scrollbar": "^4.0.0",
|
||||||
"tailwind-scrollbar": "^3.1.0",
|
"tailwindcss": "^4.0.0"
|
||||||
"tailwindcss": "^3.4.17"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/vue": "^1.7.23",
|
"@headlessui/vue": "^1.7.23",
|
||||||
"@heroicons/vue": "^2.2.0",
|
"@heroicons/vue": "^2.2.0",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@stripe/stripe-js": "^2.4.0",
|
"@stripe/stripe-js": "^2.4.0",
|
||||||
"@tiptap/core": "^2.11.2",
|
"@tiptap/core": "^3.0.0",
|
||||||
"@tiptap/extension-link": "^2.11.2",
|
"@tiptap/extension-text-align": "^3.0.0",
|
||||||
"@tiptap/extension-text-align": "^2.11.2",
|
"@tiptap/pm": "^3.0.0",
|
||||||
"@tiptap/pm": "^2.11.2",
|
"@tiptap/starter-kit": "^3.0.0",
|
||||||
"@tiptap/starter-kit": "^2.11.2",
|
"@tiptap/vue-3": "^3.0.0",
|
||||||
"@tiptap/vue-3": "^2.11.2",
|
"@types/node": "^24.0.0",
|
||||||
"@types/node": "^20.11.9",
|
|
||||||
"@vuelidate/components": "^1.2.6",
|
"@vuelidate/components": "^1.2.6",
|
||||||
"@vuelidate/core": "^2.0.3",
|
"@vuelidate/core": "^2.0.3",
|
||||||
"@vuelidate/validators": "^2.0.4",
|
"@vuelidate/validators": "^2.0.4",
|
||||||
"@vueuse/core": "^12.4.0",
|
"@vueuse/core": "^14.0.0",
|
||||||
"axios": "0.30.0",
|
"axios": "1.14.0",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^4.5.1",
|
||||||
"guid": "^0.0.12",
|
"guid": "^0.0.12",
|
||||||
"laravel-vite-plugin": "^1.1.1",
|
"laravel-vite-plugin": "^3.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"maska": "^3.0.4",
|
"maska": "^3.0.4",
|
||||||
"mini-svg-data-uri": "^1.4.4",
|
"mini-svg-data-uri": "^1.4.4",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"pinia": "^2.3.0",
|
"pinia": "^3.0.0",
|
||||||
"v-money3": "^3.24.1",
|
"v-money3": "^3.24.1",
|
||||||
"v-tooltip": "^4.0.0-beta.17",
|
"v-tooltip": "^4.0.0-beta.17",
|
||||||
"vite": "^6.0.7",
|
"vite": "^8.0.0",
|
||||||
"vue": "^3.5",
|
"vue": "^3.5",
|
||||||
"vue-flatpickr-component": "^11.0.5",
|
"vue-flatpickr-component": "^12.0.0",
|
||||||
"vue-i18n": "^11.0.1",
|
"vue-i18n": "^11.0.1",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^5.0.0",
|
||||||
"vuedraggable": "^4.1.0"
|
"vuedraggable": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
// postcss.config.js
|
|
||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
56
resources/css/components/pace-loader.css
vendored
Normal file
56
resources/css/components/pace-loader.css
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
.pace {
|
||||||
|
-webkit-pointer-events: none;
|
||||||
|
pointer-events: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pace-inactive {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pace .pace-progress {
|
||||||
|
background: #3f39ad;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2000;
|
||||||
|
top: 0;
|
||||||
|
right: 100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pace .pace-progress-inner {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
box-shadow: 0 0 10px #5851d8, 0 0 5px #5851d8;
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotate(3deg) translate(0px, -4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pace .pace-activity {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2000;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border: solid 2px transparent;
|
||||||
|
border-top-color: #5851d8;
|
||||||
|
border-left-color: #5851d8;
|
||||||
|
border-radius: 10px;
|
||||||
|
animation: pace-spinner 400ms linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pace-spinner {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,18 +80,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.popover {
|
&.popover {
|
||||||
$color: #f9f9f9;
|
|
||||||
|
|
||||||
.popover-inner {
|
.popover-inner {
|
||||||
background: $color;
|
background: #f9f9f9;
|
||||||
color: black;
|
color: black;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
box-shadow: 0 5px 30px rgba(black, .1);
|
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover-arrow {
|
.popover-arrow {
|
||||||
border-color: $color;
|
border-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
98
resources/css/invoiceshelf.css
vendored
Normal file
98
resources/css/invoiceshelf.css
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@import "./themes.css";
|
||||||
|
@import "./components/animation.css";
|
||||||
|
@import "./components/pace-loader.css";
|
||||||
|
@import "./components/v-tooltips.css";
|
||||||
|
|
||||||
|
@plugin "@tailwindcss/forms";
|
||||||
|
@plugin "@tailwindcss/typography";
|
||||||
|
@plugin "tailwind-scrollbar";
|
||||||
|
|
||||||
|
@source "../scripts/**/*.vue";
|
||||||
|
@source "../scripts/**/*.js";
|
||||||
|
@source "../../resources/views/**/*.php";
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--color-primary-50: var(--color-primary-50);
|
||||||
|
--color-primary-100: var(--color-primary-100);
|
||||||
|
--color-primary-200: var(--color-primary-200);
|
||||||
|
--color-primary-300: var(--color-primary-300);
|
||||||
|
--color-primary-400: var(--color-primary-400);
|
||||||
|
--color-primary-500: var(--color-primary-500);
|
||||||
|
--color-primary-600: var(--color-primary-600);
|
||||||
|
--color-primary-700: var(--color-primary-700);
|
||||||
|
--color-primary-800: var(--color-primary-800);
|
||||||
|
--color-primary-900: var(--color-primary-900);
|
||||||
|
--color-primary-950: var(--color-primary-950);
|
||||||
|
--color-black: #040405;
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--spacing-88: 22rem;
|
||||||
|
--font-base: Poppins, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility bg-multiselect-caret {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z' clip-rule='evenodd'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility bg-multiselect-spinner {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' fill='%234a3dff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M456.433 371.72l-27.79-16.045c-7.192-4.152-10.052-13.136-6.487-20.636 25.82-54.328 23.566-118.602-6.768-171.03-30.265-52.529-84.802-86.621-144.76-91.424C262.35 71.922 256 64.953 256 56.649V24.56c0-9.31 7.916-16.609 17.204-15.96 81.795 5.717 156.412 51.902 197.611 123.408 41.301 71.385 43.99 159.096 8.042 232.792-4.082 8.369-14.361 11.575-22.424 6.92z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility bg-multiselect-remove {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 320 512' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Poppins";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("$fonts/Poppins-Black.ttf") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Poppins";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("$fonts/Poppins-Light.ttf") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Poppins";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("$fonts/Poppins-Medium.ttf") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Poppins";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("$fonts/Poppins-Regular.ttf") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Poppins";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url("$fonts/Poppins-SemiBold.ttf") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
min-height: -webkit-fill-available;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height: -webkit-fill-available;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-screen-ios {
|
||||||
|
height: -webkit-fill-available;
|
||||||
|
}
|
||||||
13
resources/css/themes.css
vendored
Normal file
13
resources/css/themes.css
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
:root {
|
||||||
|
--color-primary-50: rgb(241, 243, 255);
|
||||||
|
--color-primary-100: rgb(229, 231, 255);
|
||||||
|
--color-primary-200: rgb(206, 209, 255);
|
||||||
|
--color-primary-300: rgb(167, 170, 255);
|
||||||
|
--color-primary-400: rgb(118, 117, 255);
|
||||||
|
--color-primary-500: rgb(74, 61, 255);
|
||||||
|
--color-primary-600: rgb(51, 22, 255);
|
||||||
|
--color-primary-700: rgb(33, 4, 253);
|
||||||
|
--color-primary-800: rgb(27, 3, 211);
|
||||||
|
--color-primary-900: rgb(25, 5, 173);
|
||||||
|
--color-primary-950: rgb(9, 0, 118);
|
||||||
|
}
|
||||||
112
resources/sass/components/pace-loader.scss
vendored
112
resources/sass/components/pace-loader.scss
vendored
@@ -1,112 +0,0 @@
|
|||||||
$pace-loader-color: #5851d8;
|
|
||||||
|
|
||||||
.pace {
|
|
||||||
-webkit-pointer-events: none;
|
|
||||||
pointer-events: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pace-inactive {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pace .pace-progress {
|
|
||||||
background: darken($pace-loader-color, 10%);
|
|
||||||
position: fixed;
|
|
||||||
z-index: 2000;
|
|
||||||
top: 0;
|
|
||||||
right: 100%;
|
|
||||||
width: 100%;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pace .pace-progress-inner {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
width: 100px;
|
|
||||||
height: 100%;
|
|
||||||
box-shadow: 0 0 10px $pace-loader-color, 0 0 5px $pace-loader-color;
|
|
||||||
opacity: 1;
|
|
||||||
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
-moz-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
-ms-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
-o-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pace .pace-activity {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 2000;
|
|
||||||
top: 15px;
|
|
||||||
right: 15px;
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
border: solid 2px transparent;
|
|
||||||
border-top-color: $pace-loader-color;
|
|
||||||
border-left-color: $pace-loader-color;
|
|
||||||
border-radius: 10px;
|
|
||||||
-webkit-animation: pace-spinner 400ms linear infinite;
|
|
||||||
-moz-animation: pace-spinner 400ms linear infinite;
|
|
||||||
-ms-animation: pace-spinner 400ms linear infinite;
|
|
||||||
-o-animation: pace-spinner 400ms linear infinite;
|
|
||||||
animation: pace-spinner 400ms linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes pace-spinner {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes pace-spinner {
|
|
||||||
0% {
|
|
||||||
-moz-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-moz-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-o-keyframes pace-spinner {
|
|
||||||
0% {
|
|
||||||
-o-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-o-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-ms-keyframes pace-spinner {
|
|
||||||
0% {
|
|
||||||
-ms-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-ms-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pace-spinner {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
61
resources/sass/invoiceshelf.scss
vendored
61
resources/sass/invoiceshelf.scss
vendored
@@ -1,61 +0,0 @@
|
|||||||
@use "themes.scss";
|
|
||||||
@use "components/animation.scss";
|
|
||||||
|
|
||||||
@tailwind base;
|
|
||||||
|
|
||||||
@tailwind components;
|
|
||||||
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Poppins";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: url("$fonts/Poppins-Black.ttf") format("truetype");
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Poppins";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: url("$fonts/Poppins-Light.ttf") format("truetype");
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Poppins";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
font-display: swap;
|
|
||||||
src: url("$fonts/Poppins-Medium.ttf") format("truetype");
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Poppins";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: url("$fonts/Poppins-Regular.ttf") format("truetype");
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Poppins";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: url("$fonts/Poppins-SemiBold.ttf") format("truetype");
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
min-height: 100vh;
|
|
||||||
min-height: -webkit-fill-available;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
height: -webkit-fill-available;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-screen-ios {
|
|
||||||
height: -webkit-fill-available;
|
|
||||||
}
|
|
||||||
13
resources/sass/themes.scss
vendored
13
resources/sass/themes.scss
vendored
@@ -1,13 +0,0 @@
|
|||||||
:root {
|
|
||||||
--color-primary-50: 241, 243, 255;
|
|
||||||
--color-primary-100: 229, 231, 255;
|
|
||||||
--color-primary-200: 206, 209, 255;
|
|
||||||
--color-primary-300: 167, 170, 255;
|
|
||||||
--color-primary-400: 118, 117, 255;
|
|
||||||
--color-primary-500: 74, 61, 255;
|
|
||||||
--color-primary-600: 51, 22, 255;
|
|
||||||
--color-primary-700: 33, 4, 253;
|
|
||||||
--color-primary-800: 27, 3, 211;
|
|
||||||
--color-primary-900: 25, 5, 173;
|
|
||||||
--color-primary-950: 9, 0, 118;
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
px-4
|
px-4
|
||||||
py-2
|
py-2
|
||||||
rounded-lg
|
rounded-lg
|
||||||
bg-opacity-40 bg-gray-300
|
bg-gray-300/40
|
||||||
whitespace-nowrap
|
whitespace-nowrap
|
||||||
flex-col
|
flex-col
|
||||||
mt-1
|
mt-1
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
h-full
|
h-full
|
||||||
inset-y-0
|
inset-y-0
|
||||||
cursor-pointer
|
cursor-pointer
|
||||||
focus:outline-none
|
focus:outline-hidden
|
||||||
text-primary-500
|
text-primary-500
|
||||||
"
|
"
|
||||||
width="37"
|
width="37"
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
<Popover v-slot="{ isOpen }">
|
<Popover v-slot="{ isOpen }">
|
||||||
<PopoverButton
|
<PopoverButton
|
||||||
v-if="userStore.hasAbilities(abilities.VIEW_NOTE)"
|
v-if="userStore.hasAbilities(abilities.VIEW_NOTE)"
|
||||||
:class="isOpen ? '' : 'text-opacity-90'"
|
:class="isOpen ? '' : ''"
|
||||||
class="
|
class="
|
||||||
flex
|
flex
|
||||||
items-center
|
items-center
|
||||||
z-10
|
z-10
|
||||||
font-medium
|
font-medium
|
||||||
text-primary-400
|
text-primary-400
|
||||||
focus:outline-none focus:border-none
|
focus:outline-hidden focus:border-none
|
||||||
"
|
"
|
||||||
@click="fetchInitialData"
|
@click="fetchInitialData"
|
||||||
>
|
>
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
overflow-hidden
|
overflow-hidden
|
||||||
rounded-md
|
rounded-md
|
||||||
shadow-lg
|
shadow-lg
|
||||||
ring-1 ring-black ring-opacity-5
|
ring-1 ring-black/5
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="relative grid bg-white">
|
<div class="relative grid bg-white">
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
py-3
|
py-3
|
||||||
bg-gray-200
|
bg-gray-200
|
||||||
border-none
|
border-none
|
||||||
outline-none
|
outline-hidden
|
||||||
"
|
"
|
||||||
@click="openNoteModal"
|
@click="openNoteModal"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Chart from 'chart.js'
|
import { Chart } from 'chart.js/auto'
|
||||||
import { ref, reactive, computed, onMounted, watchEffect, inject } from 'vue'
|
import { ref, reactive, computed, onMounted, watchEffect, inject } from 'vue'
|
||||||
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
||||||
|
|
||||||
@@ -65,19 +65,21 @@ onMounted(() => {
|
|||||||
let options = reactive({
|
let options = reactive({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
tooltips: {
|
plugins: {
|
||||||
enabled: true,
|
tooltip: {
|
||||||
callbacks: {
|
enabled: true,
|
||||||
label: function (tooltipItem, data) {
|
callbacks: {
|
||||||
return utils.formatMoney(
|
label: function (context) {
|
||||||
Math.round(tooltipItem.value * 100),
|
return utils.formatMoney(
|
||||||
defaultCurrency.value
|
Math.round(context.parsed.y * 100),
|
||||||
)
|
defaultCurrency.value
|
||||||
|
)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
legend: {
|
||||||
legend: {
|
display: false,
|
||||||
display: false,
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -87,7 +89,7 @@ onMounted(() => {
|
|||||||
{
|
{
|
||||||
label: 'Sales',
|
label: 'Sales',
|
||||||
fill: false,
|
fill: false,
|
||||||
lineTension: 0.3,
|
tension: 0.3,
|
||||||
backgroundColor: 'rgba(230, 254, 249)',
|
backgroundColor: 'rgba(230, 254, 249)',
|
||||||
borderColor: '#040405',
|
borderColor: '#040405',
|
||||||
borderCapStyle: 'butt',
|
borderCapStyle: 'butt',
|
||||||
@@ -108,7 +110,7 @@ onMounted(() => {
|
|||||||
{
|
{
|
||||||
label: 'Receipts',
|
label: 'Receipts',
|
||||||
fill: false,
|
fill: false,
|
||||||
lineTension: 0.3,
|
tension: 0.3,
|
||||||
backgroundColor: 'rgba(230, 254, 249)',
|
backgroundColor: 'rgba(230, 254, 249)',
|
||||||
borderColor: 'rgb(2, 201, 156)',
|
borderColor: 'rgb(2, 201, 156)',
|
||||||
borderCapStyle: 'butt',
|
borderCapStyle: 'butt',
|
||||||
@@ -129,7 +131,7 @@ onMounted(() => {
|
|||||||
{
|
{
|
||||||
label: 'Expenses',
|
label: 'Expenses',
|
||||||
fill: false,
|
fill: false,
|
||||||
lineTension: 0.3,
|
tension: 0.3,
|
||||||
backgroundColor: 'rgba(245, 235, 242)',
|
backgroundColor: 'rgba(245, 235, 242)',
|
||||||
borderColor: 'rgb(255,0,0)',
|
borderColor: 'rgb(255,0,0)',
|
||||||
borderCapStyle: 'butt',
|
borderCapStyle: 'butt',
|
||||||
@@ -150,7 +152,7 @@ onMounted(() => {
|
|||||||
{
|
{
|
||||||
label: 'Net Income',
|
label: 'Net Income',
|
||||||
fill: false,
|
fill: false,
|
||||||
lineTension: 0.3,
|
tension: 0.3,
|
||||||
backgroundColor: 'rgba(236, 235, 249)',
|
backgroundColor: 'rgba(236, 235, 249)',
|
||||||
borderColor: 'rgba(88, 81, 216, 1)',
|
borderColor: 'rgba(88, 81, 216, 1)',
|
||||||
borderCapStyle: 'butt',
|
borderCapStyle: 'butt',
|
||||||
@@ -190,8 +192,6 @@ function update() {
|
|||||||
(expense) => expense / 100
|
(expense) => expense / 100
|
||||||
)
|
)
|
||||||
myLineChart.data.datasets[3].data = props.income.map((_i) => _i / 100)
|
myLineChart.data.datasets[3].data = props.income.map((_i) => _i / 100)
|
||||||
myLineChart.update({
|
myLineChart.update('none')
|
||||||
lazy: true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form action="" @submit.prevent="submitBulkUpdate">
|
<form id="bulk-update-form" action="" @submit.prevent="submitBulkUpdate">
|
||||||
<ValidateEach
|
<ValidateEach
|
||||||
v-for="(c, i) in exchangeRateStore.bulkCurrencies"
|
v-for="(c, i) in exchangeRateStore.bulkCurrencies"
|
||||||
:key="i"
|
:key="i"
|
||||||
@@ -50,22 +50,21 @@
|
|||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
</template>
|
</template>
|
||||||
</ValidateEach>
|
</ValidateEach>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
<div
|
<div
|
||||||
slot="footer"
|
|
||||||
class="
|
class="
|
||||||
z-0
|
z-0
|
||||||
flex
|
flex
|
||||||
justify-end
|
justify-end
|
||||||
mt-4
|
|
||||||
pt-4
|
|
||||||
border-t border-gray-200 border-solid border-modal-bg
|
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseButton :loading="isSaving" variant="primary" type="submit">
|
<BaseButton :loading="isSaving" variant="primary" type="submit" form="bulk-update-form">
|
||||||
{{ $t('general.save') }}
|
{{ $t('general.save') }}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</template>
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
<BaseContentPlaceholders v-if="loading">
|
<BaseContentPlaceholders v-if="loading">
|
||||||
<BaseContentPlaceholdersText
|
<BaseContentPlaceholdersText
|
||||||
:lines="1"
|
:lines="1"
|
||||||
class="w-24 h-8 border rounded-md"
|
class="w-24 h-8 border border-gray-200 rounded-md"
|
||||||
/>
|
/>
|
||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<template v-if="userStore.hasAbilities(ability)" #action>
|
<template v-if="userStore.hasAbilities(ability)" #action>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex items-center justify-center w-full px-2 py-2 bg-gray-200 border-none outline-none cursor-pointer "
|
class="flex items-center justify-center w-full px-2 py-2 bg-gray-200 border-none outline-hidden cursor-pointer "
|
||||||
@click="openTaxModal"
|
@click="openTaxModal"
|
||||||
>
|
>
|
||||||
<BaseIcon name="CheckCircleIcon" class="h-5 text-primary-400" />
|
<BaseIcon name="CheckCircleIcon" class="h-5 text-primary-400" />
|
||||||
@@ -106,7 +106,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
taxes: {
|
taxes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
total: {
|
total: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@
|
|||||||
<BaseContentPlaceholders v-if="isLoading">
|
<BaseContentPlaceholders v-if="isLoading">
|
||||||
<BaseContentPlaceholdersText
|
<BaseContentPlaceholdersText
|
||||||
:lines="1"
|
:lines="1"
|
||||||
class="w-24 h-8 border rounded-md"
|
class="w-24 h-8 border border-gray-200 rounded-md"
|
||||||
/>
|
/>
|
||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
<div v-else class="flex" style="width: 140px" role="group">
|
<div v-else class="flex" style="width: 140px" role="group">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<BaseIcon
|
<BaseIcon
|
||||||
v-tooltip="{ content: 'Fetch Latest Exchange rate' }"
|
v-tooltip="{ content: 'Fetch Latest Exchange rate' }"
|
||||||
name="ArrowPathIcon"
|
name="ArrowPathIcon"
|
||||||
:class="`h-4 w-4 text-primary-500 cursor-pointer outline-none ${
|
:class="`h-4 w-4 text-primary-500 cursor-pointer outline-hidden ${
|
||||||
isFetching
|
isFetching
|
||||||
? ' animate-spin rotate-180 cursor-not-allowed pointer-events-none '
|
? ' animate-spin rotate-180 cursor-not-allowed pointer-events-none '
|
||||||
: ''
|
: ''
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ const addressData = computed(() => {
|
|||||||
address: pick(address, ['address_street_1', 'city', 'state', 'zip']),
|
address: pick(address, ['address_street_1', 'city', 'state', 'zip']),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
const isAddressAvailable = computed(() => {
|
const isAddressAvailable = computed(() => {
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
<div class="w-full mt-4 tax-select">
|
<div class="w-full mt-4 tax-select">
|
||||||
<Popover v-slot="{ isOpen }" class="relative">
|
<Popover v-slot="{ isOpen }" class="relative">
|
||||||
<PopoverButton
|
<PopoverButton
|
||||||
:class="isOpen ? '' : 'text-opacity-90'"
|
:class="isOpen ? '' : ''"
|
||||||
class="
|
class="
|
||||||
flex
|
flex
|
||||||
items-center
|
items-center
|
||||||
text-sm
|
text-sm
|
||||||
font-medium
|
font-medium
|
||||||
text-primary-400
|
text-primary-400
|
||||||
focus:outline-none focus:border-none
|
focus:outline-hidden focus:border-none
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
overflow-hidden
|
overflow-hidden
|
||||||
rounded-md
|
rounded-md
|
||||||
shadow-lg
|
shadow-lg
|
||||||
ring-1 ring-black ring-opacity-5
|
ring-1 ring-black/5
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<!-- Tax Search Input -->
|
<!-- Tax Search Input -->
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
py-3
|
py-3
|
||||||
bg-gray-200
|
bg-gray-200
|
||||||
border-none
|
border-none
|
||||||
outline-none
|
outline-hidden
|
||||||
"
|
"
|
||||||
@click="openTaxTypeModal"
|
@click="openTaxTypeModal"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
flex
|
flex
|
||||||
justify-end
|
justify-end
|
||||||
p-4
|
p-4
|
||||||
border-t border-gray-200 border-solid border-modal-bg
|
border-t border-gray-200 border-solid
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
</BaseInputGrid>
|
</BaseInputGrid>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="z-0 flex justify-end p-4 bg-gray-50 border-modal-bg">
|
<div class="z-0 flex justify-end p-4 bg-gray-50 border-t border-gray-200">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
class="mr-3 text-sm"
|
class="mr-3 text-sm"
|
||||||
variant="primary-outline"
|
variant="primary-outline"
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="z-0 flex justify-end p-4 bg-gray-50 border-modal-bg">
|
<div class="z-0 flex justify-end p-4 bg-gray-50 border-t border-gray-200">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
class="mr-3 text-sm"
|
class="mr-3 text-sm"
|
||||||
variant="primary-outline"
|
variant="primary-outline"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
>
|
>
|
||||||
<template #default="slotProps">
|
<template #default="slotProps">
|
||||||
<div
|
<div
|
||||||
class="z-0 flex justify-end p-4 border-t border-solid border-gray-light"
|
class="z-0 flex justify-end p-4 border-t border-solid border-gray-200"
|
||||||
>
|
>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
class="mr-3 text-sm"
|
class="mr-3 text-sm"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
flex
|
flex
|
||||||
justify-end
|
justify-end
|
||||||
p-4
|
p-4
|
||||||
border-t border-gray-200 border-solid border-modal-bg
|
border-t border-gray-200 border-solid
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ _ />
|
|||||||
justify-end
|
justify-end
|
||||||
px-4
|
px-4
|
||||||
py-4
|
py-4
|
||||||
border-t border-solid border-gray-light
|
border-t border-solid border-gray-200
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
@@ -280,7 +280,7 @@ function closeNoteModal() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style>
|
||||||
.note-modal {
|
.note-modal {
|
||||||
.header-editior .editor-menu-bar {
|
.header-editior .editor-menu-bar {
|
||||||
margin-left: 0.5px;
|
margin-left: 0.5px;
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
flex
|
flex
|
||||||
justify-end
|
justify-end
|
||||||
p-4
|
p-4
|
||||||
border-t border-solid border--200 border-modal-bg
|
border-t border-solid border-gray-200
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
flex
|
flex
|
||||||
justify-end
|
justify-end
|
||||||
p-4
|
p-4
|
||||||
border-t border-solid border--200 border-modal-bg
|
border-t border-solid border-gray-200
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
|||||||
@@ -116,7 +116,6 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
import axios from 'axios'
|
|
||||||
import { useModalStore } from '@/scripts/stores/modal'
|
import { useModalStore } from '@/scripts/stores/modal'
|
||||||
import { useTaxTypeStore } from '@/scripts/admin/stores/tax-type'
|
import { useTaxTypeStore } from '@/scripts/admin/stores/tax-type'
|
||||||
import { useGlobalStore } from '@/scripts/admin/stores/global'
|
import { useGlobalStore } from '@/scripts/admin/stores/global'
|
||||||
|
|||||||
@@ -179,7 +179,7 @@
|
|||||||
flex
|
flex
|
||||||
justify-end
|
justify-end
|
||||||
p-4
|
p-4
|
||||||
border-t border-solid border-gray-light border-modal-bg
|
border-t border-solid border-gray-200
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
|||||||
@@ -168,5 +168,5 @@ const loginPageLogo = computed(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style scoped>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
px-4
|
px-4
|
||||||
py-3
|
py-3
|
||||||
md:h-16 md:px-8
|
md:h-16 md:px-8
|
||||||
bg-gradient-to-r
|
bg-linear-to-r
|
||||||
from-primary-500
|
from-primary-500
|
||||||
to-primary-400
|
to-primary-400
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
leave-from="opacity-100"
|
leave-from="opacity-100"
|
||||||
leave-to="opacity-0"
|
leave-to="opacity-0"
|
||||||
>
|
>
|
||||||
<DialogOverlay class="fixed inset-0 bg-gray-600 bg-opacity-75" />
|
<DialogOverlay class="fixed inset-0 bg-gray-600/75" />
|
||||||
</TransitionChild>
|
</TransitionChild>
|
||||||
|
|
||||||
<TransitionChild
|
<TransitionChild
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
h-10
|
h-10
|
||||||
ml-1
|
ml-1
|
||||||
rounded-full
|
rounded-full
|
||||||
focus:outline-none
|
focus:outline-hidden
|
||||||
focus:ring-2
|
focus:ring-2
|
||||||
focus:ring-inset
|
focus:ring-inset
|
||||||
focus:ring-white
|
focus:ring-white
|
||||||
@@ -82,9 +82,9 @@
|
|||||||
:to="item.link"
|
:to="item.link"
|
||||||
:class="[
|
:class="[
|
||||||
hasActiveUrl(item.link)
|
hasActiveUrl(item.link)
|
||||||
? 'text-primary-500 border-primary-500 bg-gray-100 '
|
? 'text-primary-500 border-l-primary-500 bg-gray-100 '
|
||||||
: 'text-black',
|
: 'text-black border-l-transparent',
|
||||||
'cursor-pointer px-0 pl-4 py-3 border-transparent flex items-center border-l-4 border-solid text-sm not-italic font-medium',
|
'cursor-pointer px-0 pl-4 py-3 flex items-center border-l-4 border-solid text-sm not-italic font-medium',
|
||||||
]"
|
]"
|
||||||
@click="globalStore.setSidebarVisibility(false)"
|
@click="globalStore.setSidebarVisibility(false)"
|
||||||
>
|
>
|
||||||
@@ -136,8 +136,8 @@
|
|||||||
:to="item.link"
|
:to="item.link"
|
||||||
:class="[
|
:class="[
|
||||||
hasActiveUrl(item.link)
|
hasActiveUrl(item.link)
|
||||||
? 'text-primary-500 border-primary-500 bg-gray-100 '
|
? 'text-primary-500 border-l-primary-500 bg-gray-100 '
|
||||||
: 'text-black',
|
: 'text-black border-l-transparent',
|
||||||
'cursor-pointer px-0 pl-6 hover:bg-gray-50 py-3 group flex items-center border-l-4 border-solid text-sm not-italic font-medium',
|
'cursor-pointer px-0 pl-6 hover:bg-gray-50 py-3 group flex items-center border-l-4 border-solid text-sm not-italic font-medium',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
|
|||||||
26
resources/scripts/admin/stores/auth.js
vendored
26
resources/scripts/admin/stores/auth.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -7,8 +7,7 @@ export const useAuthStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('auth', {
|
||||||
id: 'auth',
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
status: '',
|
status: '',
|
||||||
|
|
||||||
@@ -22,9 +21,9 @@ export const useAuthStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
login(data) {
|
login(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.get('/sanctum/csrf-cookie').then((response) => {
|
http.get('/sanctum/csrf-cookie').then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
axios
|
http
|
||||||
.post('/login', data)
|
.post('/login', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -45,22 +44,31 @@ export const useAuthStore = (useWindow = false) => {
|
|||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/auth/logout')
|
.post('/auth/logout')
|
||||||
.then((response) => {
|
.then(async (response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
notificationStore.showNotification({
|
notificationStore.showNotification({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: 'Logged out successfully.',
|
message: 'Logged out successfully.',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Clear stored auth data so next login doesn't send stale tokens
|
||||||
|
window.Ls.remove('auth.token')
|
||||||
|
window.Ls.remove('selectedCompany')
|
||||||
|
|
||||||
|
// Refresh CSRF token so next login works cleanly
|
||||||
|
await http.get('/sanctum/csrf-cookie').catch(() => {})
|
||||||
|
|
||||||
window.router.push('/login')
|
window.router.push('/login')
|
||||||
// resetStore.clearPinia()
|
|
||||||
resolve(response)
|
resolve(response)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
handleError(err)
|
handleError(err)
|
||||||
window.router.push('/')
|
window.Ls.remove('auth.token')
|
||||||
|
window.Ls.remove('selectedCompany')
|
||||||
|
http.get('/sanctum/csrf-cookie').catch(() => {})
|
||||||
|
window.router.push('/login')
|
||||||
reject(err)
|
reject(err)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
12
resources/scripts/admin/stores/backup.js
vendored
12
resources/scripts/admin/stores/backup.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -7,9 +7,7 @@ export const useBackupStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('backup', {
|
||||||
id: 'backup',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
backups: [],
|
backups: [],
|
||||||
currentBackupData: {
|
currentBackupData: {
|
||||||
@@ -21,7 +19,7 @@ export const useBackupStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
fetchBackups(params) {
|
fetchBackups(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/backups`, { params })
|
.get(`/api/v1/backups`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.backups = response.data.data
|
this.backups = response.data.data
|
||||||
@@ -36,7 +34,7 @@ export const useBackupStore = (useWindow = false) => {
|
|||||||
|
|
||||||
createBackup(data) {
|
createBackup(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/backups`, data)
|
.post(`/api/v1/backups`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
@@ -55,7 +53,7 @@ export const useBackupStore = (useWindow = false) => {
|
|||||||
|
|
||||||
removeBackup(params) {
|
removeBackup(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.delete(`/api/v1/backups/${params.disk}`, { params })
|
.delete(`/api/v1/backups/${params.disk}`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|||||||
16
resources/scripts/admin/stores/category.js
vendored
16
resources/scripts/admin/stores/category.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -7,9 +7,7 @@ export const useCategoryStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('category', {
|
||||||
id: 'category',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
categories: [],
|
categories: [],
|
||||||
currentCategory: {
|
currentCategory: {
|
||||||
@@ -27,7 +25,7 @@ export const useCategoryStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
fetchCategories(params) {
|
fetchCategories(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/categories`, { params })
|
.get(`/api/v1/categories`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.categories = response.data.data
|
this.categories = response.data.data
|
||||||
@@ -42,7 +40,7 @@ export const useCategoryStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchCategory(id) {
|
fetchCategory(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/categories/${id}`)
|
.get(`/api/v1/categories/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.currentCategory = response.data.data
|
this.currentCategory = response.data.data
|
||||||
@@ -57,7 +55,7 @@ export const useCategoryStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addCategory(data) {
|
addCategory(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/categories', data)
|
.post('/api/v1/categories', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.categories.push(response.data.data)
|
this.categories.push(response.data.data)
|
||||||
@@ -77,7 +75,7 @@ export const useCategoryStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateCategory(data) {
|
updateCategory(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/categories/${data.id}`, data)
|
.put(`/api/v1/categories/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
@@ -104,7 +102,7 @@ export const useCategoryStore = (useWindow = false) => {
|
|||||||
|
|
||||||
deleteCategory(id) {
|
deleteCategory(id) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
axios
|
http
|
||||||
.delete(`/api/v1/categories/${id}`)
|
.delete(`/api/v1/categories/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.categories.findIndex(
|
let index = this.categories.findIndex(
|
||||||
|
|||||||
24
resources/scripts/admin/stores/company.js
vendored
24
resources/scripts/admin/stores/company.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -8,9 +8,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('company', {
|
||||||
id: 'company',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
companies: [],
|
companies: [],
|
||||||
selectedCompany: null,
|
selectedCompany: null,
|
||||||
@@ -26,7 +24,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchBasicMailConfig() {
|
fetchBasicMailConfig() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/company/mail/config')
|
.get('/api/v1/company/mail/config')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -40,7 +38,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateCompany(data) {
|
updateCompany(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put('/api/v1/company', data)
|
.put('/api/v1/company', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
@@ -66,7 +64,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateCompanyLogo(data) {
|
updateCompanyLogo(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/company/upload-logo', data)
|
.post('/api/v1/company/upload-logo', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -80,7 +78,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addNewCompany(data) {
|
addNewCompany(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/companies', data)
|
.post('/api/v1/companies', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
@@ -99,7 +97,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchCompany(params) {
|
fetchCompany(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/current-company', params)
|
.get('/api/v1/current-company', params)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
Object.assign(this.companyForm, response.data.data.address)
|
Object.assign(this.companyForm, response.data.data.address)
|
||||||
@@ -115,7 +113,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchUserCompanies() {
|
fetchUserCompanies() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/companies')
|
.get('/api/v1/companies')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -129,7 +127,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchCompanySettings(settings) {
|
fetchCompanySettings(settings) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/company/settings', {
|
.get('/api/v1/company/settings', {
|
||||||
params: {
|
params: {
|
||||||
settings,
|
settings,
|
||||||
@@ -147,7 +145,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateCompanySettings({ data, message }) {
|
updateCompanySettings({ data, message }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/company/settings', data)
|
.post('/api/v1/company/settings', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
Object.assign(this.selectedCompanySettings, data.settings)
|
Object.assign(this.selectedCompanySettings, data.settings)
|
||||||
@@ -172,7 +170,7 @@ export const useCompanyStore = (useWindow = false) => {
|
|||||||
|
|
||||||
deleteCompany(data) {
|
deleteCompany(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/companies/delete`, data)
|
.post(`/api/v1/companies/delete`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
|
|||||||
18
resources/scripts/admin/stores/custom-field.js
vendored
18
resources/scripts/admin/stores/custom-field.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import customFieldStub from '@/scripts/admin/stub/custom-field'
|
import customFieldStub from '@/scripts/admin/stub/custom-field'
|
||||||
@@ -10,9 +10,7 @@ export const useCustomFieldStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('custom-field', {
|
||||||
id: 'custom-field',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
customFields: [],
|
customFields: [],
|
||||||
isRequestOngoing: false,
|
isRequestOngoing: false,
|
||||||
@@ -41,7 +39,7 @@ export const useCustomFieldStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchCustomFields(params) {
|
fetchCustomFields(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/custom-fields`, { params })
|
.get(`/api/v1/custom-fields`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.customFields = response.data.data
|
this.customFields = response.data.data
|
||||||
@@ -63,7 +61,7 @@ export const useCustomFieldStore = (useWindow = false) => {
|
|||||||
|
|
||||||
this.isRequestOngoing = true
|
this.isRequestOngoing = true
|
||||||
|
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/custom-fields`, { params })
|
.get(`/api/v1/custom-fields`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.customFields = response.data.data
|
this.customFields = response.data.data
|
||||||
@@ -80,7 +78,7 @@ export const useCustomFieldStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchCustomField(id) {
|
fetchCustomField(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/custom-fields/${id}`)
|
.get(`/api/v1/custom-fields/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.currentCustomField = response.data.data
|
this.currentCustomField = response.data.data
|
||||||
@@ -107,7 +105,7 @@ export const useCustomFieldStore = (useWindow = false) => {
|
|||||||
addCustomField(params) {
|
addCustomField(params) {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/custom-fields`, params)
|
.post(`/api/v1/custom-fields`, params)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let data = {
|
let data = {
|
||||||
@@ -139,7 +137,7 @@ export const useCustomFieldStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/custom-fields/${params.id}`, params)
|
.put(`/api/v1/custom-fields/${params.id}`, params)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let data = {
|
let data = {
|
||||||
@@ -174,7 +172,7 @@ export const useCustomFieldStore = (useWindow = false) => {
|
|||||||
deleteCustomFields(id) {
|
deleteCustomFields(id) {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.delete(`/api/v1/custom-fields/${id}`)
|
.delete(`/api/v1/custom-fields/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.customFields.findIndex(
|
let index = this.customFields.findIndex(
|
||||||
|
|||||||
19
resources/scripts/admin/stores/customer.js
vendored
19
resources/scripts/admin/stores/customer.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -12,8 +12,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('customer', {
|
||||||
id: 'customer',
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
customers: [],
|
customers: [],
|
||||||
totalCustomers: 0,
|
totalCustomers: 0,
|
||||||
@@ -75,7 +74,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchCustomers(params) {
|
fetchCustomers(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/customers`, { params })
|
.get(`/api/v1/customers`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.customers = response.data.data
|
this.customers = response.data.data
|
||||||
@@ -92,7 +91,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
fetchViewCustomer(params) {
|
fetchViewCustomer(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.isFetchingViewData = true
|
this.isFetchingViewData = true
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/customers/${params.id}/stats`, { params })
|
.get(`/api/v1/customers/${params.id}/stats`, { params })
|
||||||
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@@ -112,7 +111,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchCustomer(id) {
|
fetchCustomer(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/customers/${id}`)
|
.get(`/api/v1/customers/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
Object.assign(this.currentCustomer, response.data.data)
|
Object.assign(this.currentCustomer, response.data.data)
|
||||||
@@ -129,7 +128,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addCustomer(data) {
|
addCustomer(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/customers', data)
|
.post('/api/v1/customers', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.customers.push(response.data.data)
|
this.customers.push(response.data.data)
|
||||||
@@ -151,7 +150,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateCustomer(data) {
|
updateCustomer(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/customers/${data.id}`, data)
|
.put(`/api/v1/customers/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
@@ -177,7 +176,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
deleteCustomer(id) {
|
deleteCustomer(id) {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/customers/delete`, id)
|
.post(`/api/v1/customers/delete`, id)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.customers.findIndex(
|
let index = this.customers.findIndex(
|
||||||
@@ -201,7 +200,7 @@ export const useCustomerStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/customers/delete`, { ids: this.selectedCustomers })
|
.post(`/api/v1/customers/delete`, { ids: this.selectedCustomers })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.selectedCustomers.forEach((customer) => {
|
this.selectedCustomers.forEach((customer) => {
|
||||||
|
|||||||
8
resources/scripts/admin/stores/dashboard.js
vendored
8
resources/scripts/admin/stores/dashboard.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useGlobalStore } from '@/scripts/admin/stores/global'
|
import { useGlobalStore } from '@/scripts/admin/stores/global'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -7,9 +7,7 @@ export const useDashboardStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('dashboard', {
|
||||||
id: 'dashboard',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
stats: {
|
stats: {
|
||||||
totalAmountDue: 0,
|
totalAmountDue: 0,
|
||||||
@@ -40,7 +38,7 @@ export const useDashboardStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
loadData(params) {
|
loadData(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/dashboard`, { params })
|
.get(`/api/v1/dashboard`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
// Stats
|
// Stats
|
||||||
|
|||||||
18
resources/scripts/admin/stores/disk.js
vendored
18
resources/scripts/admin/stores/disk.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -7,9 +7,7 @@ export const useDiskStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('disk', {
|
||||||
id: 'disk',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
disks: [],
|
disks: [],
|
||||||
diskDrivers: [],
|
diskDrivers: [],
|
||||||
@@ -71,7 +69,7 @@ export const useDiskStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
fetchDiskEnv(data) {
|
fetchDiskEnv(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/disks/${data.disk}`)
|
.get(`/api/v1/disks/${data.disk}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -85,7 +83,7 @@ export const useDiskStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchDisks(params) {
|
fetchDisks(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/disks`, { params })
|
.get(`/api/v1/disks`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.disks = response.data.data
|
this.disks = response.data.data
|
||||||
@@ -100,7 +98,7 @@ export const useDiskStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchDiskDrivers() {
|
fetchDiskDrivers() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/disk/drivers`)
|
.get(`/api/v1/disk/drivers`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.diskConfigData = response.data
|
this.diskConfigData = response.data
|
||||||
@@ -116,7 +114,7 @@ export const useDiskStore = (useWindow = false) => {
|
|||||||
|
|
||||||
deleteFileDisk(id) {
|
deleteFileDisk(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.delete(`/api/v1/disks/${id}`)
|
.delete(`/api/v1/disks/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
@@ -141,7 +139,7 @@ export const useDiskStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateDisk(data) {
|
updateDisk(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/disks/${data.id}`, data)
|
.put(`/api/v1/disks/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
@@ -166,7 +164,7 @@ export const useDiskStore = (useWindow = false) => {
|
|||||||
|
|
||||||
createDisk(data) {
|
createDisk(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/disks`, data)
|
.post(`/api/v1/disks`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
|
|||||||
40
resources/scripts/admin/stores/estimate.js
vendored
40
resources/scripts/admin/stores/estimate.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import Guid from 'guid'
|
import Guid from 'guid'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
@@ -20,9 +20,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('estimate', {
|
||||||
id: 'estimate',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
templates: [],
|
templates: [],
|
||||||
|
|
||||||
@@ -100,7 +98,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
previewEstimate(params) {
|
previewEstimate(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/estimates/${params.id}/send/preview`, { params })
|
.get(`/api/v1/estimates/${params.id}/send/preview`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -114,7 +112,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchEstimates(params) {
|
fetchEstimates(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/estimates`, { params })
|
.get(`/api/v1/estimates`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.estimates = response.data.data
|
this.estimates = response.data.data
|
||||||
@@ -130,7 +128,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
getNextNumber(params, setState = false) {
|
getNextNumber(params, setState = false) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/next-number?key=estimate`, { params })
|
.get(`/api/v1/next-number?key=estimate`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (setState) {
|
if (setState) {
|
||||||
@@ -147,7 +145,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchEstimate(id) {
|
fetchEstimate(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/estimates/${id}`)
|
.get(`/api/v1/estimates/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setEstimateData(response.data.data)
|
this.setEstimateData(response.data.data)
|
||||||
@@ -222,7 +220,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/${data.id}/send`, data)
|
.post(`/api/v1/estimates/${data.id}/send`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!data.is_preview) {
|
if (!data.is_preview) {
|
||||||
@@ -242,7 +240,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addEstimate(data) {
|
addEstimate(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/estimates', data)
|
.post('/api/v1/estimates', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.estimates = [...this.estimates, response.data.estimate]
|
this.estimates = [...this.estimates, response.data.estimate]
|
||||||
@@ -266,7 +264,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/delete`, id)
|
.post(`/api/v1/estimates/delete`, id)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.estimates.findIndex(
|
let index = this.estimates.findIndex(
|
||||||
@@ -292,7 +290,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/delete`, { ids: this.selectedEstimates })
|
.post(`/api/v1/estimates/delete`, { ids: this.selectedEstimates })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.selectedEstimates.forEach((estimate) => {
|
this.selectedEstimates.forEach((estimate) => {
|
||||||
@@ -318,7 +316,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateEstimate(data) {
|
updateEstimate(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/estimates/${data.id}`, data)
|
.put(`/api/v1/estimates/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let pos = this.estimates.findIndex(
|
let pos = this.estimates.findIndex(
|
||||||
@@ -341,7 +339,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
cloneEstimate(data) {
|
cloneEstimate(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/${data.id}/clone`, data)
|
.post(`/api/v1/estimates/${data.id}/clone`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
@@ -360,7 +358,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
markAsAccepted(data) {
|
markAsAccepted(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/${data.id}/status`, data)
|
.post(`/api/v1/estimates/${data.id}/status`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let pos = this.estimates.findIndex(
|
let pos = this.estimates.findIndex(
|
||||||
@@ -387,7 +385,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
markAsRejected(data) {
|
markAsRejected(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/${data.id}/status`, data)
|
.post(`/api/v1/estimates/${data.id}/status`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
@@ -407,7 +405,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
markAsSent(data) {
|
markAsSent(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/${data.id}/status`, data)
|
.post(`/api/v1/estimates/${data.id}/status`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let pos = this.estimates.findIndex(
|
let pos = this.estimates.findIndex(
|
||||||
@@ -435,7 +433,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
convertToInvoice(id) {
|
convertToInvoice(id) {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/estimates/${id}/convert-to-invoice`)
|
.post(`/api/v1/estimates/${id}/convert-to-invoice`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
notificationStore.showNotification({
|
notificationStore.showNotification({
|
||||||
@@ -453,7 +451,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
searchEstimate(data) {
|
searchEstimate(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/estimates?${data}`)
|
.get(`/api/v1/estimates?${data}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -487,7 +485,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
selectCustomer(id) {
|
selectCustomer(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/customers/${id}`)
|
.get(`/api/v1/customers/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.newEstimate.customer = response.data.data
|
this.newEstimate.customer = response.data.data
|
||||||
@@ -502,7 +500,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
fetchEstimateTemplates(params) {
|
fetchEstimateTemplates(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/estimates/templates`, { params })
|
.get(`/api/v1/estimates/templates`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.templates = response.data.estimateTemplates
|
this.templates = response.data.estimateTemplates
|
||||||
|
|||||||
32
resources/scripts/admin/stores/exchange-rate.js
vendored
32
resources/scripts/admin/stores/exchange-rate.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -8,9 +8,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('exchange-rate', {
|
||||||
id: 'exchange-rate',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
supportedCurrencies: [],
|
supportedCurrencies: [],
|
||||||
drivers: [],
|
drivers: [],
|
||||||
@@ -37,7 +35,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
fetchProviders(params) {
|
fetchProviders(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/exchange-rate-providers', { params })
|
.get('/api/v1/exchange-rate-providers', { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.providers = response.data.data
|
this.providers = response.data.data
|
||||||
@@ -52,7 +50,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchDefaultProviders() {
|
fetchDefaultProviders() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/config?key=exchange_rate_drivers`)
|
.get(`/api/v1/config?key=exchange_rate_drivers`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.drivers = response.data.exchange_rate_drivers
|
this.drivers = response.data.exchange_rate_drivers
|
||||||
@@ -68,7 +66,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchProvider(id) {
|
fetchProvider(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/exchange-rate-providers/${id}`)
|
.get(`/api/v1/exchange-rate-providers/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.currentExchangeRate = response.data.data
|
this.currentExchangeRate = response.data.data
|
||||||
@@ -84,7 +82,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addProvider(data) {
|
addProvider(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/exchange-rate-providers', data)
|
.post('/api/v1/exchange-rate-providers', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
notificationStore.showNotification({
|
notificationStore.showNotification({
|
||||||
@@ -104,7 +102,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateProvider(data) {
|
updateProvider(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/exchange-rate-providers/${data.id}`, data)
|
.put(`/api/v1/exchange-rate-providers/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
notificationStore.showNotification({
|
notificationStore.showNotification({
|
||||||
@@ -122,7 +120,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
|
|
||||||
deleteExchangeRate(id) {
|
deleteExchangeRate(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.delete(`/api/v1/exchange-rate-providers/${id}`)
|
.delete(`/api/v1/exchange-rate-providers/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.drivers.findIndex((driver) => driver.id === id)
|
let index = this.drivers.findIndex((driver) => driver.id === id)
|
||||||
@@ -148,7 +146,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
fetchCurrencies(params) {
|
fetchCurrencies(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/supported-currencies`, { params })
|
.get(`/api/v1/supported-currencies`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.supportedCurrencies = response.data.supportedCurrencies
|
this.supportedCurrencies = response.data.supportedCurrencies
|
||||||
@@ -163,7 +161,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
fetchActiveCurrency(params) {
|
fetchActiveCurrency(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/used-currencies', { params })
|
.get('/api/v1/used-currencies', { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.activeUsedCurrencies = response.data.activeUsedCurrencies
|
this.activeUsedCurrencies = response.data.activeUsedCurrencies
|
||||||
@@ -178,7 +176,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
fetchBulkCurrencies() {
|
fetchBulkCurrencies() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/currencies/used')
|
.get('/api/v1/currencies/used')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.bulkCurrencies = response.data.currencies.map((_m) => {
|
this.bulkCurrencies = response.data.currencies.map((_m) => {
|
||||||
@@ -196,7 +194,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
updateBulkExchangeRate(data) {
|
updateBulkExchangeRate(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/currencies/bulk-update-exchange-rate', data)
|
.post('/api/v1/currencies/bulk-update-exchange-rate', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -209,7 +207,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
getCurrentExchangeRate(currencyId) {
|
getCurrentExchangeRate(currencyId) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/currencies/${currencyId}/exchange-rate`)
|
.get(`/api/v1/currencies/${currencyId}/exchange-rate`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -221,7 +219,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
getCurrencyConverterServers() {
|
getCurrencyConverterServers() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/config?key=currency_converter_servers')
|
.get('/api/v1/config?key=currency_converter_servers')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -234,7 +232,7 @@ export const useExchangeRateStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
checkForActiveProvider(currency_id) {
|
checkForActiveProvider(currency_id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/currencies/${currency_id}/active-provider`)
|
.get(`/api/v1/currencies/${currency_id}/active-provider`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
|
|||||||
20
resources/scripts/admin/stores/expense.js
vendored
20
resources/scripts/admin/stores/expense.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
@@ -9,9 +9,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('expense', {
|
||||||
id: 'expense',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
expenses: [],
|
expenses: [],
|
||||||
totalExpenses: 0,
|
totalExpenses: 0,
|
||||||
@@ -38,7 +36,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchExpenses(params) {
|
fetchExpenses(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/expenses`, { params })
|
.get(`/api/v1/expenses`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.expenses = response.data.data
|
this.expenses = response.data.data
|
||||||
@@ -54,7 +52,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchExpense(id) {
|
fetchExpense(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/expenses/${id}`)
|
.get(`/api/v1/expenses/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
@@ -97,7 +95,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
const formData = utils.toFormData(data)
|
const formData = utils.toFormData(data)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/expenses', formData)
|
.post('/api/v1/expenses', formData)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.expenses.push(response.data)
|
this.expenses.push(response.data)
|
||||||
@@ -127,7 +125,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
formData.append('is_attachment_receipt_removed', isAttachmentReceiptRemoved)
|
formData.append('is_attachment_receipt_removed', isAttachmentReceiptRemoved)
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
axios.post(`/api/v1/expenses/${id}`, formData).then((response) => {
|
http.post(`/api/v1/expenses/${id}`, formData).then((response) => {
|
||||||
let pos = this.expenses.findIndex(
|
let pos = this.expenses.findIndex(
|
||||||
(expense) => expense.id === response.data.id
|
(expense) => expense.id === response.data.id
|
||||||
)
|
)
|
||||||
@@ -175,7 +173,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/expenses/delete`, id)
|
.post(`/api/v1/expenses/delete`, id)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.expenses.findIndex(
|
let index = this.expenses.findIndex(
|
||||||
@@ -200,7 +198,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/expenses/delete`, { ids: this.selectedExpenses })
|
.post(`/api/v1/expenses/delete`, { ids: this.selectedExpenses })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.selectedExpenses.forEach((expense) => {
|
this.selectedExpenses.forEach((expense) => {
|
||||||
@@ -223,7 +221,7 @@ export const useExpenseStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
fetchPaymentModes(params) {
|
fetchPaymentModes(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/payment-methods`, { params })
|
.get(`/api/v1/payment-methods`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.paymentModes = response.data.data
|
this.paymentModes = response.data.data
|
||||||
|
|||||||
23
resources/scripts/admin/stores/global.js
vendored
23
resources/scripts/admin/stores/global.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useCompanyStore } from './company'
|
import { useCompanyStore } from './company'
|
||||||
import { useUserStore } from './user'
|
import { useUserStore } from './user'
|
||||||
@@ -11,8 +11,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('global', {
|
||||||
id: 'global',
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
// Global Configuration
|
// Global Configuration
|
||||||
config: null,
|
config: null,
|
||||||
@@ -48,7 +47,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/bootstrap')
|
.get('/api/v1/bootstrap')
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
@@ -123,7 +122,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
resolve(this.currencies)
|
resolve(this.currencies)
|
||||||
} else {
|
} else {
|
||||||
this.areCurrenciesLoading = true
|
this.areCurrenciesLoading = true
|
||||||
axios
|
http
|
||||||
.get('/api/v1/currencies')
|
.get('/api/v1/currencies')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.currencies = response.data.data.filter((currency) => {
|
this.currencies = response.data.data.filter((currency) => {
|
||||||
@@ -143,7 +142,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchConfig(params) {
|
fetchConfig(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/config`, { params })
|
.get(`/api/v1/config`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data.languages) {
|
if (response.data.languages) {
|
||||||
@@ -165,7 +164,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
if (this.dateFormats.length) {
|
if (this.dateFormats.length) {
|
||||||
resolve(this.dateFormats)
|
resolve(this.dateFormats)
|
||||||
} else {
|
} else {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/date/formats')
|
.get('/api/v1/date/formats')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.dateFormats = response.data.date_formats
|
this.dateFormats = response.data.date_formats
|
||||||
@@ -184,7 +183,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
if (this.timeFormats.length) {
|
if (this.timeFormats.length) {
|
||||||
resolve(this.timeFormats)
|
resolve(this.timeFormats)
|
||||||
} else {
|
} else {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/time/formats')
|
.get('/api/v1/time/formats')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.timeFormats = response.data.time_formats
|
this.timeFormats = response.data.time_formats
|
||||||
@@ -203,7 +202,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
if (this.timeZones.length) {
|
if (this.timeZones.length) {
|
||||||
resolve(this.timeZones)
|
resolve(this.timeZones)
|
||||||
} else {
|
} else {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/timezones')
|
.get('/api/v1/timezones')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.timeZones = response.data.time_zones
|
this.timeZones = response.data.time_zones
|
||||||
@@ -222,7 +221,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
if (this.countries.length) {
|
if (this.countries.length) {
|
||||||
resolve(this.countries)
|
resolve(this.countries)
|
||||||
} else {
|
} else {
|
||||||
axios
|
http
|
||||||
.get('/api/v1/countries')
|
.get('/api/v1/countries')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.countries = response.data.data
|
this.countries = response.data.data
|
||||||
@@ -238,7 +237,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchPlaceholders(params) {
|
fetchPlaceholders(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/number-placeholders`, { params })
|
.get(`/api/v1/number-placeholders`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -260,7 +259,7 @@ export const useGlobalStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateGlobalSettings({ data, message }) {
|
updateGlobalSettings({ data, message }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/settings', data)
|
.post('/api/v1/settings', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
Object.assign(this.globalSettings, data.settings)
|
Object.assign(this.globalSettings, data.settings)
|
||||||
|
|||||||
32
resources/scripts/admin/stores/installation.js
vendored
32
resources/scripts/admin/stores/installation.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useCompanyStore } from './company'
|
import { useCompanyStore } from './company'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -8,9 +8,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('installation', {
|
||||||
id: 'installation',
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
currentDataBaseData: {
|
currentDataBaseData: {
|
||||||
database_connection: 'mysql',
|
database_connection: 'mysql',
|
||||||
@@ -28,7 +26,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
actions: {
|
actions: {
|
||||||
fetchInstallationLanguages() {
|
fetchInstallationLanguages() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/installation/languages`)
|
.get(`/api/v1/installation/languages`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -42,7 +40,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchInstallationRequirements() {
|
fetchInstallationRequirements() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/installation/requirements`)
|
.get(`/api/v1/installation/requirements`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -56,7 +54,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchInstallationStep() {
|
fetchInstallationStep() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/installation/wizard-step`)
|
.get(`/api/v1/installation/wizard-step`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -70,7 +68,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addInstallationStep(data) {
|
addInstallationStep(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/installation/wizard-step`, data)
|
.post(`/api/v1/installation/wizard-step`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -84,7 +82,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addInstallationLanguage(data) {
|
addInstallationLanguage(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/installation/wizard-language`, data)
|
.post(`/api/v1/installation/wizard-language`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -98,7 +96,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchInstallationPermissions() {
|
fetchInstallationPermissions() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/installation/permissions`)
|
.get(`/api/v1/installation/permissions`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -112,7 +110,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchInstallationDatabase(params) {
|
fetchInstallationDatabase(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/installation/database/config`, { params })
|
.get(`/api/v1/installation/database/config`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -126,7 +124,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addInstallationDatabase(data) {
|
addInstallationDatabase(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/installation/database/config`, data)
|
.post(`/api/v1/installation/database/config`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -140,7 +138,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addInstallationFinish() {
|
addInstallationFinish() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/installation/finish`)
|
.post(`/api/v1/installation/finish`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -154,7 +152,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
setInstallationDomain(data) {
|
setInstallationDomain(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/installation/set-domain`, data)
|
.put(`/api/v1/installation/set-domain`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -168,9 +166,9 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
installationLogin() {
|
installationLogin() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.get('/sanctum/csrf-cookie').then((response) => {
|
http.get('/sanctum/csrf-cookie').then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/installation/login')
|
.post('/api/v1/installation/login')
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
companyStore.setSelectedCompany(response.data.company)
|
companyStore.setSelectedCompany(response.data.company)
|
||||||
@@ -187,7 +185,7 @@ export const useInstallationStore = (useWindow = false) => {
|
|||||||
|
|
||||||
checkAuthenticated() {
|
checkAuthenticated() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/auth/check`)
|
.get(`/api/v1/auth/check`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
|
|||||||
33
resources/scripts/admin/stores/invoice.js
vendored
33
resources/scripts/admin/stores/invoice.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import Guid from 'guid'
|
import Guid from 'guid'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
@@ -22,8 +22,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('invoice', {
|
||||||
id: 'invoice',
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
templates: [],
|
templates: [],
|
||||||
invoices: [],
|
invoices: [],
|
||||||
@@ -108,7 +107,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
previewInvoice(params) {
|
previewInvoice(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/invoices/${params.id}/send/preview`, { params })
|
.get(`/api/v1/invoices/${params.id}/send/preview`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -122,7 +121,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchInvoices(params) {
|
fetchInvoices(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/invoices`, { params })
|
.get(`/api/v1/invoices`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.invoices = response.data.data
|
this.invoices = response.data.data
|
||||||
@@ -138,7 +137,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchInvoice(id) {
|
fetchInvoice(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/invoices/${id}`)
|
.get(`/api/v1/invoices/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setInvoiceData(response.data.data)
|
this.setInvoiceData(response.data.data)
|
||||||
@@ -204,7 +203,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
sendInvoice(data) {
|
sendInvoice(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/invoices/${data.id}/send`, data)
|
.post(`/api/v1/invoices/${data.id}/send`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
notificationStore.showNotification({
|
notificationStore.showNotification({
|
||||||
@@ -222,7 +221,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addInvoice(data) {
|
addInvoice(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/invoices', data)
|
.post('/api/v1/invoices', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.invoices = [...this.invoices, response.data.invoice]
|
this.invoices = [...this.invoices, response.data.invoice]
|
||||||
@@ -243,7 +242,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
deleteInvoice(id) {
|
deleteInvoice(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/invoices/delete`, id)
|
.post(`/api/v1/invoices/delete`, id)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.invoices.findIndex(
|
let index = this.invoices.findIndex(
|
||||||
@@ -266,7 +265,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
deleteMultipleInvoices(id) {
|
deleteMultipleInvoices(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/invoices/delete`, { ids: this.selectedInvoices })
|
.post(`/api/v1/invoices/delete`, { ids: this.selectedInvoices })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.selectedInvoices.forEach((invoice) => {
|
this.selectedInvoices.forEach((invoice) => {
|
||||||
@@ -292,7 +291,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateInvoice(data) {
|
updateInvoice(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/invoices/${data.id}`, data)
|
.put(`/api/v1/invoices/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let pos = this.invoices.findIndex(
|
let pos = this.invoices.findIndex(
|
||||||
@@ -316,7 +315,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
cloneInvoice(data) {
|
cloneInvoice(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/invoices/${data.id}/clone`, data)
|
.post(`/api/v1/invoices/${data.id}/clone`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
notificationStore.showNotification({
|
notificationStore.showNotification({
|
||||||
@@ -334,7 +333,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
markAsSent(data) {
|
markAsSent(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/invoices/${data.id}/status`, data)
|
.post(`/api/v1/invoices/${data.id}/status`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let pos = this.invoices.findIndex(
|
let pos = this.invoices.findIndex(
|
||||||
@@ -360,7 +359,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
getNextNumber(params, setState = false) {
|
getNextNumber(params, setState = false) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/next-number?key=invoice`, { params })
|
.get(`/api/v1/next-number?key=invoice`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (setState) {
|
if (setState) {
|
||||||
@@ -377,7 +376,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
searchInvoice(data) {
|
searchInvoice(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/invoices?${data}`)
|
.get(`/api/v1/invoices?${data}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
@@ -411,7 +410,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
selectCustomer(id) {
|
selectCustomer(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/customers/${id}`)
|
.get(`/api/v1/customers/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.newInvoice.customer = response.data.data
|
this.newInvoice.customer = response.data.data
|
||||||
@@ -427,7 +426,7 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchInvoiceTemplates(params) {
|
fetchInvoiceTemplates(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/invoices/templates`, { params })
|
.get(`/api/v1/invoices/templates`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.templates = response.data.invoiceTemplates
|
this.templates = response.data.invoiceTemplates
|
||||||
|
|||||||
27
resources/scripts/admin/stores/item.js
vendored
27
resources/scripts/admin/stores/item.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios'
|
import http from '@/scripts/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
@@ -7,8 +7,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
const { global } = window.i18n
|
const { global } = window.i18n
|
||||||
|
|
||||||
return defineStoreFunc({
|
return defineStoreFunc('item', {
|
||||||
id: 'item',
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
items: [],
|
items: [],
|
||||||
totalItems: 0,
|
totalItems: 0,
|
||||||
@@ -45,7 +44,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
},
|
},
|
||||||
fetchItems(params) {
|
fetchItems(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/items`, { params })
|
.get(`/api/v1/items`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.items = response.data.data
|
this.items = response.data.data
|
||||||
@@ -62,7 +61,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchItem(id) {
|
fetchItem(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/items/${id}`)
|
.get(`/api/v1/items/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
@@ -79,7 +78,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
|
|
||||||
addItem(data) {
|
addItem(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post('/api/v1/items', data)
|
.post('/api/v1/items', data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
@@ -102,7 +101,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
|
|
||||||
updateItem(data) {
|
updateItem(data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/items/${data.id}`, data)
|
.put(`/api/v1/items/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
@@ -133,7 +132,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/items/delete`, id)
|
.post(`/api/v1/items/delete`, id)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let index = this.items.findIndex((item) => item.id === id)
|
let index = this.items.findIndex((item) => item.id === id)
|
||||||
@@ -157,7 +156,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/items/delete`, { ids: this.selectedItems })
|
.post(`/api/v1/items/delete`, { ids: this.selectedItems })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.selectedItems.forEach((item) => {
|
this.selectedItems.forEach((item) => {
|
||||||
@@ -205,7 +204,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.post(`/api/v1/units`, data)
|
.post(`/api/v1/units`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.itemUnits.push(response.data.data)
|
this.itemUnits.push(response.data.data)
|
||||||
@@ -239,7 +238,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.put(`/api/v1/units/${data.id}`, data)
|
.put(`/api/v1/units/${data.id}`, data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
let pos = this.itemUnits.findIndex(
|
let pos = this.itemUnits.findIndex(
|
||||||
@@ -274,7 +273,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchItemUnits(params) {
|
fetchItemUnits(params) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/units`, { params })
|
.get(`/api/v1/units`, { params })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.itemUnits = response.data.data
|
this.itemUnits = response.data.data
|
||||||
@@ -289,7 +288,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
|
|
||||||
fetchItemUnit(id) {
|
fetchItemUnit(id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.get(`/api/v1/units/${id}`)
|
.get(`/api/v1/units/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.currentItemUnit = response.data.data
|
this.currentItemUnit = response.data.data
|
||||||
@@ -306,7 +305,7 @@ export const useItemStore = (useWindow = false) => {
|
|||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
http
|
||||||
.delete(`/api/v1/units/${id}`)
|
.delete(`/api/v1/units/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response.data.error) {
|
if (!response.data.error) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user