mirror of
https://github.com/snipe/snipe-it.git
synced 2026-08-18 03:06:23 +00:00
Merge pull request #19431 from marcusmoore/boost
Installed Laravel Boost
This commit is contained in:
71
.ai/guidelines/snipe-it-architecture.md
Normal file
71
.ai/guidelines/snipe-it-architecture.md
Normal file
@ -0,0 +1,71 @@
|
||||
# Snipe-IT Architecture
|
||||
|
||||
## Controllers
|
||||
|
||||
Two parallel controller trees:
|
||||
|
||||
- `app/Http/Controllers/` — web/UI controllers returning Blade views.
|
||||
- `app/Http/Controllers/Api/` — REST API controllers returning JSON, consumed by datatables and select2.
|
||||
|
||||
Both trees use the same subdirectory groupings: `Assets/`, `Licenses/`, `Users/`, `Accessories/`, `Consumables/`, `Components/`, `Kits/`, `Account/`, `Auth/`.
|
||||
|
||||
## API Transformers
|
||||
|
||||
Every API controller returns data through a **Transformer** in `app/Http/Transformers/`. Never return raw model attributes from an API controller. `DatatablesTransformer` wraps paginated results.
|
||||
|
||||
```php
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
```
|
||||
|
||||
This supersedes the generic advice to reach for Eloquent API Resources — follow the existing transformer convention.
|
||||
|
||||
## Authorization
|
||||
|
||||
- All authorization goes through **Policies** in `app/Policies/`.
|
||||
- `CheckoutablePermissionsPolicy` is the base for assets, licenses, accessories, and consumables.
|
||||
- Its `checkout()` / `checkin()` methods accept `$item = null`, so `@can('checkout', \App\Models\Asset::class)` works without an instance.
|
||||
|
||||
## Routes
|
||||
|
||||
- UI routes live in `routes/web.php` **and** in the per-entity files under `routes/web/` (`hardware.php`, `users.php`, `licenses.php`, `accessories.php`, `components.php`, `consumables.php`, `kits.php`, `models.php`, `fields.php`, `locations.php`). Check both when adding or locating a UI route.
|
||||
- API routes are in `routes/api.php`.
|
||||
- Breadcrumbs are defined inline with `->breadcrumbs(fn (Trail $trail) => ...)` from `tabuna/breadcrumbs`. **Every UI route should have a breadcrumb.**
|
||||
- Some route names contain slashes rather than dots. For example, use `route('reports/unaccepted_assets')`.
|
||||
|
||||
## Full Multiple Company Support (FMCS)
|
||||
|
||||
`Setting::getSettings()->full_multiple_companies_support == '1'` gates company-scoped filtering. The select2 endpoints (`selectlist()` methods) accept a `companyId` query param:
|
||||
|
||||
```php
|
||||
if ((Setting::getSettings()->full_multiple_companies_support == '1') && ($request->filled('companyId'))) {
|
||||
$query->where('table.company_id', $request->input('companyId'));
|
||||
}
|
||||
```
|
||||
|
||||
Wire it up from Blade with `data-company-id="{{ $user->company_id }}"`.
|
||||
|
||||
## Select2 AJAX Dropdowns
|
||||
|
||||
Use `class="js-data-ajax"` with `data-endpoint="hardware|licenses|consumables|..."`. `snipeit.js` auto-initializes these, forwarding `data-company-id` as `companyId` and `data-asset-status-type` as `statusType` to the API.
|
||||
|
||||
## Checkout Redirect Flow
|
||||
|
||||
After checkout, `Helper::getRedirectOption()` reads `$request->redirect_option`. To redirect back to the assigned user, the form must set:
|
||||
|
||||
- `redirect_option=target`
|
||||
- `checkout_to_type=user`
|
||||
- `assigned_user={{ $user->id }}`
|
||||
|
||||
## Translations
|
||||
|
||||
UI strings are translation keys in `resources/lang/en-US/general.php` and its sibling files. Always add a new key rather than hard-coding English in a view.
|
||||
|
||||
## Global View Variables
|
||||
|
||||
`$snipeSettings` is shared with every view by `SettingsServiceProvider`. Use it directly in Blade — do not pass `Setting::getSettings()` from the controller.
|
||||
|
||||
## Key Helper Methods (`app/Helpers/Helper.php`)
|
||||
|
||||
- `Helper::deployableStatusLabelList()` — status labels for checkout forms.
|
||||
- `Helper::defaultChartColors(int $index = 0)` — 10-color chart palette.
|
||||
- `Helper::getRedirectOption($request, $id, $table, $item_id = null)` — post-checkout redirect logic.
|
||||
32
.ai/guidelines/snipe-it-stack.md
Normal file
32
.ai/guidelines/snipe-it-stack.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Snipe-IT Stack & Tooling
|
||||
|
||||
## Frontend Is Laravel Mix, Not Vite
|
||||
|
||||
- Assets are built with **Laravel Mix (webpack)** via `webpack.mix.js`. This project has no `vite.config.js` and no Vite manifest.
|
||||
- There is **no `npm run build` script**. Ignore any generic guidance that tells you to run it. Use:
|
||||
- `npm run dev` — development build
|
||||
- `npm run watch` — rebuild on change
|
||||
- `npm run prod` — production build
|
||||
- If the user doesn't see a frontend change, ask them to run `npm run dev` or `npm run watch`.
|
||||
|
||||
## UI Layer
|
||||
|
||||
- **AdminLTE 2 / Bootstrap 3** Blade views. There is no Inertia.
|
||||
- **Livewire v4 is installed** and used for discrete widgets in `app/Livewire` (e.g. `Importer`, `CustomFieldEditor`, `LdapSettings`). It is not the primary UI layer.
|
||||
- Default to a Blade view plus a standard controller. Only reach for Livewire when extending an existing Livewire component or when the user asks for it.
|
||||
|
||||
## Charts
|
||||
|
||||
- **Chart.js v2.9.4**, bundled at `public/js/dist/Chart.min.js`.
|
||||
- Use the **v2 API**, not v3. For example, the chart type is `horizontalBar` (v3 removed it in favor of `indexAxis`).
|
||||
- Use `Helper::defaultChartColors()` for the 10-color palette.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Clear caches after config/route changes
|
||||
php artisan optimize:clear
|
||||
|
||||
# Coverage reports (served by Laravel Herd)
|
||||
herd coverage
|
||||
```
|
||||
12
.ai/guidelines/snipe-it-testing.md
Normal file
12
.ai/guidelines/snipe-it-testing.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Snipe-IT Testing
|
||||
|
||||
- Feature tests live in `tests/Feature/`, organized by entity (e.g. `tests/Feature/Assets/AssetsTest.php`). Unit tests live in `tests/Unit/`.
|
||||
- Feature tests hit the database. The test environment uses `array` drivers for cache, session, and mail.
|
||||
- Always build test data with model factories. Check for an existing custom state before setting attributes by hand.
|
||||
- Test methods are named in snake_case: `test_page_renders()`, `test_requires_permission()`. Never camelCase.
|
||||
- UI GET routes should have both a `test_page_renders` test and a `test_requires_permission` test.
|
||||
|
||||
```bash
|
||||
php artisan test tests/Feature/Assets/AssetsTest.php # single file
|
||||
php artisan test --filter test_some_method # single method
|
||||
```
|
||||
11
.ai/rules/actions.md
Normal file
11
.ai/rules/actions.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Actions/**'
|
||||
---
|
||||
|
||||
# Actions
|
||||
|
||||
## Actions expose a single static run() method
|
||||
An Action is a class in `app/Actions/<Entity>/` named `<Verb><Entity>Action`, with one `public static function run(...)` and no constructor. Call it statically: `DestroySupplierAction::run(supplier: $supplier)`.
|
||||
|
||||
Do not use `handle()`, `execute()`, `__invoke()`, or instantiate the class.
|
||||
18
.ai/rules/api.md
Normal file
18
.ai/rules/api.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Http/Controllers/Api/**'
|
||||
---
|
||||
|
||||
# Api
|
||||
|
||||
## Wrap API responses in the standard envelope
|
||||
Every API response goes through the shared envelope:
|
||||
|
||||
`return response()->json(Helper::formatStandardApiResponse('success', $payload, trans('...')));`
|
||||
|
||||
Use `'error'` with a `null` payload for failures, and a translation key for the message. There are no Eloquent API Resources in this project.
|
||||
|
||||
## Page API lists with offset and limit
|
||||
API list endpoints page with `offset`/`limit` request params, resolved through the container as `app('api_offset_value')` and `app('api_limit_value')`, then applied with `->skip($offset)->take($limit)->get()`.
|
||||
|
||||
Do not use `paginate()`, `simplePaginate()`, or `cursorPaginate()` on API endpoints.
|
||||
9
.ai/rules/app.md
Normal file
9
.ai/rules/app.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/**'
|
||||
---
|
||||
|
||||
# App
|
||||
|
||||
## Use trans(), never __()
|
||||
Translate with `trans('admin/hardware/message.some_key')` using short dotted keys from `resources/lang/<locale>/`. Never use `__()` — it appears nowhere in this codebase. Add a new key rather than hard-coding English.
|
||||
9
.ai/rules/controllers.md
Normal file
9
.ai/rules/controllers.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Http/Controllers/**'
|
||||
---
|
||||
|
||||
# Controllers
|
||||
|
||||
## No DTOs or repository layer
|
||||
Controllers build Eloquent queries inline and pass models, collections, and arrays around. There are no DTO or repository classes — do not introduce them. Extract to an Action or a Presenter when a controller method gets heavy.
|
||||
18
.ai/rules/index.md
Normal file
18
.ai/rules/index.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Project Rules Index
|
||||
|
||||
Before planning or editing, find the row whose globs match the file's path and read that rule file.
|
||||
|
||||
| Applies to | Rule file |
|
||||
| --- | --- |
|
||||
| app/Actions/** | .ai/rules/actions.md |
|
||||
| app/Http/Controllers/Api/** | .ai/rules/api.md |
|
||||
| app/** | .ai/rules/app.md |
|
||||
| app/Http/Controllers/** | .ai/rules/controllers.md |
|
||||
| app/Livewire/** | .ai/rules/livewire.md |
|
||||
| database/migrations/** | .ai/rules/migrations.md |
|
||||
| app/Models/** | .ai/rules/models.md |
|
||||
| app/Presenters/** | .ai/rules/presenters.md |
|
||||
| app/Providers/** | .ai/rules/providers.md |
|
||||
| app/Http/Requests/** | .ai/rules/requests.md |
|
||||
| tests/** | .ai/rules/tests.md |
|
||||
| resources/views/** | .ai/rules/views.md |
|
||||
9
.ai/rules/livewire.md
Normal file
9
.ai/rules/livewire.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Livewire/**'
|
||||
---
|
||||
|
||||
# Livewire
|
||||
|
||||
## Livewire components are class-based with a separate view
|
||||
A Livewire component is a class in `app/Livewire` plus a kebab-case Blade view in `resources/views/livewire`. Livewire 4 is installed, but this project uses none of its single-file, multi-file, or Volt formats — match the class-plus-view shape.
|
||||
9
.ai/rules/migrations.md
Normal file
9
.ai/rules/migrations.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
paths:
|
||||
- 'database/migrations/**'
|
||||
---
|
||||
|
||||
# Migrations
|
||||
|
||||
## No foreign-key constraints
|
||||
Relationship columns are plain `integer('other_id')` columns (nullable and indexed as needed). Do not add `foreignId()`, `foreignIdFor()`, `constrained()`, or `->foreign()->references()` — this schema has no FK constraints and referential integrity is enforced in application code.
|
||||
22
.ai/rules/models.md
Normal file
22
.ai/rules/models.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Models/**'
|
||||
---
|
||||
|
||||
# Models
|
||||
|
||||
## Models validate themselves with watson/validating
|
||||
Models carry their own validation: `use Watson\Validating\ValidatingTrait` plus a `protected $rules` array. `$model->save()` returns false when validation fails and `$model->getErrors()` holds the messages.
|
||||
|
||||
This is a second layer on top of the Form Request, not a replacement for it.
|
||||
|
||||
## Declare a presenter on the model
|
||||
A model that renders in the UI sets `protected $presenter = \App\Presenters\<Entity>Presenter::class` and `use App\Presenters\Presentable`, exposing `$model->present()`.
|
||||
|
||||
## Share model behavior through opt-in traits
|
||||
Cross-cutting model behavior comes from traits in `app/Models/Traits`, opted into per model: `CompanyableTrait` (FMCS scoping), `Loggable` (action log), `Searchable` (API/datatable search), `Requestable`, `Acceptable`, `HasUploads`.
|
||||
|
||||
Add behavior as a trait rather than pushing it into a base class.
|
||||
|
||||
## Casts go in the $casts property
|
||||
Declare casts with `protected $casts = [...]`, not a `casts()` method, even though Laravel 12 supports the method form.
|
||||
11
.ai/rules/presenters.md
Normal file
11
.ai/rules/presenters.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Presenters/**'
|
||||
---
|
||||
|
||||
# Presenters
|
||||
|
||||
## Presenters own display and datatable config
|
||||
Display formatting and Bootstrap-table column config belong in `app/Presenters/<Entity>Presenter.php`, reached from the model via `$model->present()`.
|
||||
|
||||
Keep this logic out of controllers, transformers, and Blade.
|
||||
14
.ai/rules/providers.md
Normal file
14
.ai/rules/providers.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Providers/**'
|
||||
---
|
||||
|
||||
# Providers
|
||||
|
||||
## Named validation rules live in ValidationServiceProvider
|
||||
Add a new named validation rule as a `Validator::extend()` (or `extendImplicit()`) closure in `app/Providers/ValidationServiceProvider.php`, then reference it by its string name in `$rules`.
|
||||
|
||||
`app/Rules` is reserved for the encrypted-custom-field rule objects — do not add general-purpose rules there.
|
||||
|
||||
## Register observers in AppServiceProvider
|
||||
Wire an observer with `Model::observe(ModelObserver::class)` in `AppServiceProvider::boot()`. Do not use the `#[ObservedBy]` attribute on the model.
|
||||
16
.ai/rules/requests.md
Normal file
16
.ai/rules/requests.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
paths:
|
||||
- 'app/Http/Requests/**'
|
||||
---
|
||||
|
||||
# Requests
|
||||
|
||||
## Form Requests are the validation entry point
|
||||
Validate HTTP input with a Form Request class, not inline `$request->validate()` or `Validator::make()`.
|
||||
|
||||
Extend `App\Http\Requests\Request` and declare rules in the `protected $rules` property — the base class returns it from `rules()`. When the request handles file uploads, extend `ImageUploadRequest` instead and call `$request->handleImages($model)` in the controller.
|
||||
|
||||
## Always call parent::prepareForValidation()
|
||||
`ImageUploadRequest` inherits `prepareForValidation()` from the `ConvertsBase64ToFiles` trait, which turns base64 payloads into `UploadedFile` instances before rules run.
|
||||
|
||||
If a child request overrides `prepareForValidation()`, it MUST call `parent::prepareForValidation()` — usually first. Forgetting it silently breaks base64 image uploads with no validation error to point at. This has bitten us before.
|
||||
15
.ai/rules/tests.md
Normal file
15
.ai/rules/tests.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
paths:
|
||||
- 'tests/**'
|
||||
---
|
||||
|
||||
# Tests
|
||||
|
||||
## Database refresh comes from the base TestCase
|
||||
`Tests\TestCase` already applies `LazilyRefreshDatabase` and seeds settings via `InitializesSettings`. Do not add `RefreshDatabase`, `DatabaseTransactions`, or `DatabaseMigrations` to an individual test.
|
||||
|
||||
## Test methods are snake_case
|
||||
Name test methods in snake_case: `test_page_renders()`, `test_requires_permission()`. Never camelCase.
|
||||
|
||||
## Authenticate API tests with actingAsForApi()
|
||||
Use `$this->actingAsForApi($user)` in API tests and `$this->actingAs($user)` in UI tests.
|
||||
14
.ai/rules/views.md
Normal file
14
.ai/rules/views.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
paths:
|
||||
- 'resources/views/**'
|
||||
---
|
||||
|
||||
# Views
|
||||
|
||||
## Use trans(), never __()
|
||||
Translate with `trans('general.some_key')` using short dotted keys from `resources/lang/<locale>/`. Never use `__()` — it appears nowhere in this codebase. Add a new key rather than hard-coding English.
|
||||
|
||||
## Blade composition: layouts plus anonymous components
|
||||
Pages `@extends` a layout. Reusable markup is an anonymous Blade component: a file in `resources/views/components/` declaring `@props([...])`, used as `<x-name />`.
|
||||
|
||||
Do not create class-based components — there is no `app/View/Components` directory.
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@ -76,4 +76,21 @@ storage/ldap_client_tls.key
|
||||
/storage/framework/testing
|
||||
|
||||
/.phpunit.cache
|
||||
/.claude/
|
||||
|
||||
/.mcp.json
|
||||
/.agents
|
||||
/.amp
|
||||
/.claude
|
||||
/.codex
|
||||
/.cursor
|
||||
/.factory
|
||||
/.github/skills
|
||||
/.grok
|
||||
/.junie
|
||||
/.kiro
|
||||
/.pi
|
||||
/.zed
|
||||
AGENTS.md
|
||||
boost.json
|
||||
CLAUDE.md
|
||||
opencode.json
|
||||
|
||||
133
CLAUDE.md
133
CLAUDE.md
@ -1,133 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Stack
|
||||
|
||||
- **PHP 8.2+** / **Laravel 12** (framework), **Laravel Mix** (webpack) for frontend assets
|
||||
- **AdminLTE 2** / **Bootstrap 3** UI — Blade views. Mostly server-rendered; a handful of newer admin-settings screens use **Livewire** components (`app/Livewire/`, e.g. `LdapSettings`, `CustomFieldEditor`, `Importer`) — no Inertia.
|
||||
- **Chart.js v2.9.4** — bundled at `public/js/dist/Chart.min.js`; use `horizontalBar` type (v2 API, not v3)
|
||||
- Snipe-IT is a FOSS IT asset management system (assets, licenses, accessories, consumables, components, kits) — see README.md for product context.
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
php artisan test
|
||||
# or
|
||||
vendor/bin/phpunit
|
||||
|
||||
# Run a single test file
|
||||
php artisan test tests/Feature/Assets/AssetsTest.php
|
||||
|
||||
# Run a specific test method
|
||||
php artisan test --filter testSomeMethod
|
||||
|
||||
# Run/exclude tests by group (e.g. LDAP tests, which need the LDAP extension)
|
||||
php artisan test --group=ldap
|
||||
php artisan test --exclude-group=ldap
|
||||
|
||||
# Lint / static analysis
|
||||
vendor/bin/pint # code style (Laravel preset, see pint.json)
|
||||
vendor/bin/phpstan analyse # static analysis via Larastan, level 4 (phpstan.neon.dist)
|
||||
|
||||
# Build frontend assets (dev)
|
||||
npm run dev
|
||||
|
||||
# Build for production
|
||||
npm run prod
|
||||
|
||||
# Laravel Mix watch
|
||||
npm run watch
|
||||
|
||||
# Tinker / REPL
|
||||
php artisan tinker
|
||||
|
||||
# Clear caches after config/route changes
|
||||
php artisan optimize:clear
|
||||
```
|
||||
|
||||
Dev server is served via **Laravel Herd** (`herd coverage vendor/bin/phpunit --coverage-html tests/coverage/html` for coverage reports — see `composer.json` scripts).
|
||||
|
||||
Before running tests locally, copy `.env.testing.example` to `.env.testing`. Default test config uses in-memory SQLite (`DB_CONNECTION=sqlite_testing`); MySQL is also supported by setting the `DB_*` vars. See TESTING.md for details.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Controllers
|
||||
|
||||
Two parallel controller trees:
|
||||
- `app/Http/Controllers/` — web/UI controllers (Blade views)
|
||||
- `app/Http/Controllers/Api/` — REST API controllers (JSON, used by datatables + select2)
|
||||
|
||||
Subdirectory groupings: `Assets/`, `Licenses/`, `Users/`, `Accessories/`, `Consumables/`, `Components/`, `Kits/`, `Account/`, `Auth/`
|
||||
|
||||
### API Pattern
|
||||
|
||||
Every API controller returns data via a **Transformer** (`app/Http/Transformers/`). Never return raw model attributes from API controllers — always pass through the transformer. `DatatablesTransformer` wraps paginated results.
|
||||
|
||||
```php
|
||||
return (new AssetsTransformer)->transformAssets($assets, $assets->count());
|
||||
```
|
||||
|
||||
### Authorization
|
||||
|
||||
All authorization goes through **Policies** (`app/Policies/`). `CheckoutablePermissionsPolicy` is the base for assets/licenses/accessories/consumables — its `checkout()` / `checkin()` methods accept `$item = null` so you can use `@can('checkout', \App\Models\Asset::class)` without an instance.
|
||||
|
||||
### FMCS (Full Multiple Company Support)
|
||||
|
||||
`Setting::getSettings()->full_multiple_companies_support == '1'` gates company-scoped filtering. The select2 API endpoints (`selectlist()` methods) accept a `companyId` query param — apply it like this:
|
||||
|
||||
```php
|
||||
if ((Setting::getSettings()->full_multiple_companies_support == '1') && ($request->filled('companyId'))) {
|
||||
$query->where('table.company_id', $request->input('companyId'));
|
||||
}
|
||||
```
|
||||
|
||||
Pass `data-company-id="{{ $user->company_id }}"` in Blade to wire it to select2.
|
||||
|
||||
### Select2 AJAX Dropdowns
|
||||
|
||||
Use `class="js-data-ajax"` with `data-endpoint="hardware|licenses|consumables|..."`. `snipeit.js` auto-initializes these, forwarding `data-company-id` as `companyId` and `data-asset-status-type` as `statusType` to the API.
|
||||
|
||||
### Routes
|
||||
|
||||
All routes are in `routes/web.php` (UI) and `routes/api.php` (API). Breadcrumbs are defined inline using `->breadcrumbs(fn (Trail $trail) => ...)` from `tabuna/breadcrumbs`. Every UI route should have a breadcrumb.
|
||||
|
||||
Note: the `reports/unaccepted_assets` route is named with slashes, not dots — use `route('reports/unaccepted_assets')`.
|
||||
|
||||
### Translations
|
||||
|
||||
String keys live in `resources/lang/en-US/general.php` (and other files in that directory). Always add new UI strings as translation keys rather than hard-coding English.
|
||||
|
||||
### Checkout Redirect Flow
|
||||
|
||||
After checkout, `Helper::getRedirectOption()` reads `$request->redirect_option`. For redirecting back to the assigned user after checkout:
|
||||
- Set `redirect_option=target` in the form
|
||||
- Set `checkout_to_type=user` in the form
|
||||
- Set `assigned_user={{ $user->id }}` in the form
|
||||
|
||||
### Key Helper Methods (`app/Helpers/Helper.php`)
|
||||
|
||||
- `Helper::deployableStatusLabelList()` — status labels for checkout forms
|
||||
- `Helper::defaultChartColors()` — 10-color palette used in charts
|
||||
- `Helper::getRedirectOption($request, $id, $table)` — post-checkout redirect logic
|
||||
|
||||
### Global View Variables
|
||||
|
||||
`$snipeSettings` is injected into all views via a service provider — no need to pass `Setting::getSettings()` from every controller. Use it directly in Blade.
|
||||
|
||||
### CSV Importing
|
||||
|
||||
`app/Importer/` holds per-entity importers (`AssetImporter`, `LicenseImporter`, `AccessoryImporter`, `ComponentImporter`, `ConsumableImporter`, `AssetModelImporter`, `CategoryImporter`, `UserImporter`, etc.), all extending `Importer.php`. The `Importer` Livewire component (`app/Livewire/Importer.php`) drives the UI for CSV import.
|
||||
|
||||
### Presenters
|
||||
|
||||
`app/Presenters/` (e.g. `AssetPresenter`, `AssetModelPresenter`, `AccessoryPresenter`) define the column/field configuration consumed by the Bootstrap Table datatables on index views — check these when adding or changing a column shown in a listing.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests live in `tests/Feature/` (organized by entity) and `tests/Unit/`. Feature tests hit the database; the test environment uses `array` cache/session/mail drivers. Tests use factories for data setup. Use PHPUnit `#[Group('name')]` attributes to tag tests that need optional extensions/services (e.g. `LdapTest` is tagged `ldap`) so they can be excluded via `--exclude-group`.
|
||||
|
||||
## Contribution Policy
|
||||
|
||||
Per README.md / the project's AI Contribution Policy: PRs and issues generated by fully-automated tools without human review are not accepted upstream. If producing a PR for the upstream project (not an internal fork), treat Claude's output as a draft for human review, not a submission-ready artifact.
|
||||
@ -80,6 +80,7 @@
|
||||
"require-dev": {
|
||||
"fruitcake/laravel-debugbar": "^4.0",
|
||||
"larastan/larastan": "^3.0",
|
||||
"laravel/boost": "^2.5",
|
||||
"laravel/pint": "^1.29",
|
||||
"laravel/telescope": "^5.11",
|
||||
"mockery/mockery": "^1.4",
|
||||
@ -122,6 +123,9 @@
|
||||
"@php artisan package:discover --ansi",
|
||||
"@php artisan vendor:publish --force --tag=livewire:assets --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan boost:update --ansi"
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"php artisan key:generate"
|
||||
],
|
||||
|
||||
280
composer.lock
generated
280
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "3c0126d219147bf831e2c034cee67319",
|
||||
"content-hash": "d3d61d9b70fb976c46bafc7120c9f8e6",
|
||||
"packages": [
|
||||
{
|
||||
"name": "alek13/slack",
|
||||
@ -13149,6 +13149,146 @@
|
||||
],
|
||||
"time": "2026-04-16T10:02:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/boost",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/boost.git",
|
||||
"reference": "ae6ae78ecd053000d14204cef3c5c37aae6c97ed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/boost/zipball/ae6ae78ecd053000d14204cef3c5c37aae6c97ed",
|
||||
"reference": "ae6ae78ecd053000d14204cef3c5c37aae6c97ed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"illuminate/console": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/contracts": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/routing": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/support": "^11.45.3|^12.41.1|^13.0",
|
||||
"laravel/mcp": "^0.7.1|^0.8.0|^0.9.0",
|
||||
"laravel/prompts": "^0.3.10",
|
||||
"laravel/roster": "^1.0.0",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.27.0",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"orchestra/testbench": "^9.15.0|^10.6|^11.0",
|
||||
"pestphp/pest": "^2.36.0|^3.8.4|^4.1.5",
|
||||
"phpstan/phpstan": "^2.1.27",
|
||||
"rector/rector": "^2.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Boost\\BoostServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Boost\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.",
|
||||
"homepage": "https://github.com/laravel/boost",
|
||||
"keywords": [
|
||||
"ai",
|
||||
"dev",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/boost/issues",
|
||||
"source": "https://github.com/laravel/boost"
|
||||
},
|
||||
"time": "2026-08-05T17:03:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/mcp",
|
||||
"version": "v0.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/mcp.git",
|
||||
"reference": "a08884d79a95c5143498507aec5badf751cdbec4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/mcp/zipball/a08884d79a95c5143498507aec5badf751cdbec4",
|
||||
"reference": "a08884d79a95c5143498507aec5badf751cdbec4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"illuminate/console": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/container": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/contracts": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/http": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/json-schema": "^12.41.1|^13.0",
|
||||
"illuminate/routing": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/support": "^11.45.3|^12.41.1|^13.0",
|
||||
"illuminate/validation": "^11.45.3|^12.41.1|^13.0",
|
||||
"php": "^8.2",
|
||||
"symfony/process": "^7.4.5|^8.0.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.20",
|
||||
"orchestra/testbench": "^9.15|^10.8|^11.0",
|
||||
"pestphp/pest": "^3.8.5|^4.3.2",
|
||||
"phpstan/phpstan": "^2.1.27",
|
||||
"rector/rector": "^2.2.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"Mcp": "Laravel\\Mcp\\Facades\\Mcp"
|
||||
},
|
||||
"providers": [
|
||||
"Laravel\\Mcp\\Server\\McpServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Mcp\\": "src/",
|
||||
"Laravel\\Mcp\\Server\\": "src/Server/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Rapidly build MCP servers for your Laravel applications.",
|
||||
"homepage": "https://github.com/laravel/mcp",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"mcp"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/mcp/issues",
|
||||
"source": "https://github.com/laravel/mcp"
|
||||
},
|
||||
"time": "2026-07-21T13:23:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.29.1",
|
||||
@ -13217,6 +13357,68 @@
|
||||
},
|
||||
"time": "2026-04-20T15:26:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/roster",
|
||||
"version": "v1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/roster.git",
|
||||
"reference": "89e518bd88ae98ff50f6082f6b517c8d8e8245fa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/roster/zipball/89e518bd88ae98ff50f6082f6b517c8d8e8245fa",
|
||||
"reference": "89e518bd88ae98ff50f6082f6b517c8d8e8245fa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/semver": "^3.0",
|
||||
"illuminate/console": "^11.0|^12.0|^13.0",
|
||||
"illuminate/contracts": "^11.0|^12.0|^13.0",
|
||||
"illuminate/support": "^11.0|^12.0|^13.0",
|
||||
"php": "^8.2",
|
||||
"symfony/yaml": "^7.2|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.29",
|
||||
"mockery/mockery": "^1.6",
|
||||
"orchestra/testbench": "^9.0|^10.0|^11.0",
|
||||
"pestphp/pest": "^3.0|^4.1",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"rector/rector": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Roster\\RosterServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Roster\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Detect packages & approaches in use within a Laravel project",
|
||||
"homepage": "https://github.com/laravel/roster",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/roster/issues",
|
||||
"source": "https://github.com/laravel/roster"
|
||||
},
|
||||
"time": "2026-07-18T17:53:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sentinel",
|
||||
"version": "v1.1.0",
|
||||
@ -17084,6 +17286,82 @@
|
||||
],
|
||||
"time": "2026-04-18T13:18:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v7.4.15",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "e101850ded5d2c0d44bf32abb8996404afec2dec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/e101850ded5d2c0d44bf32abb8996404afec2dec",
|
||||
"reference": "e101850ded5d2c0d44bf32abb8996404afec2dec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/console": "<6.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"bin": [
|
||||
"Resources/bin/yaml-lint"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Yaml\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v7.4.15"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-07-21T15:13:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.3.1",
|
||||
|
||||
Reference in New Issue
Block a user