diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2b96658 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +.git +.github +.idea +.vscode + +vendor +node_modules + +.env +.env.* +!.env.example + +storage/app/* +storage/framework/cache/* +storage/framework/sessions/* +storage/framework/views/* +storage/logs/* + +ai-worker/.venv +ai-worker/__pycache__ +ai-worker/app/__pycache__ diff --git a/.env.example b/.env.example index 6466991..5ecf85c 100644 --- a/.env.example +++ b/.env.example @@ -1,67 +1,81 @@ -APP_NAME=Laravel +# --- APPLICATION --- +APP_NAME=NEXUM APP_ENV=local APP_KEY= APP_DEBUG=true -APP_URL=http://localhost - -APP_LOCALE=en -APP_FALLBACK_LOCALE=en -APP_FAKER_LOCALE=en_US - -APP_MAINTENANCE_DRIVER=file -# APP_MAINTENANCE_STORE=database - -# PHP_CLI_SERVER_WORKERS=4 - -BCRYPT_ROUNDS=12 - -LOG_CHANNEL=stack -LOG_STACK=single -LOG_DEPRECATIONS_CHANNEL=null -LOG_LEVEL=debug +APP_URL=http://localhost:8080 +APP_LOCALE=it +APP_FALLBACK_LOCALE=it +APP_FAKER_LOCALE=it_IT +# --- DATABASE (PostgreSQL) --- DB_CONNECTION=pgsql -DB_HOST=127.0.0.1 +DB_HOST=postgres DB_PORT=5432 -DB_DATABASE=poc_swe -DB_USERNAME=root -DB_PASSWORD= - -SESSION_DRIVER=database -SESSION_LIFETIME=120 -SESSION_ENCRYPT=false -SESSION_PATH=/ -SESSION_DOMAIN=null - -BROADCAST_CONNECTION=log -FILESYSTEM_DISK=local -QUEUE_CONNECTION=database - -CACHE_STORE=database -# CACHE_PREFIX= - -MEMCACHED_HOST=127.0.0.1 +DB_DATABASE=nexum +DB_USERNAME=nexum +DB_PASSWORD=nexum +# --- INFRASTRUCTURE (Redis & Queue) --- REDIS_CLIENT=phpredis -REDIS_HOST=127.0.0.1 +REDIS_HOST=redis REDIS_PASSWORD=null REDIS_PORT=6379 +CACHE_STORE=redis +QUEUE_CONNECTION=redis +REDIS_QUEUE_RETRY_AFTER=360 +SESSION_DRIVER=database -MAIL_MAILER=log -MAIL_SCHEME=null -MAIL_HOST=127.0.0.1 -MAIL_PORT=2525 +# --- MAIL (SMTP / Mailpit) --- +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null -MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_ADDRESS=noreply@nexum.local MAIL_FROM_NAME="${APP_NAME}" +# --- AWS CORE CREDENTIALS (IAM) --- AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= -AWS_DEFAULT_REGION=us-east-1 -AWS_BUCKET= -AWS_USE_PATH_STYLE_ENDPOINT=false +AWS_SESSION_TOKEN= +AWS_DEFAULT_REGION=eu-north-1 + +# --- AWS STORAGE (S3 / MinIO locale) --- +FILESYSTEM_DISK=s3 +AWS_STORAGE_ACCESS_KEY_ID=minioadmin +AWS_STORAGE_SECRET_ACCESS_KEY=minioadmin +AWS_STORAGE_SESSION_TOKEN= +AWS_BUCKET=nexum-local +AWS_ENDPOINT=http://minio:9000 +AWS_URL=http://localhost:9000/nexum-local +AWS_USE_PATH_STYLE_ENDPOINT=true + +# --- AWS BEDROCK (AI Assistant & Document Analysis) --- +BEDROCK_ENABLED=false +BEDROCK_MODEL_ID=amazon.nova-lite-v1:0 +# local = fallback PoC, bedrock = estrazione strutturata tramite Bedrock +DOCUMENT_OCR_DRIVER=local +DOCUMENT_CLASSIFIER_DRIVER=fake + +# --- AWS TEXTRACT (disattivato per la PoC) --- +TEXTRACT_ENABLED=false +TEXTRACT_AWS_REGION=eu-north-1 +TEXTRACT_S3_BUCKET= +TEXTRACT_SNS_TOPIC_ARN= +TEXTRACT_ROLE_ARN= + +# --- AI WORKER (Python FastAPI) --- +AI_WORKER_URL=http://ai-worker:8000 +AI_WORKER_PORT=8001 +AI_WORKER_ENV=local -BEDROCK_MODEL_ID=us.anthropic.claude-3-5-sonnet-20241022-v2:0 +# --- POC SETTINGS & LIMITS --- +POC_CONFIDENCE_THRESHOLD=80 +DOCUMENT_MAX_UPLOAD_MB=25 +DISPATCH_MAIL_SIMULATION=true -VITE_APP_NAME="${APP_NAME}" +# --- DOCKER / SETUP --- +NGINX_PORT=8080 +COMPOSER_INSTALL_ON_STARTUP=true +LARAVEL_AUTOMATED_SETUP=true diff --git a/.github/workflows/accessibility.yml b/.github/workflows/accessibility.yml new file mode 100644 index 0000000..ad41faf --- /dev/null +++ b/.github/workflows/accessibility.yml @@ -0,0 +1,57 @@ +name: Accessibility + +on: + push: + branches: + - develop + - main + - migration/full-blade + pull_request: + +jobs: + accessibility: + name: Axe and Pa11y + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Install Node dependencies + run: npm ci --ignore-scripts=false + + - name: Install Playwright browser + run: npx playwright install --with-deps chromium + + - name: Start application + run: docker compose up -d --build + + - name: Wait for application + run: | + for attempt in {1..120}; do + if curl --silent --fail http://localhost:8080 > /dev/null; then + exit 0 + fi + + sleep 2 + done + + docker compose logs + exit 1 + + - name: Run axe + run: node scripts/a11y/axe-playwright.mjs http://localhost:8080 http://localhost:8080/admin + + - name: Run Pa11y + run: | + npx pa11y http://localhost:8080 + npx pa11y http://localhost:8080/admin + + - name: Stop application + if: always() + run: docker compose down --volumes --remove-orphans diff --git a/.github/workflows/pest.yml b/.github/workflows/pest.yml new file mode 100644 index 0000000..64d3e29 --- /dev/null +++ b/.github/workflows/pest.yml @@ -0,0 +1,40 @@ +name: Pest + +on: + push: + branches: + - develop + - main + - migration/full-blade + pull_request: + +jobs: + pest: + name: Run test suite + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: dom, fileinfo, mbstring, pdo_sqlite, sqlite3 + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Create .env file + run: cp .env.example .env + + - name: Run Pest + env: + DOCUMENT_OCR_DRIVER: local + DOCUMENT_CLASSIFIER_DRIVER: fake + BEDROCK_ENABLED: false + run: | + php artisan config:clear + php artisan test --display-warnings diff --git a/.github/workflows/pint.yml b/.github/workflows/pint.yml new file mode 100644 index 0000000..00d2215 --- /dev/null +++ b/.github/workflows/pint.yml @@ -0,0 +1,31 @@ +name: Pint + +on: + push: + branches: + - develop + - main + - migration/full-blade + pull_request: + +jobs: + pint: + name: Check code style + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.4" + extensions: dom, fileinfo, mbstring + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Run Pint + run: ./vendor/bin/pint --test diff --git a/.gitignore b/.gitignore index 867512a..50bdfb2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /.codex /.cursor/ /.claude +/.mcp.json /.specify /.idea /.nova @@ -21,11 +22,22 @@ /public/hot /public/storage /storage/*.key +/storage/app/private/documents +/storage/app/private/livewire-tmp +/storage/app/tmp +/storage/app/public/documents +/storage/framework/poc-setup-complete /storage/pail /vendor +__pycache__/ +*.py[cod] +.venv/ CLAUDE.md +boost.json /specs _ide_helper.php Homestead.json Homestead.yaml Thumbs.db + +/documenti_ocr diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index 8c6715a..0000000 --- a/.mcp.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "mcpServers": { - "laravel-boost": { - "command": "php", - "args": [ - "artisan", - "boost:mcp" - ] - } - } -} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..14c9e15 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +.PHONY: help test fresh logs bash restart setup + +# Colori per l'output +BLUE := \033[34m +RESET := \033[0m + +help: + @echo "$(BLUE)Comandi disponibili per la PoC:$(RESET)" + @echo " $(BLUE)make test$(RESET) Esegue la suite di test (Pest)" + @echo " $(BLUE)make fresh$(RESET) Resetta database e dati generati (documenti e bozze)" + @echo " $(BLUE)make logs$(RESET) Segue i log dei container app e queue" + @echo " $(BLUE)make bash$(RESET) Entra nel container dell'applicazione" + @echo " $(BLUE)make restart$(RESET) Riavvia tutti i servizi Docker" + @echo " $(BLUE)make setup$(RESET) Costruisce l'ambiente da zero" + +test: + docker compose exec -T app ./vendor/bin/pest + +fresh: + docker compose exec app php artisan migrate:fresh --seed + docker compose exec app php artisan poc:reset-data --force + +logs: + docker compose logs -f app queue + +bash: + docker compose exec app bash + +restart: + docker compose restart + +setup: + docker compose up -d --build + @echo "$(BLUE)L'ambiente è stato configurato ed è in fase di avvio.$(RESET)" + @echo "$(BLUE)Puoi monitorare il progresso con: make logs$(RESET)" diff --git a/README.md b/README.md index 5ad1377..9dd1efb 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,114 @@ -
+# NEXUM / aLittleByte - Document Intelligence PoC -## About Laravel +Proof of Concept per validare l'uso di AI generativa e AI documentale nei flussi NEXUM. -Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: +La PoC copre due casi d'uso: -- [Simple, fast routing engine](https://laravel.com/docs/routing). -- [Powerful dependency injection container](https://laravel.com/docs/container). -- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. -- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). -- Database agnostic [schema migrations](https://laravel.com/docs/migrations). -- [Robust background job processing](https://laravel.com/docs/queues). -- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). +* generazione assistita di comunicazioni HR; +* analisi di PDF multi-destinatario, divisione in sotto-documenti ed estrazione di dati strutturati. -Laravel is accessible, powerful, and provides tools required for large, robust applications. +La demo funziona in locale anche senza servizi AI reali. Quando serve una prova più realistica, può essere collegata ad Amazon Bedrock. -## Learning Laravel +## Scope della PoC -Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. +La PoC serve a verificare: -In addition, [Laracasts](https://laracasts.com) contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. +* se un PDF con più destinatari può essere diviso automaticamente in documenti separati; +* se i dati principali dei documenti possono essere precompilati per ridurre il data-entry manuale; +* se un assistente AI può generare bozze di comunicazioni HR controllabili dall'utente; +* se l'esperienza locale è sufficientemente fluida per una demo end-to-end. -You can also watch bite-sized lessons with real-world projects on [Laravel Learn](https://laravel.com/learn), where you will be guided through building a Laravel application from scratch while learning PHP fundamentals. +Il flusso è pensato per restare utilizzabile anche quando l'estrazione dei campi non riesce: il sotto-documento resta visibile e può essere verificato manualmente. -## Agentic Development +## Stack -Laravel's predictable structure and conventions make it ideal for AI coding agents like Claude Code, Cursor, and GitHub Copilot. Install [Laravel Boost](https://laravel.com/docs/ai) to supercharge your AI workflow: +* **Laravel 12** per backend, validazione, persistenza e code. +* **Blade + CSS/JS custom** per l'interfaccia della PoC. +* **Docker, PostgreSQL, Redis e MinIO** per l'ambiente locale. +* **Amazon Bedrock** opzionale per generazione contenuti, split documentale ed estrazione dati. -```bash -composer require laravel/boost --dev +## Installazione Rapida + +1. Crea il file ambiente: + + ```bash + cp .env.example .env + ``` + +2. Avvia lo stack: + + ```bash + docker compose up -d --build + ``` + +3. Apri i servizi: -php artisan boost:install + * Applicazione: `http://localhost:8080` + * Admin PoC: `http://localhost:8080/admin` + * MinIO Console: `http://localhost:9001` + +L'entrypoint Docker installa le dipendenze, prepara Laravel e applica le migrazioni al primo avvio. + +## Configurazione + +La configurazione predefinita è adatta alla demo locale: Bedrock è disabilitato, lo split è simulato e l'estrazione OCR usa dati dimostrativi. + +```env +BEDROCK_ENABLED=false +BEDROCK_MODEL_ID=amazon.nova-lite-v1:0 +DOCUMENT_CLASSIFIER_DRIVER=fake +DOCUMENT_OCR_DRIVER=local +POC_CONFIDENCE_THRESHOLD=80 ``` -Boost provides your agent 15+ tools and skills that help agents build Laravel applications while following best practices. +Per usare Bedrock reale: -## Contributing +```env +BEDROCK_ENABLED=true +AWS_ACCESS_KEY_ID=... +AWS_SECRET_ACCESS_KEY=... +AWS_SESSION_TOKEN=... +AWS_DEFAULT_REGION=eu-north-1 +DOCUMENT_CLASSIFIER_DRIVER=bedrock +DOCUMENT_OCR_DRIVER=bedrock +``` -Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). +Valori supportati: -## Code of Conduct +* `DOCUMENT_CLASSIFIER_DRIVER`: `fake` oppure `bedrock` +* `DOCUMENT_OCR_DRIVER`: `local` oppure `bedrock` +* `BEDROCK_ENABLED`: `false` per simulazione, `true` per chiamate reali -In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). +Le stesse impostazioni possono essere gestite dalla dashboard admin: -## Security Vulnerabilities +```text +http://localhost:8080/admin +``` + +Da qui si possono cambiare modello Bedrock, driver documentali, soglia di confidenza, credenziali AWS e dati demo. Le impostazioni vengono salvate nel file `.env`. + +Le variabili Textract sono presenti in `.env.example`, ma sono disattivate per questa PoC. + +## Test + +Per eseguire la suite: -If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. +```bash +make test +``` + +Comandi utili: -## License +```bash +make fresh +make logs +docker compose exec -T app ./vendor/bin/pint --test +``` -The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). +`make fresh` resetta database e dati generati dalla PoC. `make logs` mostra i log dei container principali. diff --git a/app/Filament/Resources/CommunicationResource.php b/app/Filament/Resources/CommunicationResource.php deleted file mode 100644 index 357f04f..0000000 --- a/app/Filament/Resources/CommunicationResource.php +++ /dev/null @@ -1,131 +0,0 @@ -schema([ - Forms\Components\Textarea::make('prompt') - ->label('Contenuto richiesto') - ->required() - ->rows(4) - ->columnSpanFull(), - Forms\Components\TextInput::make('tone') - ->label('Tono') - ->required() - ->maxLength(100), - Forms\Components\TextInput::make('style') - ->label('Stile') - ->required() - ->maxLength(100), - ]); - } - - public static function infolist(Infolist $infolist): Infolist - { - return $infolist - ->schema([ - Infolists\Components\Section::make('Parametri') - ->schema([ - Infolists\Components\TextEntry::make('prompt') - ->label('Contenuto richiesto') - ->columnSpanFull(), - Infolists\Components\TextEntry::make('tone') - ->label('Tono'), - Infolists\Components\TextEntry::make('style') - ->label('Stile'), - Infolists\Components\TextEntry::make('status') - ->label('Stato') - ->badge() - ->color(fn (CommunicationStatus $state): string => $state->color()), - ]) - ->columns(2), - Infolists\Components\Section::make('Testo generato') - ->schema([ - Infolists\Components\TextEntry::make('generated_title') - ->label('Titolo') - ->columnSpanFull(), - Infolists\Components\TextEntry::make('generated_body') - ->label('Corpo') - ->columnSpanFull(), - ]), - ]); - } - - public static function table(Table $table): Table - { - return $table - ->columns([ - Tables\Columns\TextColumn::make('generated_title') - ->label('Titolo') - ->limit(60) - ->searchable(), - Tables\Columns\TextColumn::make('tone') - ->label('Tono') - ->searchable(), - Tables\Columns\TextColumn::make('style') - ->label('Stile') - ->searchable(), - Tables\Columns\TextColumn::make('status') - ->label('Stato') - ->badge() - ->color(fn (CommunicationStatus $state): string => $state->color()), - Tables\Columns\TextColumn::make('created_at') - ->label('Creata il') - ->dateTime('d/m/Y H:i') - ->sortable(), - ]) - ->defaultSort('created_at', 'desc') - ->filters([ - Tables\Filters\SelectFilter::make('status') - ->label('Stato') - ->options(CommunicationStatus::class), - ]) - ->actions([ - Tables\Actions\ViewAction::make(), - ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), - ]), - ]); - } - - public static function getRelations(): array - { - return []; - } - - public static function getPages(): array - { - return [ - 'index' => Pages\ListCommunications::route('/'), - 'create' => Pages\CreateCommunication::route('/create'), - 'view' => Pages\ViewCommunication::route('/{record}'), - ]; - } -} diff --git a/app/Filament/Resources/CommunicationResource/Pages/CreateCommunication.php b/app/Filament/Resources/CommunicationResource/Pages/CreateCommunication.php deleted file mode 100644 index d5852f6..0000000 --- a/app/Filament/Resources/CommunicationResource/Pages/CreateCommunication.php +++ /dev/null @@ -1,30 +0,0 @@ -generateCommunication($data['prompt'], $data['tone'], $data['style']); - - return static::getModel()::create([ - 'prompt' => $data['prompt'], - 'tone' => $data['tone'], - 'style' => $data['style'], - 'generated_title' => $generated['title'], - 'generated_body' => $generated['body'], - 'status' => CommunicationStatus::Draft, - ]); - } -} diff --git a/app/Filament/Resources/CommunicationResource/Pages/ListCommunications.php b/app/Filament/Resources/CommunicationResource/Pages/ListCommunications.php deleted file mode 100644 index 9f7ae7a..0000000 --- a/app/Filament/Resources/CommunicationResource/Pages/ListCommunications.php +++ /dev/null @@ -1,19 +0,0 @@ -label('Scarta') - ->color('danger') - ->icon('heroicon-o-trash') - ->requiresConfirmation() - ->modalHeading('Scarta comunicazione') - ->modalDescription('Sei sicuro di voler scartare questa comunicazione? L\'azione non può essere annullata.') - ->visible(fn () => $this->record->status === CommunicationStatus::Draft) - ->action(function () { - $this->record->update(['status' => CommunicationStatus::Discarded]); - $this->refreshFormData(['status']); - }), - ]; - } -} diff --git a/app/Filament/Resources/OriginalDocumentResource.php b/app/Filament/Resources/OriginalDocumentResource.php deleted file mode 100644 index 9964420..0000000 --- a/app/Filament/Resources/OriginalDocumentResource.php +++ /dev/null @@ -1,132 +0,0 @@ -schema([]); - } - - public static function infolist(Infolist $infolist): Infolist - { - return $infolist - ->schema([ - Infolists\Components\Section::make('Documento originale') - ->schema([ - Infolists\Components\TextEntry::make('original_filename') - ->label('Nome file'), - Infolists\Components\TextEntry::make('processing_status') - ->label('Stato elaborazione') - ->badge() - ->color(fn (ProcessingStatus $state): string => $state->color()), - Infolists\Components\TextEntry::make('created_at') - ->label('Caricato il') - ->dateTime('d/m/Y H:i'), - ]) - ->columns(3), - ]); - } - - public static function table(Table $table): Table - { - return $table - ->columns([ - Tables\Columns\TextColumn::make('original_filename') - ->label('Nome file') - ->searchable(), - Tables\Columns\TextColumn::make('processing_status') - ->label('Stato') - ->badge() - ->color(fn (ProcessingStatus $state): string => $state->color()), - Tables\Columns\TextColumn::make('sub_documents_count') - ->label('Sotto-documenti') - ->counts('subDocuments'), - Tables\Columns\TextColumn::make('created_at') - ->label('Caricato il') - ->dateTime('d/m/Y H:i') - ->sortable(), - ]) - ->defaultSort('created_at', 'desc') - ->headerActions([ - Tables\Actions\Action::make('upload') - ->label('Carica PDF') - ->icon('heroicon-o-arrow-up-tray') - ->modalHeading('Carica documento PDF') - ->form([ - Forms\Components\FileUpload::make('pdf_file') - ->label('File PDF') - ->acceptedFileTypes(['application/pdf']) - ->required() - ->disk('local') - ->directory('documents/originals'), - ]) - ->action(function (array $data) { - $service = app(DocumentProcessingService::class); - $file = new UploadedFile( - Storage::disk('local')->path($data['pdf_file']), - basename($data['pdf_file']), - 'application/pdf', - null, - true, - ); - $service->handleUpload($file); - }), - ]) - ->filters([ - Tables\Filters\SelectFilter::make('processing_status') - ->label('Stato') - ->options(ProcessingStatus::class), - ]) - ->actions([ - Tables\Actions\ViewAction::make(), - ]) - ->bulkActions([ - Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), - ]), - ]); - } - - public static function getRelations(): array - { - return [ - SubDocumentsRelationManager::class, - ]; - } - - public static function getPages(): array - { - return [ - 'index' => Pages\ListOriginalDocuments::route('/'), - 'view' => Pages\ViewOriginalDocument::route('/{record}'), - 'view-sub-document' => Pages\ViewSubDocument::route('/sub-documents/{record}'), - ]; - } -} diff --git a/app/Filament/Resources/OriginalDocumentResource/Pages/ListOriginalDocuments.php b/app/Filament/Resources/OriginalDocumentResource/Pages/ListOriginalDocuments.php deleted file mode 100644 index 1a51e44..0000000 --- a/app/Filament/Resources/OriginalDocumentResource/Pages/ListOriginalDocuments.php +++ /dev/null @@ -1,19 +0,0 @@ -subDocument = SubDocument::with(['extractedData', 'originalDocument'])->findOrFail($record); - } - - protected function getHeaderActions(): array - { - return [ - Actions\Action::make('back') - ->label('Torna al documento') - ->icon('heroicon-o-arrow-left') - ->url(fn () => OriginalDocumentResource::getUrl('view', ['record' => $this->subDocument->original_document_id])), - Actions\Action::make('send') - ->label('Invia') - ->icon('heroicon-o-paper-airplane') - ->color('success') - ->requiresConfirmation() - ->modalHeading('Invia documento') - ->modalDescription('Confermi l\'invio di questo sotto-documento al dipendente?') - ->visible(fn () => $this->subDocument->send_status === SendStatus::Pending) - ->action(function () { - $this->subDocument->update(['send_status' => SendStatus::Sent]); - $this->subDocument->refresh(); - }), - ]; - } - - public function infolist(Infolist $infolist): Infolist - { - return $infolist - ->record($this->subDocument->extractedData) - ->schema([ - Infolists\Components\Grid::make(2) - ->schema([ - Infolists\Components\Section::make('Dati estratti') - ->schema([ - Infolists\Components\TextEntry::make('employee_first_name') - ->label('Nome'), - Infolists\Components\TextEntry::make('employee_last_name') - ->label('Cognome'), - Infolists\Components\TextEntry::make('company_name') - ->label('Azienda'), - Infolists\Components\TextEntry::make('document_date') - ->label('Data documento') - ->date('d/m/Y'), - Infolists\Components\TextEntry::make('document_type') - ->label('Tipo documento'), - Infolists\Components\TextEntry::make('confidence_score') - ->label('Confidenza') - ->suffix('%'), - Infolists\Components\TextEntry::make('description') - ->label('Descrizione') - ->columnSpanFull(), - ]) - ->columns(2), - Infolists\Components\Section::make('Documento') - ->schema([ - Infolists\Components\TextEntry::make('subDocument.send_status') - ->label('Stato invio') - ->state(fn () => $this->subDocument->send_status) - ->badge() - ->color(fn (SendStatus $state): string => $state->color()), - Infolists\Components\TextEntry::make('pages') - ->label('Pagine') - ->state(fn () => "{$this->subDocument->start_page}–{$this->subDocument->end_page}"), - ]), - ]), - ]); - } -} diff --git a/app/Filament/Resources/OriginalDocumentResource/RelationManagers/SubDocumentsRelationManager.php b/app/Filament/Resources/OriginalDocumentResource/RelationManagers/SubDocumentsRelationManager.php deleted file mode 100644 index 1e85ca0..0000000 --- a/app/Filament/Resources/OriginalDocumentResource/RelationManagers/SubDocumentsRelationManager.php +++ /dev/null @@ -1,51 +0,0 @@ -schema([]); - } - - public function table(Table $table): Table - { - return $table - ->columns([ - Tables\Columns\TextColumn::make('id') - ->label('#') - ->sortable(), - Tables\Columns\TextColumn::make('extractedData.employee_first_name') - ->label('Nome') - ->default('—'), - Tables\Columns\TextColumn::make('extractedData.employee_last_name') - ->label('Cognome') - ->default('—'), - Tables\Columns\TextColumn::make('start_page') - ->label('Pagine') - ->formatStateUsing(fn ($record) => "{$record->start_page}–{$record->end_page}"), - Tables\Columns\TextColumn::make('send_status') - ->label('Invio') - ->badge() - ->color(fn (SendStatus $state): string => $state->color()), - ]) - ->actions([ - Tables\Actions\Action::make('view') - ->label('Visualizza') - ->icon('heroicon-o-eye') - ->url(fn ($record) => OriginalDocumentResource::getUrl('view-sub-document', ['record' => $record->id])), - ]); - } -} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php deleted file mode 100644 index 8677cd5..0000000 --- a/app/Http/Controllers/Controller.php +++ /dev/null @@ -1,8 +0,0 @@ - */ - use HasFactory, Notifiable; - - /** - * The attributes that are mass assignable. - * - * @var array