Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83caff13cd | ||
|
|
d2736b1c46 | ||
|
|
cdc504f518 | ||
|
|
2c840b2d97 | ||
|
|
34db4b7897 | ||
|
|
beb2a43ed3 | ||
|
|
7a25a15877 | ||
|
|
8eee4bc4f5 | ||
|
|
c34cfeea11 | ||
|
|
f37edbcc92 | ||
|
|
ba581313df | ||
|
|
1c1a180dd5 | ||
|
|
8e815fd887 | ||
|
|
df612bc773 | ||
|
|
10b2cf5af1 | ||
|
|
f140256efd | ||
|
|
1ed8e5b2d0 | ||
|
|
fcb7c96bca | ||
|
|
d587e3fd00 | ||
|
|
1b26d47539 | ||
|
|
340522da19 | ||
|
|
3ecfcede3e | ||
|
|
30f76c044a | ||
|
|
854a8bb50d | ||
|
|
1be3132dc4 | ||
|
|
fefd856cfb | ||
|
|
e5fe62e4de | ||
|
|
129d63c5b1 | ||
|
|
65e9182272 | ||
|
|
3abcba2752 | ||
|
|
6c1e51d126 | ||
|
|
c1b8ba448d | ||
|
|
ab5ea81424 | ||
|
|
befd69fdb2 | ||
|
|
417b92ad9f | ||
|
|
d6b1e102fe | ||
|
|
d7bf942da2 | ||
|
|
5db82db958 | ||
|
|
c63355391a | ||
|
|
de3c8f89fb | ||
|
|
cab4c62c5d | ||
|
|
4b634f96c9 | ||
|
|
9ef0932c6e | ||
|
|
d122c8c95a | ||
|
|
820073e8e0 | ||
|
|
80c14b4b7f | ||
|
|
174acbf70e | ||
|
|
c93b8f0da9 | ||
|
|
2693b0b0f2 | ||
|
|
54d9c57925 | ||
|
|
ba3e94c4ad | ||
|
|
a644653513 | ||
|
|
3391d104f9 | ||
|
|
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
|
||||
|
||||
Use `search-docs` for detailed Tailwind CSS v3 patterns and documentation.
|
||||
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
|
||||
|
||||
## 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).
|
||||
- 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.
|
||||
- Configuration is done in the `tailwind.config.js` file.
|
||||
- Import using `@tailwind` directives:
|
||||
- Always use Tailwind CSS v4 and avoid deprecated utilities.
|
||||
- `corePlugins` is not supported in Tailwind v4.
|
||||
|
||||
<!-- 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
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@theme {
|
||||
--color-brand: oklch(0.72 0.11 178);
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
When listing items, use gap utilities for spacing; don't use margins.
|
||||
Use `gap` utilities instead of margins for spacing between siblings:
|
||||
|
||||
<!-- Gap Utilities -->
|
||||
```html
|
||||
@@ -77,15 +110,10 @@ If existing pages and components support dark mode, new pages and components mus
|
||||
</div>
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check browser for visual rendering
|
||||
2. Test responsive breakpoints
|
||||
3. Verify dark mode if project uses it
|
||||
|
||||
## 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
|
||||
- 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
|
||||
- Forgetting to add dark mode variants when the project uses dark mode
|
||||
@@ -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'
|
||||
},
|
||||
};
|
||||
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
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '.github/**/*'
|
||||
- '**/*.md'
|
||||
- 'public/build/*.js'
|
||||
- 'public/build/**/*.js'
|
||||
- 'docker/**/*'
|
||||
- 'resources/scripts/**/*'
|
||||
tags-ignore:
|
||||
- "*"
|
||||
branches-ignore:
|
||||
- 'translations'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- 'public/build/*.js'
|
||||
- 'public/build/**/*.js'
|
||||
branches-ignore:
|
||||
- 'translations'
|
||||
# Allow manually triggering the workflow.
|
||||
workflow_dispatch:
|
||||
|
||||
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:
|
||||
name: 0️⃣ Kill previous runs
|
||||
runs-on: ubuntu-latest
|
||||
@@ -41,6 +54,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- kill_previous
|
||||
- changes
|
||||
if: needs.changes.outputs.php == 'true'
|
||||
steps:
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -60,6 +75,8 @@ jobs:
|
||||
name: 2️⃣ PHP ${{ matrix.php-version }} Tests
|
||||
needs:
|
||||
- php_syntax_errors
|
||||
- changes
|
||||
if: needs.changes.outputs.php == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -83,10 +100,10 @@ jobs:
|
||||
- name: Install Composer dependencies
|
||||
uses: ramsey/composer-install@v2
|
||||
|
||||
- name: Use Node.js 20
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
|
||||
- name: 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
|
||||
uses: ramsey/composer-install@v2
|
||||
|
||||
- name: Use Node.js 20
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
|
||||
- name: Install
|
||||
run: npm install
|
||||
@@ -100,10 +100,10 @@ jobs:
|
||||
with:
|
||||
composer-options: --no-dev
|
||||
|
||||
- name: Use Node.js 20
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
|
||||
- name: Install
|
||||
run: npm install
|
||||
|
||||
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.
|
||||
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
|
||||
*/
|
||||
protected $description = 'Clean database, database_created and public/storage folder';
|
||||
protected $description = 'Clean database and public/storage folder';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
namespace App\Http\Controllers\V1\Admin\Backup;
|
||||
|
||||
use App\Jobs\CreateBackupJob;
|
||||
use App\Models\FileDisk;
|
||||
use App\Rules\Backup\PathToZip;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -27,6 +28,16 @@ class BackupsController extends ApiController
|
||||
$configuredBackupDisks = config('backup.backup.destination.disks');
|
||||
|
||||
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'));
|
||||
|
||||
$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');
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ class CompaniesController extends Controller
|
||||
$company = Company::find($request->header('company'));
|
||||
$this->authorize('transfer company ownership', $company);
|
||||
|
||||
if ($user->hasCompany($company->id)) {
|
||||
if (! $user->hasCompany($company->id)) {
|
||||
return response()->json([
|
||||
'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');
|
||||
|
||||
Customer::deleteCustomers($request->ids);
|
||||
$ids = Customer::whereCompany()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
||||
Customer::deleteCustomers($ids);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
@@ -21,6 +21,7 @@ class CloneEstimateController extends Controller
|
||||
*/
|
||||
public function __invoke(Request $request, Estimate $estimate)
|
||||
{
|
||||
$this->authorize('view', $estimate);
|
||||
$this->authorize('create', Estimate::class);
|
||||
|
||||
$date = Carbon::now();
|
||||
|
||||
@@ -68,7 +68,11 @@ class EstimatesController extends Controller
|
||||
{
|
||||
$this->authorize('delete multiple estimates');
|
||||
|
||||
Estimate::destroy($request->ids);
|
||||
$ids = Estimate::whereCompany()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
||||
Estimate::destroy($ids);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Expense;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\DuplicateExpenseRequest;
|
||||
use App\Http\Resources\ExpenseResource;
|
||||
use App\Models\CompanySetting;
|
||||
use App\Models\ExchangeRateLog;
|
||||
use App\Models\Expense;
|
||||
|
||||
class DuplicateExpenseController extends Controller
|
||||
{
|
||||
/**
|
||||
* Duplicate an expense, appending " (copy)" to the note (description).
|
||||
*/
|
||||
public function __invoke(DuplicateExpenseRequest $request, Expense $expense): ExpenseResource
|
||||
{
|
||||
$this->authorize('view', $expense);
|
||||
$this->authorize('create', Expense::class);
|
||||
|
||||
$expense->load('fields');
|
||||
|
||||
$companyCurrency = CompanySetting::getSetting('currency', $request->header('company'));
|
||||
$currentCurrency = $expense->currency_id;
|
||||
$exchangeRate = $companyCurrency != $currentCurrency ? $expense->exchange_rate : 1;
|
||||
|
||||
$notes = trim((string) $expense->notes);
|
||||
$duplicatedNotes = $notes === '' ? '(copy)' : $notes.' (copy)';
|
||||
|
||||
$newExpense = Expense::query()->create([
|
||||
'expense_date' => $request->validated('expense_date'),
|
||||
'expense_number' => null,
|
||||
'expense_category_id' => $expense->expense_category_id,
|
||||
'payment_method_id' => $expense->payment_method_id,
|
||||
'amount' => $expense->amount,
|
||||
'customer_id' => $expense->customer_id,
|
||||
'notes' => $duplicatedNotes,
|
||||
'currency_id' => $expense->currency_id,
|
||||
'creator_id' => $request->user()->id,
|
||||
'company_id' => $request->header('company'),
|
||||
'exchange_rate' => $exchangeRate,
|
||||
'base_amount' => $expense->amount * $exchangeRate,
|
||||
]);
|
||||
|
||||
if ((string) $newExpense->currency_id !== (string) $companyCurrency) {
|
||||
ExchangeRateLog::addExchangeRateLog($newExpense);
|
||||
}
|
||||
|
||||
if ($expense->fields()->exists()) {
|
||||
$customFields = [];
|
||||
|
||||
foreach ($expense->fields as $data) {
|
||||
$customFields[] = [
|
||||
'id' => $data->custom_field_id,
|
||||
'value' => $data->defaultAnswer,
|
||||
];
|
||||
}
|
||||
|
||||
$newExpense->addCustomFields($customFields);
|
||||
}
|
||||
|
||||
return new ExpenseResource($newExpense);
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,11 @@ class ExpensesController extends Controller
|
||||
{
|
||||
$this->authorize('delete multiple expenses');
|
||||
|
||||
Expense::destroy($request->ids);
|
||||
$ids = Expense::whereCompany()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
||||
Expense::destroy($ids);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
@@ -25,7 +25,8 @@ class SearchController extends Controller
|
||||
->paginate(10);
|
||||
|
||||
if ($user->isOwner()) {
|
||||
$users = User::applyFilters($request->only(['search']))
|
||||
$users = User::whereCompany()
|
||||
->applyFilters($request->only(['search']))
|
||||
->latest()
|
||||
->paginate(10);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ class CloneInvoiceController extends Controller
|
||||
*/
|
||||
public function __invoke(Request $request, Invoice $invoice)
|
||||
{
|
||||
$this->authorize('view', $invoice);
|
||||
$this->authorize('create', Invoice::class);
|
||||
|
||||
$date = Carbon::now();
|
||||
|
||||
@@ -100,7 +100,11 @@ class InvoicesController extends Controller
|
||||
{
|
||||
$this->authorize('delete multiple invoices');
|
||||
|
||||
Invoice::deleteInvoices($request->ids);
|
||||
$ids = Invoice::whereCompany()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
||||
Invoice::deleteInvoices($ids);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
@@ -90,7 +90,11 @@ class ItemsController extends Controller
|
||||
{
|
||||
$this->authorize('delete multiple items');
|
||||
|
||||
Item::destroy($request->ids);
|
||||
$ids = Item::whereCompany()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
||||
Item::destroy($ids);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
@@ -73,7 +73,11 @@ class PaymentsController extends Controller
|
||||
{
|
||||
$this->authorize('delete multiple payments');
|
||||
|
||||
Payment::deletePayments($request->ids);
|
||||
$ids = Payment::whereCompany()
|
||||
->whereIn('id', $request->ids)
|
||||
->pluck('id');
|
||||
|
||||
Payment::deletePayments($ids);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
@@ -84,7 +84,11 @@ class RecurringInvoiceController extends Controller
|
||||
{
|
||||
$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([
|
||||
'success' => true,
|
||||
|
||||
@@ -7,11 +7,12 @@ use App\Models\Company;
|
||||
use App\Models\CompanySetting;
|
||||
use App\Models\Currency;
|
||||
use App\Models\Expense;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use PDF;
|
||||
|
||||
class ExpensesReportController extends Controller
|
||||
{
|
||||
@@ -19,7 +20,7 @@ class ExpensesReportController extends Controller
|
||||
* Handle the incoming request.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return JsonResponse
|
||||
* @return View|Response
|
||||
*/
|
||||
public function __invoke(Request $request, $hash)
|
||||
{
|
||||
@@ -31,14 +32,26 @@ class ExpensesReportController extends Controller
|
||||
|
||||
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)
|
||||
->applyFilters($request->only(['from_date', 'to_date']))
|
||||
->expensesAttributes()
|
||||
->applyFilters($request->only(['from_date', 'to_date', 'expense_category_id']))
|
||||
->orderBy('expense_date', 'asc')
|
||||
->get();
|
||||
$totalAmount = 0;
|
||||
foreach ($expenseCategories as $category) {
|
||||
$totalAmount += $category->total_amount;
|
||||
|
||||
$totalAmount = $expenses->sum('base_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);
|
||||
@@ -62,7 +75,7 @@ class ExpensesReportController extends Controller
|
||||
->get();
|
||||
|
||||
view()->share([
|
||||
'expenseCategories' => $expenseCategories,
|
||||
'expenseGroups' => $expenseGroups,
|
||||
'colorSettings' => $colorSettings,
|
||||
'totalExpense' => $totalAmount,
|
||||
'company' => $company,
|
||||
@@ -70,7 +83,7 @@ class ExpensesReportController extends Controller
|
||||
'to_date' => $to_date,
|
||||
'currency' => $currency,
|
||||
]);
|
||||
$pdf = PDF::loadView('app.pdf.reports.expenses');
|
||||
$pdf = Pdf::loadView('app.pdf.reports.expenses');
|
||||
|
||||
if ($request->has('preview')) {
|
||||
return view('app.pdf.reports.expenses');
|
||||
|
||||
@@ -165,11 +165,11 @@ class MailConfigurationController extends Controller
|
||||
switch ($driver) {
|
||||
case 'smtp':
|
||||
$MailData = array_merge($MailData, [
|
||||
'mail_host' => $mailSettings['mail_host'] ?? '',
|
||||
'mail_port' => $mailSettings['mail_port'] ?? '',
|
||||
'mail_username' => $mailSettings['mail_username'] ?? '',
|
||||
'mail_password' => $mailSettings['mail_password'] ?? '',
|
||||
'mail_encryption' => $mailSettings['mail_encryption'] ?? 'none',
|
||||
'mail_host' => $mailSettings['mail_host'] ?? config('mail.mailers.smtp.host', ''),
|
||||
'mail_port' => $mailSettings['mail_port'] ?? config('mail.mailers.smtp.port', ''),
|
||||
'mail_username' => $mailSettings['mail_username'] ?? config('mail.mailers.smtp.username', ''),
|
||||
'mail_password' => $mailSettings['mail_password'] ?? config('mail.mailers.smtp.password', ''),
|
||||
'mail_encryption' => $mailSettings['mail_encryption'] ?? config('mail.mailers.smtp.encryption', 'none'),
|
||||
'mail_scheme' => $mailSettings['mail_scheme'] ?? '',
|
||||
'mail_url' => $mailSettings['mail_url'] ?? '',
|
||||
'mail_timeout' => $mailSettings['mail_timeout'] ?? '',
|
||||
|
||||
@@ -25,14 +25,15 @@ class UsersController extends Controller
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
$users = User::applyFilters($request->all())
|
||||
$users = User::whereCompany()
|
||||
->applyFilters($request->all())
|
||||
->where('id', '<>', $user->id)
|
||||
->latest()
|
||||
->paginate($limit);
|
||||
|
||||
return UserResource::collection($users)
|
||||
->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)
|
||||
{
|
||||
$databaseData = [];
|
||||
$connection = $request->connection ?? config('database.default');
|
||||
|
||||
switch ($request->connection) {
|
||||
switch ($connection) {
|
||||
case 'sqlite':
|
||||
$databaseData = [
|
||||
'database_connection' => 'sqlite',
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Http\Controllers\V1\Installation;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Space\InstallUtils;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -16,10 +15,6 @@ class FinishController extends Controller
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
if (! InstallUtils::createDbMarker()) {
|
||||
\Log::error('Install: Unable to create db marker.');
|
||||
}
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class OnboardingWizardController extends Controller
|
||||
*/
|
||||
public function getStep(Request $request)
|
||||
{
|
||||
if (! InstallUtils::dbMarkerExists()) {
|
||||
if (! InstallUtils::isDbCreated()) {
|
||||
return response()->json([
|
||||
'profile_complete' => 0,
|
||||
'profile_language' => 'en',
|
||||
|
||||
@@ -17,7 +17,11 @@ class InstallationMiddleware
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,13 @@ class RedirectIfInstalled
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (InstallUtils::dbMarkerExists()) {
|
||||
if (Setting::getSetting('profile_complete') === 'COMPLETED') {
|
||||
return redirect('login');
|
||||
if (InstallUtils::isDbCreated()) {
|
||||
try {
|
||||
if (Setting::getSetting('profile_complete') === 'COMPLETED') {
|
||||
return redirect('login');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Settings table may not exist yet during installation
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
app/Http/Requests/DuplicateExpenseRequest.php
Normal file
31
app/Http/Requests/DuplicateExpenseRequest.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DuplicateExpenseRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, array<int, string>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'expense_date' => [
|
||||
'required',
|
||||
'date_format:Y-m-d',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\FileDisk;
|
||||
use App\Space\BackupConfigurationFactory;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Spatie\Backup\Config\Config;
|
||||
use Spatie\Backup\Tasks\Backup\BackupJobFactory;
|
||||
|
||||
class CreateBackupJob implements ShouldQueue
|
||||
@@ -25,7 +24,7 @@ class CreateBackupJob implements ShouldQueue
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($data = '')
|
||||
public function __construct($data = [])
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
@@ -35,14 +34,7 @@ class CreateBackupJob implements ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$fileDisk = FileDisk::find($this->data['file_disk_id']);
|
||||
$fileDisk->setConfig();
|
||||
|
||||
$prefix = env('DYNAMIC_DISK_PREFIX', 'temp_');
|
||||
|
||||
config(['backup.backup.destination.disks' => [$prefix.$fileDisk->driver]]);
|
||||
|
||||
$config = Config::fromArray(config('backup'));
|
||||
$config = BackupConfigurationFactory::make($this->data);
|
||||
$backupJob = BackupJobFactory::createFromConfig($config);
|
||||
if (! defined('SIGINT')) {
|
||||
$backupJob->disableSignals();
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Facades\PDF;
|
||||
use App\Mail\SendEstimateMail;
|
||||
use App\Services\SerialNumberFormatter;
|
||||
use App\Space\PdfTemplateUtils;
|
||||
use App\Support\PdfHtmlSanitizer;
|
||||
use App\Traits\GeneratesPdfTrait;
|
||||
use App\Traits\HasCustomFieldsTrait;
|
||||
use Carbon\Carbon;
|
||||
@@ -476,7 +475,7 @@ class Estimate extends Model implements HasMedia
|
||||
|
||||
public function getNotes()
|
||||
{
|
||||
return PdfHtmlSanitizer::sanitize($this->getFormattedString($this->notes));
|
||||
return $this->getFormattedString($this->notes);
|
||||
}
|
||||
|
||||
public function getEmailAttachmentSetting()
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Facades\PDF;
|
||||
use App\Mail\SendInvoiceMail;
|
||||
use App\Services\SerialNumberFormatter;
|
||||
use App\Space\PdfTemplateUtils;
|
||||
use App\Support\PdfHtmlSanitizer;
|
||||
use App\Traits\GeneratesPdfTrait;
|
||||
use App\Traits\HasCustomFieldsTrait;
|
||||
use Carbon\Carbon;
|
||||
@@ -657,7 +656,7 @@ class Invoice extends Model implements HasMedia
|
||||
|
||||
public function getNotes()
|
||||
{
|
||||
return PdfHtmlSanitizer::sanitize($this->getFormattedString($this->notes));
|
||||
return $this->getFormattedString($this->notes);
|
||||
}
|
||||
|
||||
public function getEmailString($body)
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Facades\Hashids;
|
||||
use App\Jobs\GeneratePaymentPdfJob;
|
||||
use App\Mail\SendPaymentMail;
|
||||
use App\Services\SerialNumberFormatter;
|
||||
use App\Support\PdfHtmlSanitizer;
|
||||
use App\Traits\GeneratesPdfTrait;
|
||||
use App\Traits\HasCustomFieldsTrait;
|
||||
use Barryvdh\DomPDF\Facade\Pdf as PDF;
|
||||
@@ -434,7 +433,7 @@ class Payment extends Model implements HasMedia
|
||||
|
||||
public function getNotes()
|
||||
{
|
||||
return PdfHtmlSanitizer::sanitize($this->getFormattedString($this->notes));
|
||||
return $this->getFormattedString($this->notes);
|
||||
}
|
||||
|
||||
public function getEmailBody($body)
|
||||
|
||||
@@ -213,6 +213,13 @@ class User extends Authenticatable implements HasMedia
|
||||
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)
|
||||
{
|
||||
if ($limit == 'all') {
|
||||
|
||||
@@ -32,7 +32,7 @@ class CustomerPolicy
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class CustomerPolicy
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class CustomerPolicy
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class CustomerPolicy
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ class CustomerPolicy
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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 Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use League\Flysystem\FilesystemException;
|
||||
|
||||
class InstallUtils
|
||||
{
|
||||
@@ -17,7 +15,7 @@ class InstallUtils
|
||||
*/
|
||||
public static function isDbCreated()
|
||||
{
|
||||
return self::dbMarkerExists() && self::tableExists('users');
|
||||
return self::tableExists('users');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,54 +42,6 @@ class InstallUtils
|
||||
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
|
||||
*
|
||||
|
||||
@@ -19,6 +19,9 @@ final class PdfHtmlSanitizer
|
||||
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>';
|
||||
$html = strip_tags($html, $allowedTags);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Traits;
|
||||
use App\Models\Address;
|
||||
use App\Models\CompanySetting;
|
||||
use App\Models\FileDisk;
|
||||
use App\Support\PdfHtmlSanitizer;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
@@ -180,8 +181,12 @@ trait GeneratesPdfTrait
|
||||
|
||||
$str = str_replace('<p>', '', $str);
|
||||
|
||||
$str = str_replace('</p>', '</br>', $str);
|
||||
$str = str_replace('</p>', '<br />', $str);
|
||||
|
||||
return $str;
|
||||
// Sanitize the assembled HTML to strip any SSRF vectors that may have
|
||||
// entered through user-supplied address fields, customer names, or
|
||||
// custom field values. Notes also pass through this method, so they
|
||||
// get the same treatment without needing a separate wrapper.
|
||||
return PdfHtmlSanitizer::sanitize($str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,12 @@
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": "^8.4",
|
||||
"aws/aws-sdk-php": "^3.336",
|
||||
"barryvdh/laravel-dompdf": "^v3.0",
|
||||
"doctrine/dbal": "^4.2",
|
||||
"dragonmantank/cron-expression": "^v3.4",
|
||||
"gotenberg/gotenberg-php": "^2.8",
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"hashids/hashids": "^5.0",
|
||||
"invoiceshelf/modules": "^1.0.0",
|
||||
"jasonmccreary/laravel-test-assertions": "^v2.4",
|
||||
"laravel/framework": "^13.0",
|
||||
"laravel/helpers": "^1.7",
|
||||
"laravel/sanctum": "^4.0",
|
||||
@@ -36,6 +33,7 @@
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-ide-helper": "^3.5",
|
||||
"fakerphp/faker": "^1.23",
|
||||
"jasonmccreary/laravel-test-assertions": "^2.9",
|
||||
"laravel/boost": "^2.3",
|
||||
"laravel/pint": "^1.13",
|
||||
"laravel/sail": "^1.41",
|
||||
@@ -83,7 +81,11 @@
|
||||
"dev": [
|
||||
"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"
|
||||
]
|
||||
],
|
||||
"ide-helper": [
|
||||
"@php artisan ide-helper:generate",
|
||||
"@php artisan ide-helper:meta"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"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,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_KEY'),
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tax_types', function (Blueprint $table) {
|
||||
$table->decimal('percent', 5, 3)->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::table('taxes', function (Blueprint $table) {
|
||||
$table->decimal('percent', 5, 3)->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tax_types', function (Blueprint $table) {
|
||||
$table->decimal('percent', 5, 2)->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::table('taxes', function (Blueprint $table) {
|
||||
$table->decimal('percent', 5, 2)->nullable()->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -8,7 +8,6 @@ use App\Models\CompanySetting;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Space\InstallUtils;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Silber\Bouncer\BouncerFacade;
|
||||
|
||||
@@ -71,8 +70,5 @@ class DemoSeeder extends Seeder
|
||||
|
||||
// Mark profile setup as complete
|
||||
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
|
||||
COPY . /var/www/html
|
||||
RUN yarn && yarn build
|
||||
|
||||
FROM serversideup/php:8.4-fpm-alpine AS base
|
||||
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 pgsql
|
||||
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
|
||||
COPY . /var/www/html
|
||||
RUN yarn && yarn build
|
||||
|
||||
FROM serversideup/php:8.4-fpm-nginx-alpine AS base
|
||||
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 pgsql
|
||||
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 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
|
||||
ENV AUTORUN_ENABLED=true
|
||||
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 --chown=www-data:www-data . /var/www/html
|
||||
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"
|
||||
fi
|
||||
|
||||
echo "**** Setting up artisan permissions ****"
|
||||
echo "**** Setting up folder permissions ****"
|
||||
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
|
||||
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",
|
||||
"contact": "تواصل",
|
||||
"category": "الفئة",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "من تاريخ",
|
||||
"to_date": "حتى تاريخ",
|
||||
"expense_date": "التاريخ",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "اجمالي الاداءات",
|
||||
"pdf_tax_types_label": "أنواع الضرائب",
|
||||
"pdf_expenses_label": "النفقات",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "مطلوب من,",
|
||||
"pdf_ship_to": "يشحن إلى,",
|
||||
"pdf_received_from": "تم الاستلام من:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Měna",
|
||||
"contact": "Kontakt",
|
||||
"category": "Kategorie",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Od data",
|
||||
"to_date": "Do data",
|
||||
"expense_date": "Datum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "DANĚ CELKEM",
|
||||
"pdf_tax_types_label": "Typy daní",
|
||||
"pdf_expenses_label": "Výdaje",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Odběratel",
|
||||
"pdf_ship_to": "Příjemce",
|
||||
"pdf_received_from": "Přijato od:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Währung",
|
||||
"contact": "Kontakt",
|
||||
"category": "Kategorie",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Von Datum",
|
||||
"to_date": "bis Datum",
|
||||
"expense_date": "Datum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "Gesamte Umsatzsteuer",
|
||||
"pdf_tax_types_label": "Steuersätze",
|
||||
"pdf_expenses_label": "Ausgaben",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Rechnungsanschrift",
|
||||
"pdf_ship_to": "Lieferanschrift",
|
||||
"pdf_received_from": "Erhalten von:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Νόμισμα",
|
||||
"contact": "Επικοινωνία",
|
||||
"category": "Κατηγορία",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Από Ημερομηνία",
|
||||
"to_date": "Έως ημερομηνία",
|
||||
"expense_date": "Ημερομηνία",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "ΣΥΝΟΛΟ ΦΟΡΟΥ",
|
||||
"pdf_tax_types_label": "Φορολογική κλάση",
|
||||
"pdf_expenses_label": "Έξοδα",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Χρέωση σε,",
|
||||
"pdf_ship_to": "Αποστολή σε,",
|
||||
"pdf_received_from": "Λήψη από",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -676,6 +677,10 @@
|
||||
"no_expenses": "No expenses yet!",
|
||||
"list_of_expenses": "This section will contain the list of expenses.",
|
||||
"confirm_delete": "You will not be able to recover this Expense | You will not be able to recover these Expenses",
|
||||
"duplicate_expense": "Duplicate",
|
||||
"duplicate_expense_title": "Duplicate expense",
|
||||
"duplicate_expense_modal_hint": "Change the date if you need to. (copy) is added to the note.",
|
||||
"duplicated_message": "Expense duplicated successfully",
|
||||
"created_message": "Expense created successfully",
|
||||
"updated_message": "Expense updated successfully",
|
||||
"deleted_message": "Expense deleted successfully | Expenses deleted successfully",
|
||||
@@ -1649,6 +1654,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Divisa",
|
||||
"contact": "Contacto",
|
||||
"category": "Categoría",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Desde la fecha",
|
||||
"to_date": "Hasta la fecha",
|
||||
"expense_date": "Fecha",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "IMPUESTO TOTAL",
|
||||
"pdf_tax_types_label": "Tipos de impuestos",
|
||||
"pdf_expenses_label": "Gastos",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Cobrar a,",
|
||||
"pdf_ship_to": "Enviar a,",
|
||||
"pdf_received_from": "Recibido de:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Valuutta",
|
||||
"contact": "Yhteyshenkilö",
|
||||
"category": "Luokka",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Päivästä",
|
||||
"to_date": "Päivään",
|
||||
"expense_date": "Päivämäärä",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "ALV YHTEENSÄ",
|
||||
"pdf_tax_types_label": "ALV Verokannat",
|
||||
"pdf_expenses_label": "Kulut",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Laskutetaan,",
|
||||
"pdf_ship_to": "Toimitetaan,",
|
||||
"pdf_received_from": "Vastaanotettu:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Devise",
|
||||
"contact": "Contact",
|
||||
"category": "Catégorie",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Du",
|
||||
"to_date": "Au",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAXES",
|
||||
"pdf_tax_types_label": "Taxe",
|
||||
"pdf_expenses_label": "Dépenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Facturer à",
|
||||
"pdf_ship_to": "Expédier à",
|
||||
"pdf_received_from": "Reçu de :",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"yes": "हां",
|
||||
"no": "नहीं",
|
||||
"sort_by": "इसके अनुसार क्रमबद्ध करें",
|
||||
"ascending": "आरोही",
|
||||
"ascending": "बढ़ते क्रम में",
|
||||
"descending": "उतरते",
|
||||
"subject": "विषय",
|
||||
"body": "बॉडी",
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "मुद्रा",
|
||||
"contact": "संपर्क",
|
||||
"category": "वर्ग",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "इस तारीख से",
|
||||
"to_date": "इस तारीख तक",
|
||||
"expense_date": "दिनांक",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Valuta",
|
||||
"contact": "Kontakt",
|
||||
"category": "Kategorija",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Datum od",
|
||||
"to_date": "Datum do",
|
||||
"expense_date": "Datum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "UKUPNO POREZ",
|
||||
"pdf_tax_types_label": "Vrsta Poreza",
|
||||
"pdf_expenses_label": "Rashodi",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Račun za,",
|
||||
"pdf_ship_to": "Isporučiti za,",
|
||||
"pdf_received_from": "Poslat od strane:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Mata Uang",
|
||||
"contact": "Kontak",
|
||||
"category": "Kategori",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Dari Tanggal",
|
||||
"to_date": "Sampai Tanggal",
|
||||
"expense_date": "Tanggal",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL PAJAK",
|
||||
"pdf_tax_types_label": "Jenis Pajak",
|
||||
"pdf_expenses_label": "Pengeluaran",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Ditagih ke,",
|
||||
"pdf_ship_to": "Dikirim ke,",
|
||||
"pdf_received_from": "Diterima dari:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Valuta",
|
||||
"contact": "Contatto",
|
||||
"category": "Categoria",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Dalla Data",
|
||||
"to_date": "Alla Data",
|
||||
"expense_date": "Data",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTALE IMPOSTA",
|
||||
"pdf_tax_types_label": "Tipi di Tasse",
|
||||
"pdf_expenses_label": "Uscite",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Fattura a,",
|
||||
"pdf_ship_to": "Invia a,",
|
||||
"pdf_received_from": "Ricevuto da:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "通貨",
|
||||
"contact": "連絡先",
|
||||
"category": "カテゴリ",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "開始日",
|
||||
"to_date": "終了日",
|
||||
"expense_date": "日時",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "合計税額",
|
||||
"pdf_tax_types_label": "税の種類",
|
||||
"pdf_expenses_label": "支出",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "請求先",
|
||||
"pdf_ship_to": "配送先",
|
||||
"pdf_received_from": "受信元:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Data",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Mokesčių tipai",
|
||||
"pdf_expenses_label": "Išlaidos",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Siųsti į,",
|
||||
"pdf_received_from": "Gauta nuo:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Kontakti",
|
||||
"category": "Kategorija",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Datums no",
|
||||
"to_date": "Datums līdz",
|
||||
"expense_date": "Datums",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "NODOKĻI KOPĀ",
|
||||
"pdf_tax_types_label": "Nodokļu veidi",
|
||||
"pdf_expenses_label": "Izdevumi",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Saņēmējs,",
|
||||
"pdf_ship_to": "Piegādes adrese,",
|
||||
"pdf_received_from": "Saņemts no:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Valuta",
|
||||
"contact": "Contact",
|
||||
"category": "Categorie",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Van datum",
|
||||
"to_date": "Tot datum",
|
||||
"expense_date": "Datum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTALE BELASTINGEN",
|
||||
"pdf_tax_types_label": "Belastingtypen",
|
||||
"pdf_expenses_label": "Uitgaven",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Aan,",
|
||||
"pdf_ship_to": "Verzend naar,",
|
||||
"pdf_received_from": "Ontvangen van:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Waluta",
|
||||
"contact": "Kontakt",
|
||||
"category": "Kategoria",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Od daty",
|
||||
"to_date": "Do daty",
|
||||
"expense_date": "Data",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "CAŁKOWITY PODATEK",
|
||||
"pdf_tax_types_label": "Rodzaje podatku",
|
||||
"pdf_expenses_label": "Wydatki",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Wystawiono dla",
|
||||
"pdf_ship_to": "Wysyłka do",
|
||||
"pdf_received_from": "Otrzymane od:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Moeda",
|
||||
"contact": "Contato",
|
||||
"category": "Categoria",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "A partir da Data",
|
||||
"to_date": "Até a Data",
|
||||
"expense_date": "Data",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "IMPOSTOS TOTAIS",
|
||||
"pdf_tax_types_label": "Tipos de Impostos",
|
||||
"pdf_expenses_label": "Despesas",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Cobrar a,",
|
||||
"pdf_ship_to": "Envie a,",
|
||||
"pdf_received_from": "Remetente:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Moeda",
|
||||
"contact": "Contato",
|
||||
"category": "Categoria",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "A partir da Data",
|
||||
"to_date": "Até a Data",
|
||||
"expense_date": "Data",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "IMPOSTOS TOTAIS",
|
||||
"pdf_tax_types_label": "Tipos de Impostos",
|
||||
"pdf_expenses_label": "Despesas",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Cobrar a,",
|
||||
"pdf_ship_to": "Envie a,",
|
||||
"pdf_received_from": "Remetente:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Валюта",
|
||||
"contact": "Контакт",
|
||||
"category": "Категория",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "От даты",
|
||||
"to_date": "До даты",
|
||||
"expense_date": "Дата",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "ВСЕГО НАЛОГОВ",
|
||||
"pdf_tax_types_label": "Типы налогов",
|
||||
"pdf_expenses_label": "Расходы",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Адрес счёта,",
|
||||
"pdf_ship_to": "Адрес доставки,",
|
||||
"pdf_received_from": "Получено от:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Kontakt",
|
||||
"category": "Kategória",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Od dátumu",
|
||||
"to_date": "Do dátumu",
|
||||
"expense_date": "Dátum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "Celkové dane",
|
||||
"pdf_tax_types_label": "Typy daní",
|
||||
"pdf_expenses_label": "Výdaje",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Odberateľ:",
|
||||
"pdf_ship_to": "Doručiť do",
|
||||
"pdf_received_from": "Prijaté od:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Valuta",
|
||||
"contact": "Pišite na",
|
||||
"category": "Kategorija",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Od datuma",
|
||||
"to_date": "Do danes",
|
||||
"expense_date": "Datum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "SKUPAJ DAVEK",
|
||||
"pdf_tax_types_label": "Vrste davkov",
|
||||
"pdf_expenses_label": "Odhodki",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Račun za,",
|
||||
"pdf_ship_to": "Pošljite v,",
|
||||
"pdf_received_from": "Prejeto od:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Kontakt",
|
||||
"category": "Kategorija",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Datum od",
|
||||
"to_date": "Datum do",
|
||||
"expense_date": "Datum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "UKUPNO POREZ",
|
||||
"pdf_tax_types_label": "Tipovi Poreza",
|
||||
"pdf_expenses_label": "Rashodi",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Račun za,",
|
||||
"pdf_ship_to": "Isporučiti za,",
|
||||
"pdf_received_from": "Poslat od strane:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Valuta",
|
||||
"contact": "Kontakt",
|
||||
"category": "Kategori",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Från datum",
|
||||
"to_date": "Till datum",
|
||||
"expense_date": "Datum",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "SUMMA MOMS",
|
||||
"pdf_tax_types_label": "Momssatser",
|
||||
"pdf_expenses_label": "Utgifter",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Faktureras till,",
|
||||
"pdf_ship_to": "Skickas till,",
|
||||
"pdf_received_from": "Från:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "สกุลเงิน",
|
||||
"contact": "ติดต่อเรา",
|
||||
"category": "ประเภท",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "จากวันที่",
|
||||
"to_date": "ถึงวันที่",
|
||||
"expense_date": "วันที่",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "ภาษีทั้งหมด",
|
||||
"pdf_tax_types_label": "ประเภทภาษี",
|
||||
"pdf_expenses_label": "ค่าใช้จ่าย",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "ที่อยู่เรียกเก็บเงิน,",
|
||||
"pdf_ship_to": "ที่อยู่สำหรับจัดส่ง,",
|
||||
"pdf_received_from": "ได้รับจาก:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "İletişim",
|
||||
"category": "Kategori",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Başlangıç tarihi",
|
||||
"to_date": "Bitiş tarihi",
|
||||
"expense_date": "Tarih",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOPLAM VERGİ",
|
||||
"pdf_tax_types_label": "Vergi Türleri",
|
||||
"pdf_expenses_label": "Giderler",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Fatura Adresi,",
|
||||
"pdf_ship_to": "Teslimat Adresi,",
|
||||
"pdf_received_from": "Alındığı Kişi:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Валюта",
|
||||
"contact": "Контакт",
|
||||
"category": "Категорія",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Від дати",
|
||||
"to_date": "До дати",
|
||||
"expense_date": "Дата",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "Загальний звіт податків",
|
||||
"pdf_tax_types_label": "Типи податків",
|
||||
"pdf_expenses_label": "Витрати",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Рахунок до,",
|
||||
"pdf_ship_to": "Доставити до,",
|
||||
"pdf_received_from": "Отримано від:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Tiền tệ",
|
||||
"contact": "Tiếp xúc",
|
||||
"category": "Danh mục",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "Từ ngày",
|
||||
"to_date": "Đến nay",
|
||||
"expense_date": "Ngày",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TỔNG THUẾ",
|
||||
"pdf_tax_types_label": "Các loại thuế",
|
||||
"pdf_expenses_label": "Chi phí",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Hoa đơn để,",
|
||||
"pdf_ship_to": "Tàu,",
|
||||
"pdf_received_from": "Nhận được tư:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "Currency",
|
||||
"contact": "Contact",
|
||||
"category": "Category",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "From Date",
|
||||
"to_date": "To Date",
|
||||
"expense_date": "Date",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "TOTAL TAX",
|
||||
"pdf_tax_types_label": "Tax Types",
|
||||
"pdf_expenses_label": "Expenses",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "Bill to,",
|
||||
"pdf_ship_to": "Ship to,",
|
||||
"pdf_received_from": "Received from:",
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
"currency": "主要貨幣",
|
||||
"contact": "聯絡",
|
||||
"category": "分類",
|
||||
"uncategorized": "Uncategorized",
|
||||
"from_date": "啟始日",
|
||||
"to_date": "終止日",
|
||||
"expense_date": "日期",
|
||||
@@ -1649,6 +1650,7 @@
|
||||
"pdf_total_tax_label": "稅項總額",
|
||||
"pdf_tax_types_label": "稅收類型",
|
||||
"pdf_expenses_label": "支出",
|
||||
"pdf_expense_group_total_label": "Group total:",
|
||||
"pdf_bill_to": "帳單地址,",
|
||||
"pdf_ship_to": "送貨地址,",
|
||||
"pdf_received_from": "接收自",
|
||||
|
||||
55
package.json
55
package.json
@@ -1,64 +1,61 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=24"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"watch": "vite build --watch",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"test": "eslint ./resources/scripts --ext .js,.vue"
|
||||
"test": "eslint ./resources/scripts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rvxlab/tailwind-plugin-ios-full-height": "^1.1.0",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@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",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"postcss": "^8.4.49",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"prettier": "^3.4.2",
|
||||
"sass": "^1.83.1",
|
||||
"tailwind-scrollbar": "^3.1.0",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"tailwind-scrollbar": "^4.0.0",
|
||||
"tailwindcss": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@stripe/stripe-js": "^2.4.0",
|
||||
"@tiptap/core": "^2.11.2",
|
||||
"@tiptap/extension-link": "^2.11.2",
|
||||
"@tiptap/extension-text-align": "^2.11.2",
|
||||
"@tiptap/pm": "^2.11.2",
|
||||
"@tiptap/starter-kit": "^2.11.2",
|
||||
"@tiptap/vue-3": "^2.11.2",
|
||||
"@types/node": "^20.11.9",
|
||||
"@tiptap/core": "^3.0.0",
|
||||
"@tiptap/extension-text-align": "^3.0.0",
|
||||
"@tiptap/pm": "^3.0.0",
|
||||
"@tiptap/starter-kit": "^3.0.0",
|
||||
"@tiptap/vue-3": "^3.0.0",
|
||||
"@types/node": "^24.0.0",
|
||||
"@vuelidate/components": "^1.2.6",
|
||||
"@vuelidate/core": "^2.0.3",
|
||||
"@vuelidate/validators": "^2.0.4",
|
||||
"@vueuse/core": "^12.4.0",
|
||||
"axios": "0.30.0",
|
||||
"chart.js": "^2.9.4",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"axios": "1.14.0",
|
||||
"chart.js": "^4.5.1",
|
||||
"guid": "^0.0.12",
|
||||
"laravel-vite-plugin": "^1.1.1",
|
||||
"laravel-vite-plugin": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"maska": "^3.0.4",
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"moment": "^2.30.1",
|
||||
"path": "^0.12.7",
|
||||
"pinia": "^2.3.0",
|
||||
"pinia": "^3.0.0",
|
||||
"v-money3": "^3.24.1",
|
||||
"v-tooltip": "^4.0.0-beta.17",
|
||||
"vite": "^6.0.7",
|
||||
"vite": "^8.0.0",
|
||||
"vue": "^3.5",
|
||||
"vue-flatpickr-component": "^11.0.5",
|
||||
"vue-flatpickr-component": "^12.0.0",
|
||||
"vue-i18n": "^11.0.1",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-router": "^5.0.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);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user