Sfoglia il codice sorgente

feat: :sparkles: feat (relatorios) criada aba relatorios do layout adm

foi criada a aba relatorios com 3 relatorios disponiveis tanto em tabela quanto em excel

fase:dev | origin:escopo
Gustavo Zanatta 3 settimane fa
parent
commit
e99c20b37d

+ 373 - 151
_ide_helper.php

@@ -5135,7 +5135,7 @@ namespace Illuminate\Support\Facades {
          */
         public static function lock($name, $seconds = 0, $owner = null)
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
             return $instance->lock($name, $seconds, $owner);
         }
 
@@ -5149,96 +5149,60 @@ namespace Illuminate\Support\Facades {
          */
         public static function restoreLock($name, $owner)
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
             return $instance->restoreLock($name, $owner);
         }
 
         /**
-         * Remove all items from the cache.
+         * Remove an item from the cache if it is expired.
          *
+         * @param string $key
          * @return bool
          * @static
          */
-        public static function flush()
+        public static function forgetIfExpired($key)
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
-            return $instance->flush();
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
+            return $instance->forgetIfExpired($key);
         }
 
         /**
-         * Remove all expired tag set entries.
-         *
-         * @return void
-         * @static
-         */
-        public static function flushStaleTags()
-        {
-            /** @var \Illuminate\Cache\RedisStore $instance */
-            $instance->flushStaleTags();
-        }
-
-        /**
-         * Get the Redis connection instance.
-         *
-         * @return \Illuminate\Redis\Connections\Connection
-         * @static
-         */
-        public static function connection()
-        {
-            /** @var \Illuminate\Cache\RedisStore $instance */
-            return $instance->connection();
-        }
-
-        /**
-         * Get the Redis connection instance that should be used to manage locks.
+         * Remove all items from the cache.
          *
-         * @return \Illuminate\Redis\Connections\Connection
+         * @return bool
          * @static
          */
-        public static function lockConnection()
+        public static function flush()
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
-            return $instance->lockConnection();
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
+            return $instance->flush();
         }
 
         /**
-         * Specify the name of the connection that should be used to store data.
+         * Get the underlying database connection.
          *
-         * @param string $connection
-         * @return void
+         * @return \Illuminate\Database\PostgresConnection
          * @static
          */
-        public static function setConnection($connection)
+        public static function getConnection()
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
-            $instance->setConnection($connection);
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
+            return $instance->getConnection();
         }
 
         /**
          * Specify the name of the connection that should be used to manage locks.
          *
-         * @param string $connection
-         * @return \Illuminate\Cache\RedisStore
+         * @param \Illuminate\Database\ConnectionInterface $connection
+         * @return \Illuminate\Cache\DatabaseStore
          * @static
          */
         public static function setLockConnection($connection)
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
             return $instance->setLockConnection($connection);
         }
 
-        /**
-         * Get the Redis database instance.
-         *
-         * @return \Illuminate\Contracts\Redis\Factory
-         * @static
-         */
-        public static function getRedis()
-        {
-            /** @var \Illuminate\Cache\RedisStore $instance */
-            return $instance->getRedis();
-        }
-
         /**
          * Get the cache key prefix.
          *
@@ -5247,7 +5211,7 @@ namespace Illuminate\Support\Facades {
          */
         public static function getPrefix()
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
             return $instance->getPrefix();
         }
 
@@ -5260,7 +5224,7 @@ namespace Illuminate\Support\Facades {
          */
         public static function setPrefix($prefix)
         {
-            /** @var \Illuminate\Cache\RedisStore $instance */
+            /** @var \Illuminate\Cache\DatabaseStore $instance */
             $instance->setPrefix($prefix);
         }
 
@@ -22818,207 +22782,414 @@ namespace Illuminate\Support\Facades {
             }
     }
 
-namespace Illuminate\Http {
+namespace Maatwebsite\Excel\Facades {
     /**
      */
-    class Request extends \Symfony\Component\HttpFoundation\Request {
+    class Excel {
         /**
-         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestValidation()
-         * @param array $rules
-         * @param mixed $params
+         * @param object $export
+         * @param string|null $fileName
+         * @param string $writerType
+         * @param array $headers
+         * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
+         * @throws \PhpOffice\PhpSpreadsheet\Exception
+         * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
          * @static
          */
-        public static function validate($rules, ...$params)
+        public static function download($export, $fileName, $writerType = null, $headers = [])
         {
-            return \Illuminate\Http\Request::validate($rules, ...$params);
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->download($export, $fileName, $writerType, $headers);
         }
 
         /**
-         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestValidation()
-         * @param string $errorBag
-         * @param array $rules
-         * @param mixed $params
+         * @param string|null $disk Fallback for usage with named properties
+         * @param object $export
+         * @param string $filePath
+         * @param string|null $diskName
+         * @param string $writerType
+         * @param mixed $diskOptions
+         * @return bool
+         * @throws \PhpOffice\PhpSpreadsheet\Exception
+         * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
          * @static
          */
-        public static function validateWithBag($errorBag, $rules, ...$params)
+        public static function store($export, $filePath, $diskName = null, $writerType = null, $diskOptions = [], $disk = null)
         {
-            return \Illuminate\Http\Request::validateWithBag($errorBag, $rules, ...$params);
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->store($export, $filePath, $diskName, $writerType, $diskOptions, $disk);
         }
 
         /**
-         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
-         * @param mixed $absolute
+         * @param object $export
+         * @param string $filePath
+         * @param string|null $disk
+         * @param string $writerType
+         * @param mixed $diskOptions
+         * @return \Illuminate\Foundation\Bus\PendingDispatch
          * @static
          */
-        public static function hasValidSignature($absolute = true)
+        public static function queue($export, $filePath, $disk = null, $writerType = null, $diskOptions = [])
         {
-            return \Illuminate\Http\Request::hasValidSignature($absolute);
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->queue($export, $filePath, $disk, $writerType, $diskOptions);
         }
 
         /**
-         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
+         * @param object $export
+         * @param string $writerType
+         * @return string
          * @static
          */
-        public static function hasValidRelativeSignature()
+        public static function raw($export, $writerType)
         {
-            return \Illuminate\Http\Request::hasValidRelativeSignature();
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->raw($export, $writerType);
         }
 
         /**
-         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
-         * @param mixed $ignoreQuery
-         * @param mixed $absolute
+         * @param object $import
+         * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
+         * @param string|null $disk
+         * @param string|null $readerType
+         * @return \Maatwebsite\Excel\Reader|\Illuminate\Foundation\Bus\PendingDispatch
          * @static
          */
-        public static function hasValidSignatureWhileIgnoring($ignoreQuery = [], $absolute = true)
+        public static function import($import, $filePath, $disk = null, $readerType = null)
         {
-            return \Illuminate\Http\Request::hasValidSignatureWhileIgnoring($ignoreQuery, $absolute);
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->import($import, $filePath, $disk, $readerType);
         }
 
         /**
-         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
-         * @param mixed $ignoreQuery
+         * @param object $import
+         * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
+         * @param string|null $disk
+         * @param string|null $readerType
+         * @return array
          * @static
          */
-        public static function hasValidRelativeSignatureWhileIgnoring($ignoreQuery = [])
+        public static function toArray($import, $filePath, $disk = null, $readerType = null)
         {
-            return \Illuminate\Http\Request::hasValidRelativeSignatureWhileIgnoring($ignoreQuery);
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->toArray($import, $filePath, $disk, $readerType);
         }
 
-            }
-    }
+        /**
+         * @param object $import
+         * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
+         * @param string|null $disk
+         * @param string|null $readerType
+         * @return \Illuminate\Support\Collection
+         * @static
+         */
+        public static function toCollection($import, $filePath, $disk = null, $readerType = null)
+        {
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->toCollection($import, $filePath, $disk, $readerType);
+        }
 
-namespace Illuminate\Database\Schema {
-    /**
-     */
-    class Blueprint {
         /**
-         * @see \Kalnoy\Nestedset\NestedSetServiceProvider::register()
+         * @param \Illuminate\Contracts\Queue\ShouldQueue $import
+         * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
+         * @param string|null $disk
+         * @param string $readerType
+         * @return \Illuminate\Foundation\Bus\PendingDispatch
          * @static
          */
-        public static function nestedSet()
+        public static function queueImport($import, $filePath, $disk = null, $readerType = null)
         {
-            return \Illuminate\Database\Schema\Blueprint::nestedSet();
+            /** @var \Maatwebsite\Excel\Excel $instance */
+            return $instance->queueImport($import, $filePath, $disk, $readerType);
         }
 
         /**
-         * @see \Kalnoy\Nestedset\NestedSetServiceProvider::register()
+         * Register a custom macro.
+         *
+         * @param string $name
+         * @param object|callable $macro
+         * @param-closure-this static  $macro
+         * @return void
          * @static
          */
-        public static function dropNestedSet()
+        public static function macro($name, $macro)
         {
-            return \Illuminate\Database\Schema\Blueprint::dropNestedSet();
+            \Maatwebsite\Excel\Excel::macro($name, $macro);
         }
 
         /**
-         * @see \Pgvector\Laravel\Schema::register()
-         * @param string $column
-         * @param mixed|null $dimensions
-         * @return \Illuminate\Database\Schema\ColumnDefinition
+         * Mix another object into the class.
+         *
+         * @param object $mixin
+         * @param bool $replace
+         * @return void
+         * @throws \ReflectionException
          * @static
          */
-        public static function halfvec($column, $dimensions = null)
+        public static function mixin($mixin, $replace = true)
         {
-            return \Illuminate\Database\Schema\Blueprint::halfvec($column, $dimensions);
+            \Maatwebsite\Excel\Excel::mixin($mixin, $replace);
         }
 
         /**
-         * @see \Pgvector\Laravel\Schema::register()
-         * @param string $column
-         * @param mixed|null $length
-         * @return \Illuminate\Database\Schema\ColumnDefinition
+         * Checks if macro is registered.
+         *
+         * @param string $name
+         * @return bool
          * @static
          */
-        public static function bit($column, $length = null)
+        public static function hasMacro($name)
         {
-            return \Illuminate\Database\Schema\Blueprint::bit($column, $length);
+            return \Maatwebsite\Excel\Excel::hasMacro($name);
         }
 
         /**
-         * @see \Pgvector\Laravel\Schema::register()
-         * @param string $column
-         * @param mixed|null $dimensions
-         * @return \Illuminate\Database\Schema\ColumnDefinition
+         * Flush the existing macros.
+         *
+         * @return void
+         * @static
+         */
+        public static function flushMacros()
+        {
+            \Maatwebsite\Excel\Excel::flushMacros();
+        }
+
+        /**
+         * @param string $concern
+         * @param callable $handler
+         * @param string $event
+         * @static
+         */
+        public static function extend($concern, $handler, $event = 'Maatwebsite\\Excel\\Events\\BeforeWriting')
+        {
+            return \Maatwebsite\Excel\Excel::extend($concern, $handler, $event);
+        }
+
+        /**
+         * When asserting downloaded, stored, queued or imported, use regular expression
+         * to look for a matching file path.
+         *
+         * @return void
+         * @static
+         */
+        public static function matchByRegex()
+        {
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            $instance->matchByRegex();
+        }
+
+        /**
+         * When asserting downloaded, stored, queued or imported, use regular string
+         * comparison for matching file path.
+         *
+         * @return void
          * @static
          */
-        public static function sparsevec($column, $dimensions = null)
+        public static function doNotMatchByRegex()
         {
-            return \Illuminate\Database\Schema\Blueprint::sparsevec($column, $dimensions);
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            $instance->doNotMatchByRegex();
+        }
+
+        /**
+         * @param string $fileName
+         * @param callable|null $callback
+         * @static
+         */
+        public static function assertDownloaded($fileName, $callback = null)
+        {
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            return $instance->assertDownloaded($fileName, $callback);
+        }
+
+        /**
+         * @param string $filePath
+         * @param string|callable|null $disk
+         * @param callable|null $callback
+         * @static
+         */
+        public static function assertStored($filePath, $disk = null, $callback = null)
+        {
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            return $instance->assertStored($filePath, $disk, $callback);
+        }
+
+        /**
+         * @param string $filePath
+         * @param string|callable|null $disk
+         * @param callable|null $callback
+         * @static
+         */
+        public static function assertQueued($filePath, $disk = null, $callback = null)
+        {
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            return $instance->assertQueued($filePath, $disk, $callback);
+        }
+
+        /**
+         * @static
+         */
+        public static function assertQueuedWithChain($chain)
+        {
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            return $instance->assertQueuedWithChain($chain);
+        }
+
+        /**
+         * @param string $classname
+         * @param callable|null $callback
+         * @static
+         */
+        public static function assertExportedInRaw($classname, $callback = null)
+        {
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            return $instance->assertExportedInRaw($classname, $callback);
+        }
+
+        /**
+         * @param string $filePath
+         * @param string|callable|null $disk
+         * @param callable|null $callback
+         * @static
+         */
+        public static function assertImported($filePath, $disk = null, $callback = null)
+        {
+            /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
+            return $instance->assertImported($filePath, $disk, $callback);
         }
 
             }
     }
 
-namespace Illuminate\Database\Schema\Grammars {
+namespace Illuminate\Support {
     /**
+     * @template TKey of array-key
+     * @template-covariant TValue
+     * @implements \ArrayAccess<TKey, TValue>
+     * @implements \Illuminate\Support\Enumerable<TKey, TValue>
      */
-    class PostgresGrammar extends \Illuminate\Database\Schema\Grammars\Grammar {
-            }
-    /**
-     */
-    class Grammar extends \Illuminate\Database\Grammar {
+    class Collection {
+        /**
+         * @see \Maatwebsite\Excel\Mixins\DownloadCollectionMixin::downloadExcel()
+         * @param string $fileName
+         * @param string|null $writerType
+         * @param mixed $withHeadings
+         * @param array $responseHeaders
+         * @static
+         */
+        public static function downloadExcel($fileName, $writerType = null, $withHeadings = false, $responseHeaders = [])
+        {
+            return \Illuminate\Support\Collection::downloadExcel($fileName, $writerType, $withHeadings, $responseHeaders);
+        }
+
+        /**
+         * @see \Maatwebsite\Excel\Mixins\StoreCollectionMixin::storeExcel()
+         * @param string $filePath
+         * @param string|null $disk
+         * @param string|null $writerType
+         * @param mixed $withHeadings
+         * @static
+         */
+        public static function storeExcel($filePath, $disk = null, $writerType = null, $withHeadings = false)
+        {
+            return \Illuminate\Support\Collection::storeExcel($filePath, $disk, $writerType, $withHeadings);
+        }
+
             }
     }
 
-namespace Illuminate\Database {
+namespace Illuminate\Http {
     /**
      */
-    class Grammar {
+    class Request extends \Symfony\Component\HttpFoundation\Request {
         /**
-         * @see \Pgvector\Laravel\Schema::register()
-         * @param \Illuminate\Database\Schema\ColumnDefinition $column
-         * @return string
+         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestValidation()
+         * @param array $rules
+         * @param mixed $params
          * @static
          */
-        public static function typeVector($column)
+        public static function validate($rules, ...$params)
         {
-            return \Illuminate\Database\Grammar::typeVector($column);
+            return \Illuminate\Http\Request::validate($rules, ...$params);
         }
 
         /**
-         * @see \Pgvector\Laravel\Schema::register()
-         * @param \Illuminate\Database\Schema\ColumnDefinition $column
-         * @return string
+         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestValidation()
+         * @param string $errorBag
+         * @param array $rules
+         * @param mixed $params
          * @static
          */
-        public static function typeHalfvec($column)
+        public static function validateWithBag($errorBag, $rules, ...$params)
         {
-            return \Illuminate\Database\Grammar::typeHalfvec($column);
+            return \Illuminate\Http\Request::validateWithBag($errorBag, $rules, ...$params);
         }
 
         /**
-         * @see \Pgvector\Laravel\Schema::register()
-         * @param \Illuminate\Database\Schema\ColumnDefinition $column
-         * @return string
+         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
+         * @param mixed $absolute
          * @static
          */
-        public static function typeBit($column)
+        public static function hasValidSignature($absolute = true)
         {
-            return \Illuminate\Database\Grammar::typeBit($column);
+            return \Illuminate\Http\Request::hasValidSignature($absolute);
         }
 
         /**
-         * @see \Pgvector\Laravel\Schema::register()
-         * @param \Illuminate\Database\Schema\ColumnDefinition $column
-         * @return string
+         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
          * @static
          */
-        public static function typeSparsevec($column)
+        public static function hasValidRelativeSignature()
         {
-            return \Illuminate\Database\Grammar::typeSparsevec($column);
+            return \Illuminate\Http\Request::hasValidRelativeSignature();
+        }
+
+        /**
+         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
+         * @param mixed $ignoreQuery
+         * @param mixed $absolute
+         * @static
+         */
+        public static function hasValidSignatureWhileIgnoring($ignoreQuery = [], $absolute = true)
+        {
+            return \Illuminate\Http\Request::hasValidSignatureWhileIgnoring($ignoreQuery, $absolute);
+        }
+
+        /**
+         * @see \Illuminate\Foundation\Providers\FoundationServiceProvider::registerRequestSignatureValidation()
+         * @param mixed $ignoreQuery
+         * @static
+         */
+        public static function hasValidRelativeSignatureWhileIgnoring($ignoreQuery = [])
+        {
+            return \Illuminate\Http\Request::hasValidRelativeSignatureWhileIgnoring($ignoreQuery);
         }
 
             }
     }
 
-namespace Illuminate\Database\Query\Grammars {
-    /**
-     */
-    class PostgresGrammar extends \Illuminate\Database\Query\Grammars\Grammar {
-            }
+namespace Illuminate\Database\Schema {
     /**
      */
-    class Grammar extends \Illuminate\Database\Grammar {
+    class Blueprint {
+        /**
+         * @see \Kalnoy\Nestedset\NestedSetServiceProvider::register()
+         * @static
+         */
+        public static function nestedSet()
+        {
+            return \Illuminate\Database\Schema\Blueprint::nestedSet();
+        }
+
+        /**
+         * @see \Kalnoy\Nestedset\NestedSetServiceProvider::register()
+         * @static
+         */
+        public static function dropNestedSet()
+        {
+            return \Illuminate\Database\Schema\Blueprint::dropNestedSet();
+        }
+
             }
     }
 
@@ -25020,6 +25191,56 @@ namespace  {
             return $instance->mergeConstraintsFrom($from);
         }
 
+        /**
+         * @see \Maatwebsite\Excel\Mixins\DownloadQueryMacro::__invoke()
+         * @param string $fileName
+         * @param string|null $writerType
+         * @param mixed $withHeadings
+         * @static
+         */
+        public static function downloadExcel($fileName, $writerType = null, $withHeadings = false)
+        {
+            return \Illuminate\Database\Eloquent\Builder::downloadExcel($fileName, $writerType, $withHeadings);
+        }
+
+        /**
+         * @see \Maatwebsite\Excel\Mixins\StoreQueryMacro::__invoke()
+         * @param string $filePath
+         * @param string|null $disk
+         * @param string|null $writerType
+         * @param mixed $withHeadings
+         * @static
+         */
+        public static function storeExcel($filePath, $disk = null, $writerType = null, $withHeadings = false)
+        {
+            return \Illuminate\Database\Eloquent\Builder::storeExcel($filePath, $disk, $writerType, $withHeadings);
+        }
+
+        /**
+         * @see \Maatwebsite\Excel\Mixins\ImportMacro::__invoke()
+         * @param string $filename
+         * @param string|null $disk
+         * @param string|null $readerType
+         * @static
+         */
+        public static function import($filename, $disk = null, $readerType = null)
+        {
+            return \Illuminate\Database\Eloquent\Builder::import($filename, $disk, $readerType);
+        }
+
+        /**
+         * @see \Maatwebsite\Excel\Mixins\ImportAsMacro::__invoke()
+         * @param string $filename
+         * @param callable $mapping
+         * @param string|null $disk
+         * @param string|null $readerType
+         * @static
+         */
+        public static function importAs($filename, $mapping, $disk = null, $readerType = null)
+        {
+            return \Illuminate\Database\Eloquent\Builder::importAs($filename, $mapping, $disk, $readerType);
+        }
+
         /**
          * Set the columns to be selected.
          *
@@ -27823,6 +28044,7 @@ namespace  {
     class Validator extends \Illuminate\Support\Facades\Validator {}
     class View extends \Illuminate\Support\Facades\View {}
     class Vite extends \Illuminate\Support\Facades\Vite {}
+    class Excel extends \Maatwebsite\Excel\Facades\Excel {}
 }
 
 

+ 56 - 0
app/Exports/ContatosAssociadosExport.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace App\Exports;
+
+use App\Enums\UserTypeEnum;
+use App\Models\User;
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithHeadings;
+use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithColumnWidths;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use Illuminate\Support\Collection;
+
+class ContatosAssociadosExport implements FromCollection, WithHeadings, WithStyles, WithColumnWidths, WithTitle
+{
+    public function collection(): Collection
+    {
+        return User::where('type', UserTypeEnum::ASSOCIADO)
+            ->orderBy('name', 'asc')
+            ->get()
+            ->map(fn($u) => [
+                $u->name,
+                $u->phone ?? '',
+                $u->email ?? '',
+            ]);
+    }
+
+    public function headings(): array
+    {
+        return ['Nome', 'Telefone', 'E-mail'];
+    }
+
+    public function title(): string
+    {
+        return 'Contatos';
+    }
+
+    public function columnWidths(): array
+    {
+        return ['A' => 40, 'B' => 22, 'C' => 35];
+    }
+
+    public function styles(Worksheet $sheet): array
+    {
+        return [
+            1 => [
+                'font'      => ['bold' => true, 'color' => ['argb' => 'FFFFFFFF']],
+                'fill'      => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF661D75']],
+                'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
+            ],
+        ];
+    }
+}

+ 62 - 0
app/Exports/ExclusoesMesExport.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace App\Exports;
+
+use App\Enums\UserTypeEnum;
+use App\Models\User;
+use Carbon\Carbon;
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithHeadings;
+use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithColumnWidths;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use Illuminate\Support\Collection;
+
+class ExclusoesMesExport implements FromCollection, WithHeadings, WithStyles, WithColumnWidths, WithTitle
+{
+    public function collection(): Collection
+    {
+        $now = Carbon::now();
+
+        return User::where('type', UserTypeEnum::ASSOCIADO)
+            ->whereNotNull('excluded_at')
+            ->whereYear('excluded_at', $now->year)
+            ->whereMonth('excluded_at', $now->month)
+            ->orderBy('excluded_at', 'desc')
+            ->get()
+            ->map(fn($u) => [
+                $u->name,
+                $u->cpf ?? '',
+                $u->excluded_at?->format('d/m/Y') ?? '',
+            ]);
+    }
+
+    public function headings(): array
+    {
+        return ['Nome', 'CPF', 'Data de Exclusão'];
+    }
+
+    public function title(): string
+    {
+        return 'Exclusões do Mês';
+    }
+
+    public function columnWidths(): array
+    {
+        return ['A' => 40, 'B' => 20, 'C' => 20];
+    }
+
+    public function styles(Worksheet $sheet): array
+    {
+        return [
+            1 => [
+                'font'      => ['bold' => true, 'color' => ['argb' => 'FFFFFFFF']],
+                'fill'      => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF661D75']],
+                'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
+            ],
+        ];
+    }
+}

+ 61 - 0
app/Exports/NovoAssociadosExport.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App\Exports;
+
+use App\Enums\UserTypeEnum;
+use App\Models\User;
+use Carbon\Carbon;
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithHeadings;
+use Maatwebsite\Excel\Concerns\WithStyles;
+use Maatwebsite\Excel\Concerns\WithColumnWidths;
+use Maatwebsite\Excel\Concerns\WithTitle;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use Illuminate\Support\Collection;
+
+class NovoAssociadosExport implements FromCollection, WithHeadings, WithStyles, WithColumnWidths, WithTitle
+{
+    public function collection(): Collection
+    {
+        $now = Carbon::now();
+
+        return User::where('type', UserTypeEnum::ASSOCIADO)
+            ->whereYear('created_at', $now->year)
+            ->whereMonth('created_at', $now->month)
+            ->orderBy('created_at', 'desc')
+            ->get()
+            ->map(fn($u) => [
+                $u->name,
+                $u->cpf ?? '',
+                $u->created_at?->format('d/m/Y') ?? '',
+            ]);
+    }
+
+    public function headings(): array
+    {
+        return ['Nome', 'CPF', 'Data de Cadastro'];
+    }
+
+    public function title(): string
+    {
+        return 'Novos Associados';
+    }
+
+    public function columnWidths(): array
+    {
+        return ['A' => 40, 'B' => 20, 'C' => 20];
+    }
+
+    public function styles(Worksheet $sheet): array
+    {
+        return [
+            1 => [
+                'font'      => ['bold' => true, 'color' => ['argb' => 'FFFFFFFF']],
+                'fill'      => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF661D75']],
+                'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
+            ],
+        ];
+    }
+}

+ 106 - 0
app/Http/Controllers/ReportController.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Exports\ContatosAssociadosExport;
+use App\Exports\ExclusoesMesExport;
+use App\Exports\NovoAssociadosExport;
+use App\Services\ReportService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Maatwebsite\Excel\Facades\Excel;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+
+class ReportController extends Controller
+{
+    public function __construct(protected ReportService $service) {}
+
+    public function getCounters(): JsonResponse
+    {
+        return $this->successResponse(payload: $this->service->getCounters());
+    }
+
+    public function getNovoAssociadosPaginated(Request $request): JsonResponse
+    {
+        $filters   = $request->only(['search']);
+        $perPage   = min((int) $request->get('per_page', 10), 100);
+        $paginator = $this->service->getNovoAssociadosPaginated($filters, $perPage);
+
+        return $this->successResponse(payload: [
+            'data'  => array_map(
+                fn($u) => [
+                    'id'         => $u->id,
+                    'name'       => $u->name,
+                    'cpf'        => $u->cpf,
+                    'created_at' => $u->created_at?->format('d/m/Y'),
+                ],
+                $paginator->items()
+            ),
+            'total' => $paginator->total(),
+            'from'  => $paginator->firstItem() ?? 0,
+            'to'    => $paginator->lastItem() ?? 0,
+        ]);
+    }
+
+    public function getContatosAssociadosPaginated(Request $request): JsonResponse
+    {
+        $filters   = $request->only(['search']);
+        $perPage   = min((int) $request->get('per_page', 10), 100);
+        $paginator = $this->service->getContatosAssociadosPaginated($filters, $perPage);
+
+        return $this->successResponse(payload: [
+            'data'  => array_map(
+                fn($u) => [
+                    'id'    => $u->id,
+                    'name'  => $u->name,
+                    'phone' => $u->phone,
+                    'email' => $u->email,
+                ],
+                $paginator->items()
+            ),
+            'total' => $paginator->total(),
+            'from'  => $paginator->firstItem() ?? 0,
+            'to'    => $paginator->lastItem() ?? 0,
+        ]);
+    }
+
+    public function getExclusoesMesPaginated(Request $request): JsonResponse
+    {
+        $filters   = $request->only(['search']);
+        $perPage   = min((int) $request->get('per_page', 10), 100);
+        $paginator = $this->service->getExclusoesMesPaginated($filters, $perPage);
+
+        return $this->successResponse(payload: [
+            'data'  => array_map(
+                fn($u) => [
+                    'id'          => $u->id,
+                    'name'        => $u->name,
+                    'cpf'         => $u->cpf,
+                    'excluded_at' => $u->excluded_at?->format('d/m/Y'),
+                ],
+                $paginator->items()
+            ),
+            'total' => $paginator->total(),
+            'from'  => $paginator->firstItem() ?? 0,
+            'to'    => $paginator->lastItem() ?? 0,
+        ]);
+    }
+
+    public function exportNovoAssociados(): BinaryFileResponse
+    {
+        $filename = 'novos_associados_' . now()->format('d-m-Y') . '.xlsx';
+        return Excel::download(new NovoAssociadosExport(), $filename);
+    }
+
+    public function exportContatosAssociados(): BinaryFileResponse
+    {
+        $filename = 'contatos_associados_' . now()->format('d-m-Y') . '.xlsx';
+        return Excel::download(new ContatosAssociadosExport(), $filename);
+    }
+
+    public function exportExclusoesMes(): BinaryFileResponse
+    {
+        $filename = 'exclusoes_mes_' . now()->format('d-m-Y') . '.xlsx';
+        return Excel::download(new ExclusoesMesExport(), $filename);
+    }
+}

+ 1 - 0
app/Http/Resources/UserResource.php

@@ -20,6 +20,7 @@ class UserResource extends JsonResource
             'id'             => $this->id,
             'name'           => $this->name,
             'email'          => $this->email,
+            'phone'          => $this->phone,
             'cpf'            => $this->cpf,
             'registration'   => $this->registration,
             'language'       => $this->language,

+ 1 - 0
app/Models/User.php

@@ -77,6 +77,7 @@ class User extends Authenticatable
             "status"            => UserStatusEnum::class,
             "admission_date"    => "date",
             "expiry_date"       => "date",
+            "excluded_at"       => "datetime",
         ];
     }
 

+ 120 - 0
app/Services/ReportService.php

@@ -0,0 +1,120 @@
+<?php
+
+namespace App\Services;
+
+use App\Enums\UserTypeEnum;
+use App\Models\User;
+use Carbon\Carbon;
+use Illuminate\Pagination\LengthAwarePaginator;
+use Illuminate\Database\Eloquent\Collection;
+
+class ReportService
+{
+    public function getCounters(): array
+    {
+        $now = Carbon::now();
+
+        return [
+            'novos_associados' => User::where('type', UserTypeEnum::ASSOCIADO)
+                ->whereYear('created_at', $now->year)
+                ->whereMonth('created_at', $now->month)
+                ->count(),
+            'contatos' => User::where('type', UserTypeEnum::ASSOCIADO)->count(),
+            'exclusoes_mes' => User::where('type', UserTypeEnum::ASSOCIADO)
+                ->whereNotNull('excluded_at')
+                ->whereYear('excluded_at', $now->year)
+                ->whereMonth('excluded_at', $now->month)
+                ->count(),
+        ];
+    }
+
+    public function getNovoAssociadosPaginated(array $filters = [], int $perPage = 10): LengthAwarePaginator
+    {
+        $now = Carbon::now();
+
+        $query = User::where('type', UserTypeEnum::ASSOCIADO)
+            ->whereYear('created_at', $now->year)
+            ->whereMonth('created_at', $now->month)
+            ->orderBy('created_at', 'desc');
+
+        if (!empty($filters['search'])) {
+            $term = '%' . mb_strtolower($filters['search']) . '%';
+            $query->where(function ($q) use ($term, $filters) {
+                $q->whereRaw('UNACCENT(LOWER(name)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw('UNACCENT(LOWER(cpf)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw("TO_CHAR(created_at, 'DD/MM/YYYY') LIKE ?", [$term]);
+            });
+        }
+
+        return $query->paginate($perPage);
+    }
+
+    public function getContatosAssociadosPaginated(array $filters = [], int $perPage = 10): LengthAwarePaginator
+    {
+        $query = User::where('type', UserTypeEnum::ASSOCIADO)
+            ->orderBy('name', 'asc');
+
+        if (!empty($filters['search'])) {
+            $term = '%' . mb_strtolower($filters['search']) . '%';
+            $query->where(function ($q) use ($term) {
+                $q->whereRaw('UNACCENT(LOWER(name)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw('UNACCENT(LOWER(email)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw('UNACCENT(LOWER(COALESCE(phone, \'\'))) LIKE UNACCENT(?)', [$term]);
+            });
+        }
+
+        return $query->paginate($perPage);
+    }
+
+    public function getExclusoesMesPaginated(array $filters = [], int $perPage = 10): LengthAwarePaginator
+    {
+        $now = Carbon::now();
+
+        $query = User::where('type', UserTypeEnum::ASSOCIADO)
+            ->whereNotNull('excluded_at')
+            ->whereYear('excluded_at', $now->year)
+            ->whereMonth('excluded_at', $now->month)
+            ->orderBy('excluded_at', 'desc');
+
+        if (!empty($filters['search'])) {
+            $term = '%' . mb_strtolower($filters['search']) . '%';
+            $query->where(function ($q) use ($term) {
+                $q->whereRaw('UNACCENT(LOWER(name)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw('UNACCENT(LOWER(cpf)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw("TO_CHAR(excluded_at, 'DD/MM/YYYY') LIKE ?", [$term]);
+            });
+        }
+
+        return $query->paginate($perPage);
+    }
+
+    public function getAllNovoAssociados(): Collection
+    {
+        $now = Carbon::now();
+
+        return User::where('type', UserTypeEnum::ASSOCIADO)
+            ->whereYear('created_at', $now->year)
+            ->whereMonth('created_at', $now->month)
+            ->orderBy('created_at', 'desc')
+            ->get();
+    }
+
+    public function getAllContatosAssociados(): Collection
+    {
+        return User::where('type', UserTypeEnum::ASSOCIADO)
+            ->orderBy('name', 'asc')
+            ->get();
+    }
+
+    public function getAllExclusoesMes(): Collection
+    {
+        $now = Carbon::now();
+
+        return User::where('type', UserTypeEnum::ASSOCIADO)
+            ->whereNotNull('excluded_at')
+            ->whereYear('excluded_at', $now->year)
+            ->whereMonth('excluded_at', $now->month)
+            ->orderBy('excluded_at', 'desc')
+            ->get();
+    }
+}

+ 19 - 6
app/Services/UserService.php

@@ -2,6 +2,7 @@
 
 namespace App\Services;
 
+use App\Enums\UserStatusEnum;
 use App\Enums\UserTypeEnum;
 use App\Models\User;
 use Illuminate\Database\Eloquent\Collection;
@@ -33,12 +34,12 @@ class UserService
         }
 
         if (!empty($filters['search'])) {
-            $search = $filters['search'];
-            $query->where(function ($q) use ($search) {
-                $q->where('name', 'like', "%{$search}%")
-                  ->orWhere('email', 'like', "%{$search}%")
-                  ->orWhere('cpf', 'like', "%{$search}%")
-                  ->orWhere('registration', 'like', "%{$search}%");
+            $term = '%' . mb_strtolower($filters['search']) . '%';
+            $query->where(function ($q) use ($term) {
+                $q->whereRaw('UNACCENT(LOWER(name)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw('UNACCENT(LOWER(email)) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw('UNACCENT(LOWER(COALESCE(cpf, \'\'))) LIKE UNACCENT(?)', [$term])
+                  ->orWhereRaw('UNACCENT(LOWER(COALESCE(registration, \'\'))) LIKE UNACCENT(?)', [$term]);
             });
         }
 
@@ -63,6 +64,18 @@ class UserService
             return null;
         }
 
+        if (isset($data['status'])) {
+            $newStatus = $data['status'] instanceof UserStatusEnum
+                ? $data['status']
+                : UserStatusEnum::from($data['status']);
+
+            if ($newStatus === UserStatusEnum::INACTIVE && $model->status !== UserStatusEnum::INACTIVE) {
+                $data['excluded_at'] = now();
+            } elseif ($newStatus !== UserStatusEnum::INACTIVE) {
+                $data['excluded_at'] = null;
+            }
+        }
+
         $model->update($data);
         return $model->fresh();
     }

+ 2 - 1
composer.json

@@ -12,7 +12,8 @@
         "kalnoy/nestedset": "^6.0",
         "laravel/framework": "^12.0",
         "laravel/sanctum": "^4.0",
-        "laravel/tinker": "^2.9"
+        "laravel/tinker": "^2.9",
+        "maatwebsite/excel": "^3.1"
     },
     "require-dev": {
         "barryvdh/laravel-ide-helper": "^3.6",

+ 592 - 80
composer.lock

@@ -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": "40404f977a56fc05d7f7679557905a6b",
+    "content-hash": "9c05ec87dcb6d98f4c1e99d13bdc3bc4",
     "packages": [
         {
             "name": "brick/math",
@@ -135,6 +135,162 @@
             ],
             "time": "2024-02-09T16:56:22+00:00"
         },
+        {
+            "name": "composer/pcre",
+            "version": "3.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/pcre.git",
+                "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+                "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.4 || ^8.0"
+            },
+            "conflict": {
+                "phpstan/phpstan": "<1.11.10"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^1.12 || ^2",
+                "phpstan/phpstan-strict-rules": "^1 || ^2",
+                "phpunit/phpunit": "^8 || ^9"
+            },
+            "type": "library",
+            "extra": {
+                "phpstan": {
+                    "includes": [
+                        "extension.neon"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-main": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Pcre\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
+            "keywords": [
+                "PCRE",
+                "preg",
+                "regex",
+                "regular expression"
+            ],
+            "support": {
+                "issues": "https://github.com/composer/pcre/issues",
+                "source": "https://github.com/composer/pcre/tree/3.3.2"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-11-12T16:29:46+00:00"
+        },
+        {
+            "name": "composer/semver",
+            "version": "3.4.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/semver.git",
+                "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+                "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.2 || ^7.0 || ^8.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^1.11",
+                "symfony/phpunit-bridge": "^3 || ^7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Semver\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                },
+                {
+                    "name": "Rob Bast",
+                    "email": "rob.bast@gmail.com",
+                    "homepage": "http://robbast.nl"
+                }
+            ],
+            "description": "Semver library that offers utilities, version constraint parsing and validation.",
+            "keywords": [
+                "semantic",
+                "semver",
+                "validation",
+                "versioning"
+            ],
+            "support": {
+                "irc": "ircs://irc.libera.chat:6697/composer",
+                "issues": "https://github.com/composer/semver/issues",
+                "source": "https://github.com/composer/semver/tree/3.4.4"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                }
+            ],
+            "time": "2025-08-20T19:15:30+00:00"
+        },
         {
             "name": "dflydev/dot-access-data",
             "version": "v3.0.3",
@@ -510,6 +666,67 @@
             ],
             "time": "2025-03-06T22:45:56+00:00"
         },
+        {
+            "name": "ezyang/htmlpurifier",
+            "version": "v4.19.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ezyang/htmlpurifier.git",
+                "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf",
+                "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf",
+                "shasum": ""
+            },
+            "require": {
+                "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0"
+            },
+            "require-dev": {
+                "cerdic/css-tidy": "^1.7 || ^2.0",
+                "simpletest/simpletest": "dev-master"
+            },
+            "suggest": {
+                "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
+                "ext-bcmath": "Used for unit conversion and imagecrash protection",
+                "ext-iconv": "Converts text to and from non-UTF-8 encodings",
+                "ext-tidy": "Used for pretty-printing HTML"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "library/HTMLPurifier.composer.php"
+                ],
+                "psr-0": {
+                    "HTMLPurifier": "library/"
+                },
+                "exclude-from-classmap": [
+                    "/library/HTMLPurifier/Language/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "Edward Z. Yang",
+                    "email": "admin@htmlpurifier.org",
+                    "homepage": "http://ezyang.com"
+                }
+            ],
+            "description": "Standards compliant HTML filter written in PHP",
+            "homepage": "http://htmlpurifier.org/",
+            "keywords": [
+                "html"
+            ],
+            "support": {
+                "issues": "https://github.com/ezyang/htmlpurifier/issues",
+                "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0"
+            },
+            "time": "2025-10-17T16:34:55+00:00"
+        },
         {
             "name": "fruitcake/php-cors",
             "version": "v1.3.0",
@@ -2133,6 +2350,272 @@
             ],
             "time": "2024-12-08T08:18:47+00:00"
         },
+        {
+            "name": "maatwebsite/excel",
+            "version": "3.1.69",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/SpartnerNL/Laravel-Excel.git",
+                "reference": "ae5d65b7c9a2fac43bff4d44f796ac95d7a8e760"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/ae5d65b7c9a2fac43bff4d44f796ac95d7a8e760",
+                "reference": "ae5d65b7c9a2fac43bff4d44f796ac95d7a8e760",
+                "shasum": ""
+            },
+            "require": {
+                "composer/semver": "^3.3",
+                "ext-json": "*",
+                "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0||^13.0",
+                "php": "^7.0||^8.0",
+                "phpoffice/phpspreadsheet": "^1.30.4",
+                "psr/simple-cache": "^1.0||^2.0||^3.0"
+            },
+            "require-dev": {
+                "laravel/scout": "^7.0||^8.0||^9.0||^10.0||^11.0",
+                "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0||^10.0||^11.0",
+                "predis/predis": "^1.1"
+            },
+            "type": "library",
+            "extra": {
+                "laravel": {
+                    "aliases": {
+                        "Excel": "Maatwebsite\\Excel\\Facades\\Excel"
+                    },
+                    "providers": [
+                        "Maatwebsite\\Excel\\ExcelServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Maatwebsite\\Excel\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Patrick Brouwers",
+                    "email": "patrick@spartner.nl"
+                }
+            ],
+            "description": "Supercharged Excel exports and imports in Laravel",
+            "keywords": [
+                "PHPExcel",
+                "batch",
+                "csv",
+                "excel",
+                "export",
+                "import",
+                "laravel",
+                "php",
+                "phpspreadsheet"
+            ],
+            "support": {
+                "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues",
+                "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.69"
+            },
+            "funding": [
+                {
+                    "url": "https://laravel-excel.com/commercial-support",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/patrickbrouwers",
+                    "type": "github"
+                }
+            ],
+            "time": "2026-04-30T20:03:58+00:00"
+        },
+        {
+            "name": "maennchen/zipstream-php",
+            "version": "3.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/maennchen/ZipStream-PHP.git",
+                "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e",
+                "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "ext-zlib": "*",
+                "php-64bit": "^8.3"
+            },
+            "require-dev": {
+                "brianium/paratest": "^7.7",
+                "ext-zip": "*",
+                "friendsofphp/php-cs-fixer": "^3.86",
+                "guzzlehttp/guzzle": "^7.5",
+                "mikey179/vfsstream": "^1.6",
+                "php-coveralls/php-coveralls": "^2.5",
+                "phpunit/phpunit": "^12.0",
+                "vimeo/psalm": "^6.0"
+            },
+            "suggest": {
+                "guzzlehttp/psr7": "^2.4",
+                "psr/http-message": "^2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "ZipStream\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paul Duncan",
+                    "email": "pabs@pablotron.org"
+                },
+                {
+                    "name": "Jonatan Männchen",
+                    "email": "jonatan@maennchen.ch"
+                },
+                {
+                    "name": "Jesse Donat",
+                    "email": "donatj@gmail.com"
+                },
+                {
+                    "name": "András Kolesár",
+                    "email": "kolesar@kolesar.hu"
+                }
+            ],
+            "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+            "keywords": [
+                "stream",
+                "zip"
+            ],
+            "support": {
+                "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+                "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/maennchen",
+                    "type": "github"
+                }
+            ],
+            "time": "2026-04-11T18:38:28+00:00"
+        },
+        {
+            "name": "markbaker/complex",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/MarkBaker/PHPComplex.git",
+                "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+                "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+                "phpcompatibility/php-compatibility": "^9.3",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+                "squizlabs/php_codesniffer": "^3.7"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Complex\\": "classes/src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mark Baker",
+                    "email": "mark@lange.demon.co.uk"
+                }
+            ],
+            "description": "PHP Class for working with complex numbers",
+            "homepage": "https://github.com/MarkBaker/PHPComplex",
+            "keywords": [
+                "complex",
+                "mathematics"
+            ],
+            "support": {
+                "issues": "https://github.com/MarkBaker/PHPComplex/issues",
+                "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
+            },
+            "time": "2022-12-06T16:21:08+00:00"
+        },
+        {
+            "name": "markbaker/matrix",
+            "version": "3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/MarkBaker/PHPMatrix.git",
+                "reference": "728434227fe21be27ff6d86621a1b13107a2562c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
+                "reference": "728434227fe21be27ff6d86621a1b13107a2562c",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+                "phpcompatibility/php-compatibility": "^9.3",
+                "phpdocumentor/phpdocumentor": "2.*",
+                "phploc/phploc": "^4.0",
+                "phpmd/phpmd": "2.*",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+                "sebastian/phpcpd": "^4.0",
+                "squizlabs/php_codesniffer": "^3.7"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Matrix\\": "classes/src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mark Baker",
+                    "email": "mark@demon-angel.eu"
+                }
+            ],
+            "description": "PHP Class for working with matrices",
+            "homepage": "https://github.com/MarkBaker/PHPMatrix",
+            "keywords": [
+                "mathematics",
+                "matrix",
+                "vector"
+            ],
+            "support": {
+                "issues": "https://github.com/MarkBaker/PHPMatrix/issues",
+                "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
+            },
+            "time": "2022-12-02T22:17:43+00:00"
+        },
         {
             "name": "monolog/monolog",
             "version": "3.9.0",
@@ -2634,6 +3117,114 @@
             ],
             "time": "2025-05-08T08:14:37+00:00"
         },
+        {
+            "name": "phpoffice/phpspreadsheet",
+            "version": "1.30.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+                "reference": "02970383cc12e7bf0bc0707ea6e2e8ed23a7aec9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/02970383cc12e7bf0bc0707ea6e2e8ed23a7aec9",
+                "reference": "02970383cc12e7bf0bc0707ea6e2e8ed23a7aec9",
+                "shasum": ""
+            },
+            "require": {
+                "composer/pcre": "^1||^2||^3",
+                "ext-ctype": "*",
+                "ext-dom": "*",
+                "ext-fileinfo": "*",
+                "ext-gd": "*",
+                "ext-iconv": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-simplexml": "*",
+                "ext-xml": "*",
+                "ext-xmlreader": "*",
+                "ext-xmlwriter": "*",
+                "ext-zip": "*",
+                "ext-zlib": "*",
+                "ezyang/htmlpurifier": "^4.15",
+                "maennchen/zipstream-php": "^2.1 || ^3.0",
+                "markbaker/complex": "^3.0",
+                "markbaker/matrix": "^3.0",
+                "php": ">=7.4.0 <8.5.0",
+                "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+                "doctrine/instantiator": "^1.5",
+                "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0",
+                "friendsofphp/php-cs-fixer": "^3.2",
+                "mitoteam/jpgraph": "^10.3",
+                "mpdf/mpdf": "^8.1.1",
+                "phpcompatibility/php-compatibility": "^9.3",
+                "phpstan/phpstan": "^1.1",
+                "phpstan/phpstan-phpunit": "^1.0",
+                "phpunit/phpunit": "^8.5 || ^9.0",
+                "squizlabs/php_codesniffer": "^3.7",
+                "tecnickcom/tcpdf": "^6.5"
+            },
+            "suggest": {
+                "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+                "ext-intl": "PHP Internationalization Functions",
+                "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
+                "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
+                "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Maarten Balliauw",
+                    "homepage": "https://blog.maartenballiauw.be"
+                },
+                {
+                    "name": "Mark Baker",
+                    "homepage": "https://markbakeruk.net"
+                },
+                {
+                    "name": "Franck Lefevre",
+                    "homepage": "https://rootslabs.net"
+                },
+                {
+                    "name": "Erik Tilt"
+                },
+                {
+                    "name": "Adrien Crivelli"
+                },
+                {
+                    "name": "Owen Leibman"
+                }
+            ],
+            "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+            "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
+            "keywords": [
+                "OpenXML",
+                "excel",
+                "gnumeric",
+                "ods",
+                "php",
+                "spreadsheet",
+                "xls",
+                "xlsx"
+            ],
+            "support": {
+                "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
+                "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.4"
+            },
+            "time": "2026-04-19T06:00:39+00:00"
+        },
         {
             "name": "phpoption/phpoption",
             "version": "1.9.3",
@@ -6121,85 +6712,6 @@
             ],
             "time": "2025-08-20T18:52:43+00:00"
         },
-        {
-            "name": "composer/pcre",
-            "version": "3.3.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/composer/pcre.git",
-                "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
-                "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.4 || ^8.0"
-            },
-            "conflict": {
-                "phpstan/phpstan": "<1.11.10"
-            },
-            "require-dev": {
-                "phpstan/phpstan": "^1.12 || ^2",
-                "phpstan/phpstan-strict-rules": "^1 || ^2",
-                "phpunit/phpunit": "^8 || ^9"
-            },
-            "type": "library",
-            "extra": {
-                "phpstan": {
-                    "includes": [
-                        "extension.neon"
-                    ]
-                },
-                "branch-alias": {
-                    "dev-main": "3.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Composer\\Pcre\\": "src"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Jordi Boggiano",
-                    "email": "j.boggiano@seld.be",
-                    "homepage": "http://seld.be"
-                }
-            ],
-            "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
-            "keywords": [
-                "PCRE",
-                "preg",
-                "regex",
-                "regular expression"
-            ],
-            "support": {
-                "issues": "https://github.com/composer/pcre/issues",
-                "source": "https://github.com/composer/pcre/tree/3.3.2"
-            },
-            "funding": [
-                {
-                    "url": "https://packagist.com",
-                    "type": "custom"
-                },
-                {
-                    "url": "https://github.com/composer",
-                    "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
-                    "type": "tidelift"
-                }
-            ],
-            "time": "2024-11-12T16:29:46+00:00"
-        },
         {
             "name": "fakerphp/faker",
             "version": "v1.24.1",

+ 380 - 0
config/excel.php

@@ -0,0 +1,380 @@
+<?php
+
+use Maatwebsite\Excel\Excel;
+use PhpOffice\PhpSpreadsheet\Reader\Csv;
+
+return [
+    'exports' => [
+
+        /*
+        |--------------------------------------------------------------------------
+        | Chunk size
+        |--------------------------------------------------------------------------
+        |
+        | When using FromQuery, the query is automatically chunked.
+        | Here you can specify how big the chunk should be.
+        |
+        */
+        'chunk_size'             => 1000,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Pre-calculate formulas during export
+        |--------------------------------------------------------------------------
+        */
+        'pre_calculate_formulas' => false,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Enable strict null comparison
+        |--------------------------------------------------------------------------
+        |
+        | When enabling strict null comparison empty cells ('') will
+        | be added to the sheet.
+        */
+        'strict_null_comparison' => false,
+
+        /*
+        |--------------------------------------------------------------------------
+        | CSV Settings
+        |--------------------------------------------------------------------------
+        |
+        | Configure e.g. delimiter, enclosure and line ending for CSV exports.
+        |
+        */
+        'csv'                    => [
+            'delimiter'              => ',',
+            'enclosure'              => '"',
+            'line_ending'            => PHP_EOL,
+            'use_bom'                => false,
+            'include_separator_line' => false,
+            'excel_compatibility'    => false,
+            'output_encoding'        => '',
+            'test_auto_detect'       => true,
+        ],
+
+        /*
+        |--------------------------------------------------------------------------
+        | Worksheet properties
+        |--------------------------------------------------------------------------
+        |
+        | Configure e.g. default title, creator, subject,...
+        |
+        */
+        'properties'             => [
+            'creator'        => '',
+            'lastModifiedBy' => '',
+            'title'          => '',
+            'description'    => '',
+            'subject'        => '',
+            'keywords'       => '',
+            'category'       => '',
+            'manager'        => '',
+            'company'        => '',
+        ],
+    ],
+
+    'imports'            => [
+
+        /*
+        |--------------------------------------------------------------------------
+        | Read Only
+        |--------------------------------------------------------------------------
+        |
+        | When dealing with imports, you might only be interested in the
+        | data that the sheet exists. By default we ignore all styles,
+        | however if you want to do some logic based on style data
+        | you can enable it by setting read_only to false.
+        |
+        */
+        'read_only'    => true,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Ignore Empty
+        |--------------------------------------------------------------------------
+        |
+        | When dealing with imports, you might be interested in ignoring
+        | rows that have null values or empty strings. By default rows
+        | containing empty strings or empty values are not ignored but can be
+        | ignored by enabling the setting ignore_empty to true.
+        |
+        */
+        'ignore_empty' => false,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Heading Row Formatter
+        |--------------------------------------------------------------------------
+        |
+        | Configure the heading row formatter.
+        | Available options: none|slug|custom
+        |
+        */
+        'heading_row'  => [
+            'formatter' => 'slug',
+        ],
+
+        /*
+        |--------------------------------------------------------------------------
+        | CSV Settings
+        |--------------------------------------------------------------------------
+        |
+        | Configure e.g. delimiter, enclosure and line ending for CSV imports.
+        |
+        */
+        'csv'          => [
+            'delimiter'        => null,
+            'enclosure'        => '"',
+            'escape_character' => '\\',
+            'contiguous'       => false,
+            'input_encoding'   => Csv::GUESS_ENCODING,
+        ],
+
+        /*
+        |--------------------------------------------------------------------------
+        | Worksheet properties
+        |--------------------------------------------------------------------------
+        |
+        | Configure e.g. default title, creator, subject,...
+        |
+        */
+        'properties'   => [
+            'creator'        => '',
+            'lastModifiedBy' => '',
+            'title'          => '',
+            'description'    => '',
+            'subject'        => '',
+            'keywords'       => '',
+            'category'       => '',
+            'manager'        => '',
+            'company'        => '',
+        ],
+
+        /*
+       |--------------------------------------------------------------------------
+       | Cell Middleware
+       |--------------------------------------------------------------------------
+       |
+       | Configure middleware that is executed on getting a cell value
+       |
+       */
+        'cells'        => [
+            'middleware' => [
+                //\Maatwebsite\Excel\Middleware\TrimCellValue::class,
+                //\Maatwebsite\Excel\Middleware\ConvertEmptyCellValuesToNull::class,
+            ],
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Extension detector
+    |--------------------------------------------------------------------------
+    |
+    | Configure here which writer/reader type should be used when the package
+    | needs to guess the correct type based on the extension alone.
+    |
+    */
+    'extension_detector' => [
+        'xlsx'     => Excel::XLSX,
+        'xlsm'     => Excel::XLSX,
+        'xltx'     => Excel::XLSX,
+        'xltm'     => Excel::XLSX,
+        'xls'      => Excel::XLS,
+        'xlt'      => Excel::XLS,
+        'ods'      => Excel::ODS,
+        'ots'      => Excel::ODS,
+        'slk'      => Excel::SLK,
+        'xml'      => Excel::XML,
+        'gnumeric' => Excel::GNUMERIC,
+        'htm'      => Excel::HTML,
+        'html'     => Excel::HTML,
+        'csv'      => Excel::CSV,
+        'tsv'      => Excel::TSV,
+
+        /*
+        |--------------------------------------------------------------------------
+        | PDF Extension
+        |--------------------------------------------------------------------------
+        |
+        | Configure here which Pdf driver should be used by default.
+        | Available options: Excel::MPDF | Excel::TCPDF | Excel::DOMPDF
+        |
+        */
+        'pdf'      => Excel::DOMPDF,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Value Binder
+    |--------------------------------------------------------------------------
+    |
+    | PhpSpreadsheet offers a way to hook into the process of a value being
+    | written to a cell. In there some assumptions are made on how the
+    | value should be formatted. If you want to change those defaults,
+    | you can implement your own default value binder.
+    |
+    | Possible value binders:
+    |
+    | [x] Maatwebsite\Excel\DefaultValueBinder::class
+    | [x] PhpOffice\PhpSpreadsheet\Cell\StringValueBinder::class
+    | [x] PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder::class
+    |
+    */
+    'value_binder'       => [
+        'default' => Maatwebsite\Excel\DefaultValueBinder::class,
+    ],
+
+    'cache'        => [
+        /*
+        |--------------------------------------------------------------------------
+        | Default cell caching driver
+        |--------------------------------------------------------------------------
+        |
+        | By default PhpSpreadsheet keeps all cell values in memory, however when
+        | dealing with large files, this might result into memory issues. If you
+        | want to mitigate that, you can configure a cell caching driver here.
+        | When using the illuminate driver, it will store each value in the
+        | cache store. This can slow down the process, because it needs to
+        | store each value. You can use the "batch" store if you want to
+        | only persist to the store when the memory limit is reached.
+        |
+        | Drivers: memory|illuminate|batch
+        |
+        */
+        'driver'      => 'memory',
+
+        /*
+        |--------------------------------------------------------------------------
+        | Batch memory caching
+        |--------------------------------------------------------------------------
+        |
+        | When dealing with the "batch" caching driver, it will only
+        | persist to the store when the memory limit is reached.
+        | Here you can tweak the memory limit to your liking.
+        |
+        */
+        'batch'       => [
+            'memory_limit' => 60000,
+        ],
+
+        /*
+        |--------------------------------------------------------------------------
+        | Illuminate cache
+        |--------------------------------------------------------------------------
+        |
+        | When using the "illuminate" caching driver, it will automatically use
+        | your default cache store. However if you prefer to have the cell
+        | cache on a separate store, you can configure the store name here.
+        | You can use any store defined in your cache config. When leaving
+        | at "null" it will use the default store.
+        |
+        */
+        'illuminate'  => [
+            'store' => null,
+        ],
+
+        /*
+        |--------------------------------------------------------------------------
+        | Cache Time-to-live (TTL)
+        |--------------------------------------------------------------------------
+        |
+        | The TTL of items written to cache. If you want to keep the items cached
+        | indefinitely, set this to null.  Otherwise, set a number of seconds,
+        | a \DateInterval, or a callable.
+        |
+        | Allowable types: callable|\DateInterval|int|null
+        |
+         */
+        'default_ttl' => 10800,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Transaction Handler
+    |--------------------------------------------------------------------------
+    |
+    | By default the import is wrapped in a transaction. This is useful
+    | for when an import may fail and you want to retry it. With the
+    | transactions, the previous import gets rolled-back.
+    |
+    | You can disable the transaction handler by setting this to null.
+    | Or you can choose a custom made transaction handler here.
+    |
+    | Supported handlers: null|db
+    |
+    */
+    'transactions' => [
+        'handler' => 'db',
+        'db'      => [
+            'connection' => null,
+        ],
+    ],
+
+    'temporary_files' => [
+
+        /*
+        |--------------------------------------------------------------------------
+        | Local Temporary Path
+        |--------------------------------------------------------------------------
+        |
+        | When exporting and importing files, we use a temporary file, before
+        | storing reading or downloading. Here you can customize that path.
+        | permissions is an array with the permission flags for the directory (dir)
+        | and the create file (file).
+        |
+        */
+        'local_path'          => storage_path('framework/cache/laravel-excel'),
+
+        /*
+        |--------------------------------------------------------------------------
+        | Local Temporary Path Permissions
+        |--------------------------------------------------------------------------
+        |
+        | Permissions is an array with the permission flags for the directory (dir)
+        | and the create file (file).
+        | If omitted the default permissions of the filesystem will be used.
+        |
+        */
+        'local_permissions'   => [
+            // 'dir'  => 0755,
+            // 'file' => 0644,
+        ],
+
+        /*
+        |--------------------------------------------------------------------------
+        | Remote Temporary Disk
+        |--------------------------------------------------------------------------
+        |
+        | When dealing with a multi server setup with queues in which you
+        | cannot rely on having a shared local temporary path, you might
+        | want to store the temporary file on a shared disk. During the
+        | queue executing, we'll retrieve the temporary file from that
+        | location instead. When left to null, it will always use
+        | the local path. This setting only has effect when using
+        | in conjunction with queued imports and exports.
+        |
+        */
+        'remote_disk'         => null,
+        'remote_prefix'       => null,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Force Resync
+        |--------------------------------------------------------------------------
+        |
+        | When dealing with a multi server setup as above, it's possible
+        | for the clean up that occurs after entire queue has been run to only
+        | cleanup the server that the last AfterImportJob runs on. The rest of the server
+        | would still have the local temporary file stored on it. In this case your
+        | local storage limits can be exceeded and future imports won't be processed.
+        | To mitigate this you can set this config value to be true, so that after every
+        | queued chunk is processed the local temporary file is deleted on the server that
+        | processed it.
+        |
+        */
+        'force_resync_remote' => null,
+    ],
+];

+ 24 - 0
database/migrations/2026_05_18_000001_add_excluded_at_to_users_table.php

@@ -0,0 +1,24 @@
+<?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('users', function (Blueprint $table) {
+            $table->timestamp('excluded_at')->nullable()->after('expiry_date');
+            $table->index('excluded_at');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropIndex(['excluded_at']);
+            $table->dropColumn('excluded_at');
+        });
+    }
+};

+ 81 - 0
database/seeders/AssociadosRelatorioSeeder.php

@@ -0,0 +1,81 @@
+<?php
+
+namespace Database\Seeders;
+
+use App\Enums\UserStatusEnum;
+use App\Enums\UserTypeEnum;
+use App\Models\User;
+use Carbon\Carbon;
+use Faker\Factory as Faker;
+use Illuminate\Database\Seeder;
+use Illuminate\Support\Facades\Hash;
+
+class AssociadosRelatorioSeeder extends Seeder
+{
+    private array $positions = [1, 2, 3, 4, 5, 6, 7, 8];
+    private array $sectors   = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+    public function run(): void
+    {
+        $faker = Faker::create('pt_BR');
+
+        // ── Tab 1: Novos Associados ─────────────────────────────────────────
+        // 25 associados ativos cadastrados em maio/2026
+        for ($i = 1; $i <= 25; $i++) {
+            $createdAt = Carbon::create(2026, 5, rand(1, 18), rand(8, 17), rand(0, 59));
+
+            User::create([
+                'name'           => $faker->name(),
+                'email'          => "novo.associado{$i}@serprati.test",
+                'password'       => Hash::make('senha123'),
+                'type'           => UserTypeEnum::ASSOCIADO,
+                'language'       => 'pt',
+                'status'         => UserStatusEnum::ACTIVE,
+                'cpf'            => $this->formatCpf($i),
+                'registration'   => 'NA' . str_pad($i, 4, '0', STR_PAD_LEFT),
+                'phone'          => $faker->cellphoneNumber(),
+                'admission_date' => Carbon::create(2026, 5, rand(1, 18)),
+                'expiry_date'    => Carbon::create(2027, 5, rand(1, 18)),
+                'position_id'    => $this->positions[($i - 1) % count($this->positions)],
+                'sector_id'      => $this->sectors[($i - 1) % count($this->sectors)],
+                'created_at'     => $createdAt,
+                'updated_at'     => $createdAt,
+            ]);
+        }
+
+        // ── Tab 3: Exclusões do mês ─────────────────────────────────────────
+        // 25 associados inativos excluídos em maio/2026, cadastrados antes
+        for ($i = 1; $i <= 25; $i++) {
+            $createdAt   = Carbon::create(2026, rand(1, 4), rand(1, 28), rand(8, 17), rand(0, 59));
+            $excludedAt  = Carbon::create(2026, 5, rand(1, 18), rand(8, 17), rand(0, 59));
+            $offset      = $i + 25;
+
+            User::create([
+                'name'           => $faker->name(),
+                'email'          => "excluido.associado{$i}@serprati.test",
+                'password'       => Hash::make('senha123'),
+                'type'           => UserTypeEnum::ASSOCIADO,
+                'language'       => 'pt',
+                'status'         => UserStatusEnum::INACTIVE,
+                'cpf'            => $this->formatCpf($offset),
+                'registration'   => 'EX' . str_pad($i, 4, '0', STR_PAD_LEFT),
+                'phone'          => rand(0, 1) ? $faker->cellphoneNumber() : null,
+                'admission_date' => $createdAt->copy()->subMonths(rand(1, 6)),
+                'expiry_date'    => null,
+                'position_id'    => $this->positions[($i - 1) % count($this->positions)],
+                'sector_id'      => $this->sectors[($i - 1) % count($this->sectors)],
+                'excluded_at'    => $excludedAt,
+                'created_at'     => $createdAt,
+                'updated_at'     => $excludedAt,
+            ]);
+        }
+
+        $this->command->info('AssociadosRelatorioSeeder: 25 novos associados + 25 exclusões do mês criados.');
+    }
+
+    private function formatCpf(int $n): string
+    {
+        $base = str_pad($n, 9, '0', STR_PAD_LEFT);
+        return substr($base, 0, 3) . '.' . substr($base, 3, 3) . '.' . substr($base, 6, 3) . '-00';
+    }
+}

+ 6 - 0
database/seeders/PermissionSeeder.php

@@ -181,6 +181,12 @@ class PermissionSeeder extends Seeder
                 "bits" => Permission::CRUD,
                 "children" => [],
             ],
+            [
+                "scope" => "relatorio",
+                "description" => "Relatórios",
+                "bits" => Permission::MENU | Permission::VIEW,
+                "children" => [],
+            ],
         ];
 
         $this->createPermissionsAndChildren(permissions: $permissions);

+ 6 - 0
database/seeders/UserTypePermissionSeeder.php

@@ -6,12 +6,18 @@ use App\Models\Permission;
 use App\Models\UserTypePermission;
 use Illuminate\Database\Seeder;
 use App\Enums\UserTypeEnum;
+use Illuminate\Support\Facades\Cache;
 
 class UserTypePermissionSeeder extends Seeder
 {
     public function run(): void
     {
         $allPermissions = Permission::all()->keyBy('scope');
+
+        foreach (UserTypeEnum::cases() as $userType) {
+            Cache::forget("permissions_role_{$userType->value}");
+        }
+        Cache::forget('permissions_guest');
         foreach (UserTypeEnum::cases() as $userType) {
             $dataToSync = [];
             switch ($userType) {

+ 16 - 0
routes/authRoutes/relatorio.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\ReportController;
+
+Route::controller(ReportController::class)->prefix('relatorio')->middleware('permission:relatorio,view')->group(function () {
+    Route::get('/counters', 'getCounters');
+
+    Route::get('/novos-associados', 'getNovoAssociadosPaginated');
+    Route::get('/contatos-associados', 'getContatosAssociadosPaginated');
+    Route::get('/exclusoes-mes', 'getExclusoesMesPaginated');
+
+    Route::get('/novos-associados/export', 'exportNovoAssociados');
+    Route::get('/contatos-associados/export', 'exportContatosAssociados');
+    Route::get('/exclusoes-mes/export', 'exportExclusoesMes');
+});