├── .nvmrc
├── public
├── favicon.ico
├── robots.txt
├── fonts
│ └── filament
│ │ └── filament
│ │ └── inter
│ │ ├── inter-greek-wght-normal-AXVTPQD5.woff2
│ │ ├── inter-greek-wght-normal-IRE366VL.woff2
│ │ ├── inter-greek-wght-normal-N43DBLU2.woff2
│ │ ├── inter-latin-wght-normal-NRMW37G5.woff2
│ │ ├── inter-latin-wght-normal-O25CN4JL.woff2
│ │ ├── inter-latin-wght-normal-OPIJAQLS.woff2
│ │ ├── inter-cyrillic-wght-normal-EWLSKVKN.woff2
│ │ ├── inter-cyrillic-wght-normal-JEOLYBOO.woff2
│ │ ├── inter-cyrillic-wght-normal-R5CMSONN.woff2
│ │ ├── inter-greek-ext-wght-normal-7GGTF7EK.woff2
│ │ ├── inter-greek-ext-wght-normal-EOVOK2B5.woff2
│ │ ├── inter-greek-ext-wght-normal-ZEVLMORV.woff2
│ │ ├── inter-latin-ext-wght-normal-5SRY4DMZ.woff2
│ │ ├── inter-latin-ext-wght-normal-GZCIV3NH.woff2
│ │ ├── inter-latin-ext-wght-normal-HA22NDSG.woff2
│ │ ├── inter-vietnamese-wght-normal-CE5GGD3W.woff2
│ │ ├── inter-vietnamese-wght-normal-TWG5UU7E.woff2
│ │ ├── inter-cyrillic-ext-wght-normal-ASVAGXXE.woff2
│ │ ├── inter-cyrillic-ext-wght-normal-IYF56FF6.woff2
│ │ ├── inter-cyrillic-ext-wght-normal-XKHXBTUO.woff2
│ │ └── index.css
├── index.php
├── js
│ └── filament
│ │ ├── forms
│ │ └── components
│ │ │ ├── textarea.js
│ │ │ ├── tags-input.js
│ │ │ ├── key-value.js
│ │ │ ├── checkbox-list.js
│ │ │ └── color-picker.js
│ │ ├── schemas
│ │ ├── components
│ │ │ ├── tabs.js
│ │ │ ├── actions.js
│ │ │ └── wizard.js
│ │ └── schemas.js
│ │ ├── tables
│ │ ├── components
│ │ │ ├── columns
│ │ │ │ ├── checkbox.js
│ │ │ │ ├── toggle.js
│ │ │ │ └── text-input.js
│ │ │ └── table.js
│ │ └── tables.js
│ │ ├── actions
│ │ └── actions.js
│ │ ├── support
│ │ └── async-alpine.js
│ │ ├── notifications
│ │ └── notifications.js
│ │ └── filament
│ │ └── app.js
├── .htaccess
└── css
│ └── filament
│ └── support
│ └── support.css
├── database
├── .gitignore
├── seeders
│ └── DatabaseSeeder.php
├── migrations
│ ├── 2025_02_07_113149_user_timezone.php
│ ├── 2022_12_14_083707_create_settings_table.php
│ ├── 0001_01_01_000001_create_cache_table.php
│ ├── 0001_01_01_000000_create_users_table.php
│ ├── 0001_01_01_000002_create_jobs_table.php
│ └── 2025_07_24_073400_create_permission_tables.php
└── factories
│ └── UserFactory.php
├── bootstrap
├── cache
│ └── .gitignore
├── providers.php
└── app.php
├── resources
├── js
│ ├── app.js
│ └── bootstrap.js
└── css
│ └── app.css
├── storage
├── logs
│ └── .gitignore
├── app
│ ├── private
│ │ └── .gitignore
│ ├── public
│ │ └── .gitignore
│ └── .gitignore
├── debugbar
│ └── .gitignore
└── framework
│ ├── testing
│ └── .gitignore
│ ├── views
│ └── .gitignore
│ ├── cache
│ ├── data
│ │ └── .gitignore
│ └── .gitignore
│ ├── sessions
│ └── .gitignore
│ └── .gitignore
├── app
├── Http
│ └── Controllers
│ │ └── Controller.php
├── Settings
│ └── AppSetting.php
├── Filament
│ ├── Resources
│ │ └── Users
│ │ │ ├── Pages
│ │ │ ├── CreateUser.php
│ │ │ ├── ListUsers.php
│ │ │ └── EditUser.php
│ │ │ └── UserResource.php
│ ├── Pages
│ │ └── AppSetting.php
│ └── User
│ │ └── Pages
│ │ └── EditProfile.php
├── Notifications
│ └── VerifyEmail.php
├── Providers
│ ├── AppServiceProvider.php
│ └── Filament
│ │ ├── AdminPanelProvider.php
│ │ └── UserPanelProvider.php
├── helpers.php
└── Models
│ └── User.php
├── routes
├── web.php
└── console.php
├── tests
├── TestCase.php
├── Unit
│ └── ExampleTest.php
└── Feature
│ └── ExampleTest.php
├── .gitattributes
├── .vscode
├── settings.json
├── extensions.json
└── launch.json
├── .editorconfig
├── mago.toml
├── .gitignore
├── phpstan.neon
├── package.json
├── artisan
├── vite.config.js
├── README.md
├── config
├── services.php
├── filesystems.php
├── settings.php
├── mail.php
├── cache.php
├── auth.php
├── queue.php
├── app.php
├── logging.php
├── essentials.php
├── database.php
├── permission.php
├── session.php
└── ide-helper.php
├── .env.example
├── phpunit.xml
├── rector.php
├── pint.json
└── composer.json
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite*
2 |
--------------------------------------------------------------------------------
/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/resources/js/app.js:
--------------------------------------------------------------------------------
1 | import './bootstrap';
2 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/storage/app/private/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/debugbar/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !private/
3 | !public/
4 | !.gitignore
5 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
8 | })->purpose('Display an inspiring quote');
9 |
--------------------------------------------------------------------------------
/app/Settings/AppSetting.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.{yml,yaml}]
15 | indent_size = 2
16 |
17 | [compose.yaml]
18 | indent_size = 4
19 |
--------------------------------------------------------------------------------
/app/Notifications/VerifyEmail.php:
--------------------------------------------------------------------------------
1 | get('/');
16 |
17 | $testResponse->assertStatus(200);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - vendor/larastan/larastan/extension.neon
3 |
4 | parameters:
5 |
6 | paths:
7 | - app/
8 |
9 | # Level 9 is the highest level
10 | level: 9
11 |
12 | ignoreErrors:
13 | # - '#Unsafe usage of new static#'
14 |
15 | excludePaths:
16 | - ./*/*/FileToBeExcluded.php
17 | - **/node_modules/**
18 | - **/vendor/**
19 | - **/storage/**
20 |
21 | editorUrl: 'vscode://file/%%file%%:%%line%%'
22 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | handleCommand(new ArgvInput);
17 |
18 | exit($status);
19 |
--------------------------------------------------------------------------------
/app/helpers.php:
--------------------------------------------------------------------------------
1 | as(User::class);
12 | }
13 | }
14 |
15 | if (! function_exists('diskPublic')) {
16 | function diskPublic(): FilesystemAdapter
17 | {
18 | return Storage::disk('public');
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import laravel from 'laravel-vite-plugin';
3 | import tailwindcss from '@tailwindcss/vite';
4 |
5 | export default defineConfig({
6 | plugins: [
7 | laravel({
8 | input: ['resources/css/app.css', 'resources/js/app.js'],
9 | refresh: true,
10 | }),
11 | tailwindcss(),
12 | ],
13 | server: {
14 | watch: {
15 | ignored: ['**/storage/framework/views/**'],
16 | },
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Users/Pages/ListUsers.php:
--------------------------------------------------------------------------------
1 | create();
17 |
18 | User::factory()->create([
19 | 'name' => 'Test User',
20 | 'email' => 'test@example.com',
21 | ]);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | withRouting(
9 | web: __DIR__.'/../routes/web.php',
10 | commands: __DIR__.'/../routes/console.php',
11 | health: '/up',
12 | )
13 | ->withMiddleware(function (Middleware $middleware): void {
14 | //
15 | })
16 | ->withExceptions(function (Exceptions $exceptions): void {
17 | //
18 | })->create();
19 |
--------------------------------------------------------------------------------
/app/Filament/Pages/AppSetting.php:
--------------------------------------------------------------------------------
1 | components([
19 | // ...
20 | ]);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | handleRequest(Request::capture());
21 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/textarea.js:
--------------------------------------------------------------------------------
1 | function r({initialHeight:t,shouldAutosize:i,state:s}){return{state:s,wrapperEl:null,init(){this.wrapperEl=this.$el.parentNode,this.setInitialHeight(),i?this.$watch("state",()=>{this.resize()}):this.setUpResizeObserver()},setInitialHeight(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default};
2 |
--------------------------------------------------------------------------------
/database/migrations/2025_02_07_113149_user_timezone.php:
--------------------------------------------------------------------------------
1 | string('timezone')->nullable()->comment('+7 or Asia/Ho_Chi_Minh')->after('remember_token');
16 | });
17 | }
18 |
19 | /**
20 | * Reverse the migrations.
21 | */
22 | public function down(): void
23 | {
24 | //
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Users/Pages/EditUser.php:
--------------------------------------------------------------------------------
1 | id();
13 |
14 | $blueprint->string('group');
15 | $blueprint->string('name');
16 | $blueprint->boolean('locked')->default(false);
17 | $blueprint->json('payload');
18 |
19 | $blueprint->timestamps();
20 |
21 | $blueprint->unique(['group', 'name']);
22 | });
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/tags-input.js:
--------------------------------------------------------------------------------
1 | function s({state:n,splitKeys:a}){return{newTag:"",state:n,createTag(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag(t){this.state=this.state.filter(e=>e!==t)},reorderTags(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{"x-on:blur":"createTag()","x-model":"newTag","x-on:keydown"(t){["Enter",...a].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},"x-on:paste"(){this.$nextTick(()=>{if(a.length===0){this.createTag();return}let t=a.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{s as default};
2 |
--------------------------------------------------------------------------------
/public/js/filament/schemas/components/tabs.js:
--------------------------------------------------------------------------------
1 | function u({activeTab:a,isTabPersistedInQueryString:e,livewireId:h,tab:o,tabQueryStringKey:s}){return{tab:o,init(){let t=this.getTabs(),i=new URLSearchParams(window.location.search);e&&i.has(s)&&t.includes(i.get(s))&&(this.tab=i.get(s)),this.$watch("tab",()=>this.updateQueryString()),(!this.tab||!t.includes(this.tab))&&(this.tab=t[a-1]),Livewire.hook("commit",({component:r,commit:f,succeed:c,fail:l,respond:b})=>{c(({snapshot:d,effect:m})=>{this.$nextTick(()=>{if(r.id!==h)return;let n=this.getTabs();n.includes(this.tab)||(this.tab=n[a-1]??this.tab)})})})},getTabs(){return this.$refs.tabsData?JSON.parse(this.$refs.tabsData.value):[]},updateQueryString(){if(!e)return;let t=new URL(window.location.href);t.searchParams.set(s,this.tab),history.replaceState(null,document.title,t.toString())}}}export{u as default};
2 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Handle X-XSRF-Token Header
13 | RewriteCond %{HTTP:x-xsrf-token} .
14 | RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
15 |
16 | # Redirect Trailing Slashes If Not A Folder...
17 | RewriteCond %{REQUEST_FILENAME} !-d
18 | RewriteCond %{REQUEST_URI} (.+)/$
19 | RewriteRule ^ %1 [L,R=301]
20 |
21 | # Send Requests To Front Controller...
22 | RewriteCond %{REQUEST_FILENAME} !-d
23 | RewriteCond %{REQUEST_FILENAME} !-f
24 | RewriteRule ^ index.php [L]
25 |
26 |
--------------------------------------------------------------------------------
/public/js/filament/tables/components/columns/checkbox.js:
--------------------------------------------------------------------------------
1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:h,respond:u})=>{n(({snapshot:f,effect:d})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||Alpine.raw(this.state)===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{o as default};
2 |
--------------------------------------------------------------------------------
/public/js/filament/tables/components/columns/toggle.js:
--------------------------------------------------------------------------------
1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:h,respond:u})=>{n(({snapshot:f,effect:d})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||Alpine.raw(this.state)===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{o as default};
2 |
--------------------------------------------------------------------------------
/public/js/filament/schemas/components/actions.js:
--------------------------------------------------------------------------------
1 | var i=()=>({isSticky:!1,width:0,resizeObserver:null,boundUpdateWidth:null,init(){let e=this.$el.parentElement;e&&(this.updateWidth(),this.resizeObserver=new ResizeObserver(()=>this.updateWidth()),this.resizeObserver.observe(e),this.boundUpdateWidth=this.updateWidth.bind(this),window.addEventListener("resize",this.boundUpdateWidth))},enableSticky(){this.isSticky=this.$el.getBoundingClientRect().top>0},disableSticky(){this.isSticky=!1},updateWidth(){let e=this.$el.parentElement;if(!e)return;let t=getComputedStyle(this.$root.querySelector(".fi-ac"));this.width=e.offsetWidth+parseInt(t.marginInlineStart,10)*-1+parseInt(t.marginInlineEnd,10)*-1},destroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.boundUpdateWidth&&(window.removeEventListener("resize",this.boundUpdateWidth),this.boundUpdateWidth=null)}});export{i as default};
2 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/key-value.js:
--------------------------------------------------------------------------------
1 | function h({state:r}){return{state:r,rows:[],init(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(e,t)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(e)===0&&s(t)===0||this.updateRows()})},addRow(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow(e){this.rows.splice(e,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows(e){let t=Alpine.raw(this.rows);this.rows=[];let s=t.splice(e.oldIndex,1)[0];t.splice(e.newIndex,0,s),this.$nextTick(()=>{this.rows=t,this.updateState()})},updateRows(){let t=Alpine.raw(this.state).map(({key:s,value:i})=>({key:s,value:i}));this.rows.forEach(s=>{(s.key===""||s.key===null)&&t.push({key:"",value:s.value})}),this.rows=t},updateState(){let e=[];this.rows.forEach(t=>{t.key===""||t.key===null||e.push({key:t.key,value:t.value})}),JSON.stringify(this.state)!==JSON.stringify(e)&&(this.state=e)}}}export{h as default};
2 |
--------------------------------------------------------------------------------
/public/js/filament/actions/actions.js:
--------------------------------------------------------------------------------
1 | (()=>{var n=({livewireId:e})=>({actionNestingIndex:null,init(){window.addEventListener("sync-action-modals",t=>{t.detail.id===e&&this.syncActionModals(t.detail.newActionNestingIndex)})},syncActionModals(t){if(this.actionNestingIndex===t){this.actionNestingIndex!==null&&this.$nextTick(()=>this.openModal());return}if(this.actionNestingIndex!==null&&this.closeModal(),this.actionNestingIndex=t,this.actionNestingIndex!==null){if(!this.$el.querySelector(`#${this.generateModalId(t)}`)){this.$nextTick(()=>this.openModal());return}this.openModal()}},generateModalId(t){return`fi-${e}-action-`+t},openModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("open-modal",{bubbles:!0,composed:!0,detail:{id:t}}))},closeModal(){let t=this.generateModalId(this.actionNestingIndex);document.dispatchEvent(new CustomEvent("close-modal-quietly",{bubbles:!0,composed:!0,detail:{id:t}}))}});document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentActionModals",n)});})();
2 |
--------------------------------------------------------------------------------
/public/js/filament/tables/components/columns/text-input.js:
--------------------------------------------------------------------------------
1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:d,respond:u})=>{n(({snapshot:f,effect:h})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||this.getNormalizedState()===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||this.getNormalizedState()===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.getNormalizedState()),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[null,void 0].includes(this.$refs.serverState.value)?"":this.$refs.serverState.value.replaceAll('\\"','"')},getNormalizedState(){let e=Alpine.raw(this.state);return[null,void 0].includes(e)?"":e}}}export{o as default};
2 |
--------------------------------------------------------------------------------
/app/Filament/User/Pages/EditProfile.php:
--------------------------------------------------------------------------------
1 | components([
20 | FileUpload::make('avatar')
21 | ->hiddenLabel()
22 | ->disk('public')
23 | ->directory('profile-photos')
24 | ->avatar()
25 | ->alignCenter(),
26 | $this->getNameFormComponent(),
27 | $this->getEmailFormComponent(),
28 | $this->getPasswordFormComponent(),
29 | $this->getPasswordConfirmationFormComponent(),
30 | $this->getCurrentPasswordFormComponent(),
31 | ]);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000001_create_cache_table.php:
--------------------------------------------------------------------------------
1 | string('key')->primary();
16 | $blueprint->mediumText('value');
17 | $blueprint->integer('expiration');
18 | });
19 |
20 | Schema::create('cache_locks', function (Blueprint $blueprint): void {
21 | $blueprint->string('key')->primary();
22 | $blueprint->string('owner');
23 | $blueprint->integer('expiration');
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | */
30 | public function down(): void
31 | {
32 | Schema::dropIfExists('cache');
33 | Schema::dropIfExists('cache_locks');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cài đặt
2 |
3 | Hãy bấm nút `Use this template` > `Create a new repository`
4 |
5 | ```bash
6 | composer install
7 | cp .env.example .env
8 | php artisan key:generate --force
9 | php artisan storage:link
10 | php artisan migrate
11 | # tạo tài khoản admin, nhập admin@example.com và password
12 | php artisan make:filament-user
13 | ```
14 |
15 | ## Đồng bộ code từ template này
16 |
17 | ```bash
18 | git remote add template https://github.com/flashteamdev/laravel-skeleton.git
19 | git fetch --all
20 | git merge template/main --allow-unrelated-histories
21 | # nếu conflict xảy ra, chạy lệnh này
22 | git diff --name-only --diff-filter=U | xargs git checkout --ours --
23 | ```
24 |
25 | ## VS Code Extension
26 |
27 | Mở VS Code Extension gõ @recommended và cài toàn bộ `WORKSPACE RECOMMENDATIONS` extension.
28 |
29 | ## Format Code
30 |
31 | Vui lòng chạy lệnh dưới đây trước khi gửi Pull Request!
32 |
33 | ```bash
34 | composer ide
35 | php artisan test
36 | ```
37 |
38 | ## Test Coverage
39 |
40 | ```bash
41 | php artisan test --coverage
42 | ```
43 |
44 | Mở trình duyệt http://127.0.0.1:8000/coverage/index.html để xem kết quả
45 |
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | [
18 | 'key' => env('POSTMARK_API_KEY'),
19 | ],
20 |
21 | 'resend' => [
22 | 'key' => env('RESEND_API_KEY'),
23 | ],
24 |
25 | 'ses' => [
26 | 'key' => env('AWS_ACCESS_KEY_ID'),
27 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
28 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
29 | ],
30 |
31 | 'slack' => [
32 | 'notifications' => [
33 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
34 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
35 | ],
36 | ],
37 |
38 | ];
39 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class UserFactory extends Factory
13 | {
14 | /**
15 | * The current password being used by the factory.
16 | */
17 | protected static ?string $password = null;
18 |
19 | /**
20 | * Define the model's default state.
21 | *
22 | * @return array
23 | */
24 | public function definition(): array
25 | {
26 | return [
27 | 'name' => fake()->name(),
28 | 'email' => fake()->unique()->safeEmail(),
29 | 'email_verified_at' => now(),
30 | 'password' => static::$password ??= Hash::make('password'),
31 | 'remember_token' => Str::random(10),
32 | ];
33 | }
34 |
35 | /**
36 | * Indicate that the model's email address should be unverified.
37 | */
38 | public function unverified(): static
39 | {
40 | return $this->state(fn (array $attributes): array => [
41 | 'email_verified_at' => null,
42 | ]);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/js/filament/schemas/components/wizard.js:
--------------------------------------------------------------------------------
1 | function o({isSkippable:s,isStepPersistedInQueryString:i,key:r,startStep:h,stepQueryStringKey:n}){return{step:null,init(){this.$watch("step",()=>this.updateQueryString()),this.step=this.getSteps().at(h-1),this.autofocusFields()},async requestNextStep(){await this.$wire.callSchemaComponentMethod(r,"nextStep",{currentStepIndex:this.getStepIndex(this.step)})},goToNextStep(){let t=this.getStepIndex(this.step)+1;t>=this.getSteps().length||(this.step=this.getSteps()[t],this.autofocusFields(),this.scroll())},goToPreviousStep(){let t=this.getStepIndex(this.step)-1;t<0||(this.step=this.getSteps()[t],this.autofocusFields(),this.scroll())},scroll(){this.$nextTick(()=>{this.$refs.header?.children[this.getStepIndex(this.step)].scrollIntoView({behavior:"smooth",block:"start"})})},autofocusFields(){this.$nextTick(()=>this.$refs[`step-${this.step}`].querySelector("[autofocus]")?.focus())},getStepIndex(t){let e=this.getSteps().findIndex(p=>p===t);return e===-1?0:e},getSteps(){return JSON.parse(this.$refs.stepsData.value)},isFirstStep(){return this.getStepIndex(this.step)<=0},isLastStep(){return this.getStepIndex(this.step)+1>=this.getSteps().length},isStepAccessible(t){return s||this.getStepIndex(this.step)>this.getStepIndex(t)},updateQueryString(){if(!i)return;let t=new URL(window.location.href);t.searchParams.set(n,this.step),history.replaceState(null,document.title,t.toString())}}}export{o as default};
2 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_TIMEZONE=UTC
6 | APP_URL=http://localhost
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | APP_MAINTENANCE_DRIVER=file
13 | # APP_MAINTENANCE_STORE=database
14 |
15 | # PHP_CLI_SERVER_WORKERS=4
16 |
17 | BCRYPT_ROUNDS=12
18 |
19 | LOG_CHANNEL=stack
20 | LOG_STACK=single
21 | LOG_DEPRECATIONS_CHANNEL=null
22 | LOG_LEVEL=debug
23 |
24 | DB_CONNECTION=sqlite
25 | # DB_HOST=127.0.0.1
26 | # DB_PORT=3306
27 | # DB_DATABASE=laravel
28 | # DB_USERNAME=root
29 | # DB_PASSWORD=
30 |
31 | SESSION_DRIVER=database
32 | SESSION_LIFETIME=120
33 | SESSION_ENCRYPT=false
34 | SESSION_PATH=/
35 | SESSION_DOMAIN=null
36 |
37 | BROADCAST_CONNECTION=log
38 | FILESYSTEM_DISK=local
39 | QUEUE_CONNECTION=database
40 |
41 | CACHE_STORE=database
42 | # CACHE_PREFIX=
43 |
44 | MEMCACHED_HOST=127.0.0.1
45 |
46 | REDIS_CLIENT=phpredis
47 | REDIS_HOST=127.0.0.1
48 | REDIS_PASSWORD=null
49 | REDIS_PORT=6379
50 |
51 | MAIL_MAILER=log
52 | MAIL_SCHEME=null
53 | MAIL_HOST=127.0.0.1
54 | MAIL_PORT=2525
55 | MAIL_USERNAME=null
56 | MAIL_PASSWORD=null
57 | MAIL_FROM_ADDRESS="hello@example.com"
58 | MAIL_FROM_NAME="${APP_NAME}"
59 |
60 | AWS_ACCESS_KEY_ID=
61 | AWS_SECRET_ACCESS_KEY=
62 | AWS_DEFAULT_REGION=us-east-1
63 | AWS_BUCKET=
64 | AWS_USE_PATH_STYLE_ENDPOINT=false
65 |
66 | VITE_APP_NAME="${APP_NAME}"
67 |
68 | ADMIN_EMAIL=admin@example.com
69 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/checkbox-list.js:
--------------------------------------------------------------------------------
1 | function c({livewireId:s}){return{areAllCheckboxesChecked:!1,checkboxListOptions:[],search:"",visibleCheckboxListOptions:[],init(){this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.$nextTick(()=>{this.checkIfAllCheckboxesAreChecked()}),Livewire.hook("commit",({component:e,commit:t,succeed:i,fail:o,respond:h})=>{i(({snapshot:r,effect:l})=>{this.$nextTick(()=>{e.id===s&&(this.checkboxListOptions=Array.from(this.$root.querySelectorAll(".fi-fo-checkbox-list-option")),this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked())})})}),this.$watch("search",()=>{this.updateVisibleCheckboxListOptions(),this.checkIfAllCheckboxesAreChecked()})},checkIfAllCheckboxesAreChecked(){this.areAllCheckboxesChecked=this.visibleCheckboxListOptions.length===this.visibleCheckboxListOptions.filter(e=>e.querySelector("input[type=checkbox]:checked, input[type=checkbox]:disabled")).length},toggleAllCheckboxes(){this.checkIfAllCheckboxesAreChecked();let e=!this.areAllCheckboxesChecked;this.visibleCheckboxListOptions.forEach(t=>{let i=t.querySelector("input[type=checkbox]");i.disabled||i.checked!==e&&(i.checked=e,i.dispatchEvent(new Event("change")))}),this.areAllCheckboxesChecked=e},updateVisibleCheckboxListOptions(){this.visibleCheckboxListOptions=this.checkboxListOptions.filter(e=>["",null,void 0].includes(this.search)||e.querySelector(".fi-fo-checkbox-list-option-label")?.innerText.toLowerCase().includes(this.search.toLowerCase())?!0:e.querySelector(".fi-fo-checkbox-list-option-description")?.innerText.toLowerCase().includes(this.search.toLowerCase()))}}}export{c as default};
2 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | tests/Unit
10 |
11 |
12 | tests/Feature
13 |
14 |
15 |
16 |
17 | app
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Listen for Xdebug",
9 | "type": "php",
10 | "request": "launch",
11 | "port": 9003
12 | },
13 | {
14 | "name": "Launch currently open script",
15 | "type": "php",
16 | "request": "launch",
17 | "program": "${file}",
18 | "cwd": "${fileDirname}",
19 | "port": 0,
20 | "runtimeArgs": [
21 | "-dxdebug.start_with_request=yes"
22 | ],
23 | "env": {
24 | "XDEBUG_MODE": "debug,develop",
25 | "XDEBUG_CONFIG": "client_port=${port}"
26 | }
27 | },
28 | {
29 | "name": "Launch Built-in web server",
30 | "type": "php",
31 | "request": "launch",
32 | "runtimeArgs": [
33 | "-dxdebug.mode=debug",
34 | "-dxdebug.start_with_request=yes",
35 | "-S",
36 | "localhost:0"
37 | ],
38 | "program": "",
39 | "cwd": "${workspaceRoot}",
40 | "port": 9003,
41 | "serverReadyAction": {
42 | "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
43 | "uriFormat": "http://localhost:%s",
44 | "action": "openExternally"
45 | }
46 | }
47 | ]
48 | }
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $blueprint->string('name');
17 | $blueprint->string('email')->unique();
18 | $blueprint->string('avatar')->nullable();
19 | $blueprint->timestamp('email_verified_at')->nullable();
20 | $blueprint->string('password');
21 | $blueprint->rememberToken();
22 | $blueprint->timestamps();
23 | });
24 |
25 | Schema::create('password_reset_tokens', function (Blueprint $blueprint): void {
26 | $blueprint->string('email')->primary();
27 | $blueprint->string('token');
28 | $blueprint->timestamp('created_at')->nullable();
29 | });
30 |
31 | Schema::create('sessions', function (Blueprint $blueprint): void {
32 | $blueprint->string('id')->primary();
33 | $blueprint->foreignId('user_id')->nullable()->index();
34 | $blueprint->string('ip_address', 45)->nullable();
35 | $blueprint->text('user_agent')->nullable();
36 | $blueprint->longText('payload');
37 | $blueprint->integer('last_activity')->index();
38 | });
39 | }
40 |
41 | /**
42 | * Reverse the migrations.
43 | */
44 | public function down(): void
45 | {
46 | Schema::dropIfExists('users');
47 | Schema::dropIfExists('password_reset_tokens');
48 | Schema::dropIfExists('sessions');
49 | }
50 | };
51 |
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/index.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url("./inter-cyrillic-ext-wght-normal-IYF56FF6.woff2") format("woff2-variations");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url("./inter-cyrillic-wght-normal-JEOLYBOO.woff2") format("woff2-variations");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url("./inter-greek-ext-wght-normal-EOVOK2B5.woff2") format("woff2-variations");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url("./inter-greek-wght-normal-IRE366VL.woff2") format("woff2-variations");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url("./inter-vietnamese-wght-normal-CE5GGD3W.woff2") format("woff2-variations");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url("./inter-latin-ext-wght-normal-HA22NDSG.woff2") format("woff2-variations");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url("./inter-latin-wght-normal-NRMW37G5.woff2") format("woff2-variations");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
2 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | withPaths([
15 | __DIR__.'/app',
16 | __DIR__.'/bootstrap/app.php',
17 | // __DIR__.'/config',
18 | __DIR__.'/database',
19 | __DIR__.'/public',
20 | // __DIR__.'/tests',
21 | ])
22 | ->withSkipPath(__DIR__.'/app/Filament')
23 | // ->withSkipPath(__DIR__.'/app/Livewire')
24 | ->withSkip([
25 | StaticArrowFunctionRector::class,
26 | StaticClosureRector::class,
27 | AddOverrideAttributeToOverriddenMethodsRector::class,
28 | EncapsedStringsToSprintfRector::class,
29 | ])
30 | // here we can define, what prepared sets of rules will be applied
31 | ->withPreparedSets(
32 | deadCode: true,
33 | codeQuality: true,
34 | codingStyle: true,
35 | typeDeclarations: true,
36 | privatization: true,
37 | naming: true,
38 | instanceOf: true,
39 | earlyReturn: true,
40 | strictBooleans: true,
41 | )
42 | ->withPhpSets(php84: true)
43 | ->withSets([
44 | LaravelLevelSetList::UP_TO_LARAVEL_110,
45 | LaravelSetList::LARAVEL_ARRAY_STR_FUNCTION_TO_STATIC_CALL,
46 | LaravelSetList::LARAVEL_CODE_QUALITY,
47 | LaravelSetList::LARAVEL_ELOQUENT_MAGIC_METHOD_TO_QUERY_BUILDER,
48 | LaravelSetList::LARAVEL_FACADE_ALIASES_TO_FULL_NAMES,
49 | LaravelSetList::LARAVEL_LEGACY_FACTORIES_TO_CLASSES,
50 | LaravelSetList::LARAVEL_IF_HELPERS,
51 | LaravelSetList::LARAVEL_CONTAINER_STRING_TO_FULLY_QUALIFIED_NAME,
52 | LaravelSetList::LARAVEL_COLLECTION,
53 | ]);
54 |
--------------------------------------------------------------------------------
/pint.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "laravel",
3 | "notPath": [
4 | "tests/TestCase.php"
5 | ],
6 | "rules": {
7 | "array_push": true,
8 | "backtick_to_shell_exec": true,
9 | "date_time_immutable": true,
10 | "declare_strict_types": true,
11 | "lowercase_keywords": true,
12 | "lowercase_static_reference": true,
13 | "final_class": true,
14 | "final_internal_class": true,
15 | "final_public_method_for_abstract_class": true,
16 | "fully_qualified_strict_types": true,
17 | "global_namespace_import": {
18 | "import_classes": true,
19 | "import_constants": true,
20 | "import_functions": true
21 | },
22 | "mb_str_functions": true,
23 | "modernize_types_casting": true,
24 | "new_with_parentheses": false,
25 | "no_superfluous_elseif": true,
26 | "no_useless_else": true,
27 | "no_multiple_statements_per_line": true,
28 | "ordered_class_elements": {
29 | "order": [
30 | "use_trait",
31 | "case",
32 | "constant",
33 | "constant_public",
34 | "constant_protected",
35 | "constant_private",
36 | "property_public",
37 | "property_protected",
38 | "property_private",
39 | "construct",
40 | "destruct",
41 | "magic",
42 | "phpunit",
43 | "method_abstract",
44 | "method_public_static",
45 | "method_public",
46 | "method_protected_static",
47 | "method_protected",
48 | "method_private_static",
49 | "method_private"
50 | ],
51 | "sort_algorithm": "none"
52 | },
53 | "ordered_interfaces": true,
54 | "ordered_traits": true,
55 | "protected_to_private": true,
56 | "self_accessor": true,
57 | "self_static_accessor": true,
58 | "strict_comparison": true,
59 | "visibility_required": true
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000002_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $blueprint->string('queue')->index();
17 | $blueprint->longText('payload');
18 | $blueprint->unsignedTinyInteger('attempts');
19 | $blueprint->unsignedInteger('reserved_at')->nullable();
20 | $blueprint->unsignedInteger('available_at');
21 | $blueprint->unsignedInteger('created_at');
22 | });
23 |
24 | Schema::create('job_batches', function (Blueprint $blueprint): void {
25 | $blueprint->string('id')->primary();
26 | $blueprint->string('name');
27 | $blueprint->integer('total_jobs');
28 | $blueprint->integer('pending_jobs');
29 | $blueprint->integer('failed_jobs');
30 | $blueprint->longText('failed_job_ids');
31 | $blueprint->mediumText('options')->nullable();
32 | $blueprint->integer('cancelled_at')->nullable();
33 | $blueprint->integer('created_at');
34 | $blueprint->integer('finished_at')->nullable();
35 | });
36 |
37 | Schema::create('failed_jobs', function (Blueprint $blueprint): void {
38 | $blueprint->id();
39 | $blueprint->string('uuid')->unique();
40 | $blueprint->text('connection');
41 | $blueprint->text('queue');
42 | $blueprint->longText('payload');
43 | $blueprint->longText('exception');
44 | $blueprint->timestamp('failed_at')->useCurrent();
45 | });
46 | }
47 |
48 | /**
49 | * Reverse the migrations.
50 | */
51 | public function down(): void
52 | {
53 | Schema::dropIfExists('jobs');
54 | Schema::dropIfExists('job_batches');
55 | Schema::dropIfExists('failed_jobs');
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/public/js/filament/schemas/schemas.js:
--------------------------------------------------------------------------------
1 | (()=>{var d=()=>({isSticky:!1,width:0,resizeObserver:null,boundUpdateWidth:null,init(){let t=this.$el.parentElement;t&&(this.updateWidth(),this.resizeObserver=new ResizeObserver(()=>this.updateWidth()),this.resizeObserver.observe(t),this.boundUpdateWidth=this.updateWidth.bind(this),window.addEventListener("resize",this.boundUpdateWidth))},enableSticky(){this.isSticky=this.$el.getBoundingClientRect().top>0},disableSticky(){this.isSticky=!1},updateWidth(){let t=this.$el.parentElement;if(!t)return;let e=getComputedStyle(this.$root.querySelector(".fi-ac"));this.width=t.offsetWidth+parseInt(e.marginInlineStart,10)*-1+parseInt(e.marginInlineEnd,10)*-1},destroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.boundUpdateWidth&&(window.removeEventListener("resize",this.boundUpdateWidth),this.boundUpdateWidth=null)}});var u=function(t,e,n){let i=t;if(e.startsWith("/")&&(n=!0,e=e.slice(1)),n)return e;for(;e.startsWith("../");)i=i.includes(".")?i.slice(0,i.lastIndexOf(".")):null,e=e.slice(3);return["",null,void 0].includes(i)?e:["",null,void 0].includes(e)?i:`${i}.${e}`},c=t=>{let e=Alpine.findClosest(t,n=>n.__livewire);if(!e)throw"Could not find Livewire component in DOM tree.";return e.__livewire};document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentSchema",({livewireId:t})=>({handleFormValidationError(e){e.detail.livewireId===t&&this.$nextTick(()=>{let n=this.$el.querySelector("[data-validation-error]");if(!n)return;let i=n;for(;i;)i.dispatchEvent(new CustomEvent("expand")),i=i.parentNode;setTimeout(()=>n.closest("[data-field-wrapper]").scrollIntoView({behavior:"smooth",block:"start",inline:"start"}),200)})},isStateChanged(e,n){if(e===void 0)return!1;try{return JSON.stringify(e)!==JSON.stringify(n)}catch{return e!==n}}})),window.Alpine.data("filamentSchemaComponent",({path:t,containerPath:e,$wire:n})=>({$statePath:t,$get:(i,s)=>n.$get(u(e,i,s)),$set:(i,s,a,o=!1)=>n.$set(u(e,i,a),s,o),get $state(){return n.$get(t)}})),window.Alpine.data("filamentActionsSchemaComponent",d),Livewire.hook("commit",({component:t,commit:e,respond:n,succeed:i,fail:s})=>{i(({snapshot:a,effects:o})=>{o.dispatches?.forEach(r=>{if(!r.params?.awaitSchemaComponent)return;let l=Array.from(t.el.querySelectorAll(`[wire\\:partial="schema-component::${r.params.awaitSchemaComponent}"]`)).filter(h=>c(h)===t);if(l.length!==1){if(l.length>1)throw`Multiple schema components found with key [${r.params.awaitSchemaComponent}].`;window.addEventListener(`schema-component-${t.id}-${r.params.awaitSchemaComponent}-loaded`,()=>{window.dispatchEvent(new CustomEvent(r.name,{detail:r.params}))},{once:!0})}})})})});})();
2 |
--------------------------------------------------------------------------------
/app/Providers/Filament/AdminPanelProvider.php:
--------------------------------------------------------------------------------
1 | default()
29 | ->id('admin')
30 | ->path('admin')
31 | ->login()
32 | ->colors([
33 | 'primary' => Color::Amber,
34 | ])
35 | ->discoverResources(
36 | in: app_path('Filament/Resources'),
37 | for: 'App\\Filament\\Resources',
38 | )
39 | ->discoverPages(
40 | in: app_path('Filament/Pages'),
41 | for: 'App\\Filament\\Pages',
42 | )
43 | ->pages([
44 | Dashboard::class,
45 | ])
46 | ->discoverWidgets(
47 | in: app_path('Filament/Widgets'),
48 | for: 'App\\Filament\\Widgets',
49 | )
50 | ->widgets([
51 | AccountWidget::class,
52 | FilamentInfoWidget::class,
53 | ])
54 | ->plugin(FilamentSpatieRolesPermissionsPlugin::make())
55 | ->middleware([
56 | EncryptCookies::class,
57 | AddQueuedCookiesToResponse::class,
58 | StartSession::class,
59 | AuthenticateSession::class,
60 | ShareErrorsFromSession::class,
61 | VerifyCsrfToken::class,
62 | SubstituteBindings::class,
63 | DisableBladeIconComponents::class,
64 | DispatchServingFilamentEvent::class,
65 | ])
66 | ->authMiddleware([
67 | Authenticate::class,
68 | ]);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/config/filesystems.php:
--------------------------------------------------------------------------------
1 | env('FILESYSTEM_DISK', 'local'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Filesystem Disks
21 | |--------------------------------------------------------------------------
22 | |
23 | | Below you may configure as many filesystem disks as necessary, and you
24 | | may even configure multiple disks for the same driver. Examples for
25 | | most supported storage drivers are configured here for reference.
26 | |
27 | | Supported drivers: "local", "ftp", "sftp", "s3"
28 | |
29 | */
30 |
31 | 'disks' => [
32 |
33 | 'local' => [
34 | 'driver' => 'local',
35 | 'root' => storage_path('app/private'),
36 | 'serve' => true,
37 | 'throw' => false,
38 | 'report' => false,
39 | ],
40 |
41 | 'public' => [
42 | 'driver' => 'local',
43 | 'root' => storage_path('app/public'),
44 | 'url' => env('APP_URL').'/storage',
45 | 'visibility' => 'public',
46 | 'throw' => false,
47 | 'report' => false,
48 | ],
49 |
50 | 's3' => [
51 | 'driver' => 's3',
52 | 'key' => env('AWS_ACCESS_KEY_ID'),
53 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
54 | 'region' => env('AWS_DEFAULT_REGION'),
55 | 'bucket' => env('AWS_BUCKET'),
56 | 'url' => env('AWS_URL'),
57 | 'endpoint' => env('AWS_ENDPOINT'),
58 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
59 | 'throw' => false,
60 | 'report' => false,
61 | ],
62 |
63 | ],
64 |
65 | /*
66 | |--------------------------------------------------------------------------
67 | | Symbolic Links
68 | |--------------------------------------------------------------------------
69 | |
70 | | Here you may configure the symbolic links that will be created when the
71 | | `storage:link` Artisan command is executed. The array keys should be
72 | | the locations of the links and the values should be their targets.
73 | |
74 | */
75 |
76 | 'links' => [
77 | public_path('storage') => storage_path('app/public'),
78 | ],
79 |
80 | ];
81 |
--------------------------------------------------------------------------------
/app/Providers/Filament/UserPanelProvider.php:
--------------------------------------------------------------------------------
1 | id('user')
31 | ->path('user')
32 | ->login()
33 | ->registration()
34 | ->passwordReset()
35 | ->emailVerification()
36 | ->emailChangeVerification()
37 | ->profile(EditProfile::class, isSimple: false)
38 | ->navigationItems([
39 | NavigationItem::make(__('Profile'))
40 | ->url('/user/profile')
41 | ->icon('heroicon-o-user-circle')
42 | ->sort(1),
43 | ])
44 | ->colors([
45 | 'primary' => Color::Amber,
46 | ])
47 | ->discoverResources(in: app_path('Filament/User/Resources'), for: 'App\Filament\User\Resources')
48 | ->discoverPages(in: app_path('Filament/User/Pages'), for: 'App\Filament\User\Pages')
49 | ->pages([
50 | Dashboard::class,
51 | ])
52 | ->discoverWidgets(in: app_path('Filament/User/Widgets'), for: 'App\Filament\User\Widgets')
53 | ->widgets([
54 | AccountWidget::class,
55 | ])
56 | ->middleware([
57 | EncryptCookies::class,
58 | AddQueuedCookiesToResponse::class,
59 | StartSession::class,
60 | AuthenticateSession::class,
61 | ShareErrorsFromSession::class,
62 | VerifyCsrfToken::class,
63 | SubstituteBindings::class,
64 | DisableBladeIconComponents::class,
65 | DispatchServingFilamentEvent::class,
66 | ])
67 | ->authMiddleware([
68 | Authenticate::class,
69 | ]);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/public/css/filament/support/support.css:
--------------------------------------------------------------------------------
1 | .fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3}
--------------------------------------------------------------------------------
/public/js/filament/tables/components/table.js:
--------------------------------------------------------------------------------
1 | function d(){return{checkboxClickController:null,collapsedGroups:[],isLoading:!1,selectedRecords:[],shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,init:function(){this.livewireId=this.$root.closest("[wire\\:id]").attributes["wire:id"].value,this.$wire.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),this.$watch("selectedRecords",()=>{if(!this.shouldCheckUniqueSelection){this.shouldCheckUniqueSelection=!0;return}this.selectedRecords=[...new Set(this.selectedRecords)],this.shouldCheckUniqueSelection=!1}),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction:function(e,t=null){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableAction(e,t)},mountBulkAction:function(e){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableBulkAction(e)},toggleSelectRecordsOnPage:function(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecordsInGroup:async function(e){this.isLoading=!0;let t=await this.$wire.getGroupedSelectableTableRecordKeys(e);this.areRecordsSelected(this.getRecordsInGroupOnPage(e))?this.deselectRecords(t):this.selectRecords(t),this.isLoading=!1},getRecordsInGroupOnPage:function(e){let t=[];for(let s of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])s.dataset.group===e&&t.push(s.value);return t},getRecordsOnPage:function(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords:function(e){for(let t of e)this.isRecordSelected(t)||this.selectedRecords.push(t)},deselectRecords:function(e){for(let t of e){let s=this.selectedRecords.indexOf(t);s!==-1&&this.selectedRecords.splice(s,1)}},selectAllRecords:async function(){this.isLoading=!0,this.selectedRecords=await this.$wire.getAllSelectableTableRecordKeys(),this.isLoading=!1},deselectAllRecords:function(){this.selectedRecords=[]},isRecordSelected:function(e){return this.selectedRecords.includes(e)},areRecordsSelected:function(e){return e.every(t=>this.isRecordSelected(t))},toggleCollapseGroup:function(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed:function(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups:function(){this.collapsedGroups=[]},watchForCheckboxClicks:function(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:e}=this.checkboxClickController;this.$root?.addEventListener("click",t=>t.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(t,t.target),{signal:e})},handleCheckboxClick:function(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let s=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!s.includes(this.lastChecked)){this.lastChecked=t;return}let l=s.indexOf(this.lastChecked),r=s.indexOf(t),o=[l,r].sort((c,n)=>c-n),i=[];for(let c=o[0];c<=o[1];c++)s[c].checked=t.checked,i.push(s[c].value);t.checked?this.selectRecords(i):this.deselectRecords(i)}this.lastChecked=t}}}export{d as default};
2 |
--------------------------------------------------------------------------------
/config/settings.php:
--------------------------------------------------------------------------------
1 | [
10 |
11 | ],
12 |
13 | /*
14 | * The path where the settings classes will be created.
15 | */
16 | 'setting_class_path' => app_path('Settings'),
17 |
18 | /*
19 | * In these directories settings migrations will be stored and ran when migrating. A settings
20 | * migration created via the make:settings-migration command will be stored in the first path or
21 | * a custom defined path when running the command.
22 | */
23 | 'migrations_paths' => [
24 | database_path('settings'),
25 | ],
26 |
27 | /*
28 | * When no repository was set for a settings class the following repository
29 | * will be used for loading and saving settings.
30 | */
31 | 'default_repository' => 'database',
32 |
33 | /*
34 | * Settings will be stored and loaded from these repositories.
35 | */
36 | 'repositories' => [
37 | 'database' => [
38 | 'type' => Spatie\LaravelSettings\SettingsRepositories\DatabaseSettingsRepository::class,
39 | 'model' => null,
40 | 'table' => null,
41 | 'connection' => null,
42 | ],
43 | 'redis' => [
44 | 'type' => Spatie\LaravelSettings\SettingsRepositories\RedisSettingsRepository::class,
45 | 'connection' => null,
46 | 'prefix' => null,
47 | ],
48 | ],
49 |
50 | /*
51 | * The encoder and decoder will determine how settings are stored and
52 | * retrieved in the database. By default, `json_encode` and `json_decode`
53 | * are used.
54 | */
55 | 'encoder' => null,
56 | 'decoder' => null,
57 |
58 | /*
59 | * The contents of settings classes can be cached through your application,
60 | * settings will be stored within a provided Laravel store and can have an
61 | * additional prefix.
62 | */
63 | 'cache' => [
64 | 'enabled' => env('SETTINGS_CACHE_ENABLED', false),
65 | 'store' => null,
66 | 'prefix' => null,
67 | 'ttl' => null,
68 | ],
69 |
70 | /*
71 | * These global casts will be automatically used whenever a property within
72 | * your settings class isn't a default PHP type.
73 | */
74 | 'global_casts' => [
75 | DateTimeInterface::class => Spatie\LaravelSettings\SettingsCasts\DateTimeInterfaceCast::class,
76 | DateTimeZone::class => Spatie\LaravelSettings\SettingsCasts\DateTimeZoneCast::class,
77 | // Spatie\DataTransferObject\DataTransferObject::class => Spatie\LaravelSettings\SettingsCasts\DtoCast::class,
78 | Spatie\LaravelData\Data::class => Spatie\LaravelSettings\SettingsCasts\DataCast::class,
79 | ],
80 |
81 | /*
82 | * The package will look for settings in these paths and automatically
83 | * register them.
84 | */
85 | 'auto_discover_settings' => [
86 | app_path('Settings'),
87 | ],
88 |
89 | /*
90 | * Automatically discovered settings classes can be cached, so they don't
91 | * need to be searched each time the application boots up.
92 | */
93 | 'discovered_settings_cache_path' => base_path('bootstrap/cache'),
94 | ];
95 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Users/UserResource.php:
--------------------------------------------------------------------------------
1 | components([
33 | TextInput::make('name')->required()->maxLength(255),
34 | TextInput::make('email')
35 | ->email()
36 | ->required()
37 | ->maxLength(255),
38 | FileUpload::make('avatar')
39 | ->disk('public')
40 | ->directory('avatars')
41 | ->avatar(),
42 | Select::make('timezone')
43 | ->options(array_combine(DateTimeZone::listIdentifiers(), DateTimeZone::listIdentifiers()))
44 | ->searchable(),
45 | DateTimePicker::make('email_verified_at'),
46 | TextInput::make('password')
47 | ->password()
48 | ->maxLength(255)
49 | ->required(fn ($component, $get, $livewire, $model, $record, $set, $state): bool => $record === null)
50 | ->dehydrateStateUsing(fn ($state) => empty($state) ? '' : Hash::make($state)),
51 | ]);
52 | }
53 |
54 | public static function table(Table $table): Table
55 | {
56 | return $table
57 | ->columns([
58 | TextColumn::make('id')->searchable(),
59 | TextColumn::make('name')->searchable(),
60 | TextColumn::make('email')->searchable(),
61 | ImageColumn::make('avatar')->circular(),
62 | TextColumn::make('email_verified_at')->dateTime()->sortable(),
63 | TextColumn::make('created_at')
64 | ->dateTime()
65 | ->sortable()
66 | ->toggleable(isToggledHiddenByDefault: true),
67 | TextColumn::make('updated_at')
68 | ->dateTime()
69 | ->sortable()
70 | ->toggleable(isToggledHiddenByDefault: true),
71 | ])
72 | ->defaultSort('id', 'desc')
73 | ->filters([
74 | //
75 | ])
76 | ->recordActions([
77 | EditAction::make(),
78 | ])
79 | ->toolbarActions([
80 | BulkActionGroup::make([
81 | DeleteBulkAction::make(),
82 | ]),
83 | ]);
84 | }
85 |
86 | public static function getRelations(): array
87 | {
88 | return [
89 | //
90 | ];
91 | }
92 |
93 | public static function getPages(): array
94 | {
95 | return [
96 | 'index' => ListUsers::route('/'),
97 | 'create' => CreateUser::route('/create'),
98 | 'edit' => EditUser::route('/{record}/edit'),
99 | ];
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://getcomposer.org/schema.json",
3 | "name": "laravel/laravel",
4 | "type": "project",
5 | "description": "The skeleton application for the Laravel framework.",
6 | "keywords": [
7 | "laravel",
8 | "framework"
9 | ],
10 | "license": "MIT",
11 | "require": {
12 | "php": "^8.4",
13 | "althinect/filament-spatie-roles-permissions": "3.x-dev",
14 | "filament/filament": "^4.2",
15 | "filament/spatie-laravel-settings-plugin": "^4.2",
16 | "laravel/framework": "^12.40",
17 | "laravel/tinker": "^2.10",
18 | "nunomaduro/essentials": "^1.0",
19 | "pinkary-project/type-guard": "^0.1.0",
20 | "spatie/laravel-permission": "^6.23",
21 | "spatie/laravel-settings": "^3.5"
22 | },
23 | "require-dev": {
24 | "barryvdh/laravel-debugbar": "^3.15",
25 | "barryvdh/laravel-ide-helper": "^3.5",
26 | "driftingly/rector-laravel": "^2.0",
27 | "fakerphp/faker": "^1.24",
28 | "filament/upgrade": "^4.0",
29 | "larastan/larastan": "^3.2",
30 | "laravel/boost": "^1.0",
31 | "laravel/pail": "^1.2",
32 | "laravel/pint": "^1.21",
33 | "laravel/sail": "^1.41",
34 | "mockery/mockery": "^1.6",
35 | "nunomaduro/collision": "^8.7",
36 | "phpunit/phpunit": "^11.5"
37 | },
38 | "autoload": {
39 | "psr-4": {
40 | "App\\": "app/",
41 | "Database\\Factories\\": "database/factories/",
42 | "Database\\Seeders\\": "database/seeders/"
43 | },
44 | "files": [
45 | "app/helpers.php"
46 | ]
47 | },
48 | "autoload-dev": {
49 | "psr-4": {
50 | "Tests\\": "tests/"
51 | }
52 | },
53 | "scripts": {
54 | "post-autoload-dump": [
55 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
56 | "@php artisan package:discover --ansi",
57 | "@php artisan filament:upgrade"
58 | ],
59 | "post-update-cmd": [
60 | "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
61 | ],
62 | "post-root-package-install": [
63 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
64 | ],
65 | "post-create-project-cmd": [
66 | "@php artisan key:generate --ansi",
67 | "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
68 | "@php artisan migrate --graceful --ansi"
69 | ],
70 | "dev": [
71 | "Composer\\Config::disableProcessTimeout",
72 | "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
73 | ],
74 | "ide": [
75 | "php artisan ide-helper:models --write --reset",
76 | "./vendor/bin/rector",
77 | "./vendor/bin/pint --parallel"
78 | ],
79 | "latest": [
80 | "composer show --no-dev --direct --name-only | xargs composer require"
81 | ],
82 | "format": [
83 | "mago fmt",
84 | "./vendor/bin/pint --parallel"
85 | ]
86 | },
87 | "extra": {
88 | "laravel": {
89 | "dont-discover": []
90 | }
91 | },
92 | "config": {
93 | "optimize-autoloader": true,
94 | "preferred-install": "dist",
95 | "sort-packages": true,
96 | "allow-plugins": {
97 | "pestphp/pest-plugin": true,
98 | "php-http/discovery": true
99 | }
100 | },
101 | "minimum-stability": "stable",
102 | "prefer-stable": true
103 | }
104 |
--------------------------------------------------------------------------------
/config/mail.php:
--------------------------------------------------------------------------------
1 | env('MAIL_MAILER', 'log'),
18 |
19 | /*
20 | |--------------------------------------------------------------------------
21 | | Mailer Configurations
22 | |--------------------------------------------------------------------------
23 | |
24 | | Here you may configure all of the mailers used by your application plus
25 | | their respective settings. Several examples have been configured for
26 | | you and you are free to add your own as your application requires.
27 | |
28 | | Laravel supports a variety of mail "transport" drivers that can be used
29 | | when delivering an email. You may specify which one you're using for
30 | | your mailers below. You may also add additional mailers if needed.
31 | |
32 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
33 | | "postmark", "resend", "log", "array",
34 | | "failover", "roundrobin"
35 | |
36 | */
37 |
38 | 'mailers' => [
39 |
40 | 'smtp' => [
41 | 'transport' => 'smtp',
42 | 'scheme' => env('MAIL_SCHEME'),
43 | 'url' => env('MAIL_URL'),
44 | 'host' => env('MAIL_HOST', '127.0.0.1'),
45 | 'port' => env('MAIL_PORT', 2525),
46 | 'username' => env('MAIL_USERNAME'),
47 | 'password' => env('MAIL_PASSWORD'),
48 | 'timeout' => null,
49 | 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
50 | ],
51 |
52 | 'ses' => [
53 | 'transport' => 'ses',
54 | ],
55 |
56 | 'postmark' => [
57 | 'transport' => 'postmark',
58 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
59 | // 'client' => [
60 | // 'timeout' => 5,
61 | // ],
62 | ],
63 |
64 | 'resend' => [
65 | 'transport' => 'resend',
66 | ],
67 |
68 | 'sendmail' => [
69 | 'transport' => 'sendmail',
70 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
71 | ],
72 |
73 | 'log' => [
74 | 'transport' => 'log',
75 | 'channel' => env('MAIL_LOG_CHANNEL'),
76 | ],
77 |
78 | 'array' => [
79 | 'transport' => 'array',
80 | ],
81 |
82 | 'failover' => [
83 | 'transport' => 'failover',
84 | 'mailers' => [
85 | 'smtp',
86 | 'log',
87 | ],
88 | 'retry_after' => 60,
89 | ],
90 |
91 | 'roundrobin' => [
92 | 'transport' => 'roundrobin',
93 | 'mailers' => [
94 | 'ses',
95 | 'postmark',
96 | ],
97 | 'retry_after' => 60,
98 | ],
99 |
100 | ],
101 |
102 | /*
103 | |--------------------------------------------------------------------------
104 | | Global "From" Address
105 | |--------------------------------------------------------------------------
106 | |
107 | | You may wish for all emails sent by your application to be sent from
108 | | the same address. Here you may specify a name and address that is
109 | | used globally for all emails that are sent by your application.
110 | |
111 | */
112 |
113 | 'from' => [
114 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
115 | 'name' => env('MAIL_FROM_NAME', 'Example'),
116 | ],
117 |
118 | ];
119 |
--------------------------------------------------------------------------------
/config/cache.php:
--------------------------------------------------------------------------------
1 | env('CACHE_STORE', 'database'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Cache Stores
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may define all of the cache "stores" for your application as
26 | | well as their drivers. You may even define multiple stores for the
27 | | same cache driver to group types of items stored in your caches.
28 | |
29 | | Supported drivers: "array", "database", "file", "memcached",
30 | | "redis", "dynamodb", "octane",
31 | | "failover", "null"
32 | |
33 | */
34 |
35 | 'stores' => [
36 |
37 | 'array' => [
38 | 'driver' => 'array',
39 | 'serialize' => false,
40 | ],
41 |
42 | 'database' => [
43 | 'driver' => 'database',
44 | 'connection' => env('DB_CACHE_CONNECTION'),
45 | 'table' => env('DB_CACHE_TABLE', 'cache'),
46 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
47 | 'lock_table' => env('DB_CACHE_LOCK_TABLE'),
48 | ],
49 |
50 | 'file' => [
51 | 'driver' => 'file',
52 | 'path' => storage_path('framework/cache/data'),
53 | 'lock_path' => storage_path('framework/cache/data'),
54 | ],
55 |
56 | 'memcached' => [
57 | 'driver' => 'memcached',
58 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
59 | 'sasl' => [
60 | env('MEMCACHED_USERNAME'),
61 | env('MEMCACHED_PASSWORD'),
62 | ],
63 | 'options' => [
64 | // Memcached::OPT_CONNECT_TIMEOUT => 2000,
65 | ],
66 | 'servers' => [
67 | [
68 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'),
69 | 'port' => env('MEMCACHED_PORT', 11211),
70 | 'weight' => 100,
71 | ],
72 | ],
73 | ],
74 |
75 | 'redis' => [
76 | 'driver' => 'redis',
77 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
78 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
79 | ],
80 |
81 | 'dynamodb' => [
82 | 'driver' => 'dynamodb',
83 | 'key' => env('AWS_ACCESS_KEY_ID'),
84 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
85 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
86 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
87 | 'endpoint' => env('DYNAMODB_ENDPOINT'),
88 | ],
89 |
90 | 'octane' => [
91 | 'driver' => 'octane',
92 | ],
93 |
94 | 'failover' => [
95 | 'driver' => 'failover',
96 | 'stores' => [
97 | 'database',
98 | 'array',
99 | ],
100 | ],
101 |
102 | ],
103 |
104 | /*
105 | |--------------------------------------------------------------------------
106 | | Cache Key Prefix
107 | |--------------------------------------------------------------------------
108 | |
109 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache
110 | | stores, there might be other applications using the same cache. For
111 | | that reason, you may prefix every cache key to avoid collisions.
112 | |
113 | */
114 |
115 | 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
116 |
117 | ];
118 |
--------------------------------------------------------------------------------
/app/Models/User.php:
--------------------------------------------------------------------------------
1 | $notifications
33 | * @property-read int|null $notifications_count
34 | * @property-read \Illuminate\Database\Eloquent\Collection $permissions
35 | * @property-read int|null $permissions_count
36 | * @property-read \Illuminate\Database\Eloquent\Collection $roles
37 | * @property-read int|null $roles_count
38 | *
39 | * @method static \Database\Factories\UserFactory factory($count = null, $state = [])
40 | * @method static Builder|User newModelQuery()
41 | * @method static Builder|User newQuery()
42 | * @method static Builder|User permission($permissions, $without = false)
43 | * @method static Builder|User query()
44 | * @method static Builder|User role($roles, $guard = null, $without = false)
45 | * @method static Builder|User withoutPermission($permissions)
46 | * @method static Builder|User withoutRole($roles, $guard = null)
47 | *
48 | * @mixin \Illuminate\Database\Eloquent\Model
49 | */
50 | class User extends Authenticatable implements FilamentUser, HasAvatar, MustVerifyEmail
51 | {
52 | use AuthMustVerifyEmail;
53 |
54 | /** @use HasFactory */
55 | use HasFactory;
56 |
57 | use HasRoles;
58 | use Notifiable;
59 |
60 | /**
61 | * The attributes that are mass assignable.
62 | *
63 | * @var list
64 | */
65 | protected $fillable = [
66 | 'name',
67 | 'email',
68 | 'avatar',
69 | 'password',
70 | 'timezone',
71 | ];
72 |
73 | /**
74 | * The attributes that should be hidden for serialization.
75 | *
76 | * @var list
77 | */
78 | protected $hidden = [
79 | 'password',
80 | 'remember_token',
81 | ];
82 |
83 | /**
84 | * Get the attributes that should be cast.
85 | *
86 | * @return array
87 | */
88 | protected function casts(): array
89 | {
90 | return [
91 | 'email_verified_at' => 'datetime',
92 | 'password' => 'hashed',
93 | ];
94 | }
95 |
96 | public function isAdmin(): bool
97 | {
98 | return $this->email === config('app.admin_email');
99 | }
100 |
101 | public function canAccessPanel(Panel $panel): bool
102 | {
103 | if ($panel->getId() === 'admin') {
104 | return $this->isAdmin();
105 | }
106 |
107 | return true;
108 | }
109 |
110 | public function getFilamentAvatarUrl(): ?string
111 | {
112 | return $this->avatar
113 | ? diskPublic()->url($this->avatar)
114 | : ('https://gravatar.com/avatar/'.hash('sha256', $this->email));
115 | }
116 |
117 | /**
118 | * Send the email verification notification.
119 | */
120 | public function sendEmailVerificationNotification(): void
121 | {
122 | $this->notify(new VerifyEmail);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/config/auth.php:
--------------------------------------------------------------------------------
1 | [
17 | 'guard' => env('AUTH_GUARD', 'web'),
18 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
19 | ],
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Authentication Guards
24 | |--------------------------------------------------------------------------
25 | |
26 | | Next, you may define every authentication guard for your application.
27 | | Of course, a great default configuration has been defined for you
28 | | which utilizes session storage plus the Eloquent user provider.
29 | |
30 | | All authentication guards have a user provider, which defines how the
31 | | users are actually retrieved out of your database or other storage
32 | | system used by the application. Typically, Eloquent is utilized.
33 | |
34 | | Supported: "session"
35 | |
36 | */
37 |
38 | 'guards' => [
39 | 'web' => [
40 | 'driver' => 'session',
41 | 'provider' => 'users',
42 | ],
43 | ],
44 |
45 | /*
46 | |--------------------------------------------------------------------------
47 | | User Providers
48 | |--------------------------------------------------------------------------
49 | |
50 | | All authentication guards have a user provider, which defines how the
51 | | users are actually retrieved out of your database or other storage
52 | | system used by the application. Typically, Eloquent is utilized.
53 | |
54 | | If you have multiple user tables or models you may configure multiple
55 | | providers to represent the model / table. These providers may then
56 | | be assigned to any extra authentication guards you have defined.
57 | |
58 | | Supported: "database", "eloquent"
59 | |
60 | */
61 |
62 | 'providers' => [
63 | 'users' => [
64 | 'driver' => 'eloquent',
65 | 'model' => env('AUTH_MODEL', App\Models\User::class),
66 | ],
67 |
68 | // 'users' => [
69 | // 'driver' => 'database',
70 | // 'table' => 'users',
71 | // ],
72 | ],
73 |
74 | /*
75 | |--------------------------------------------------------------------------
76 | | Resetting Passwords
77 | |--------------------------------------------------------------------------
78 | |
79 | | These configuration options specify the behavior of Laravel's password
80 | | reset functionality, including the table utilized for token storage
81 | | and the user provider that is invoked to actually retrieve users.
82 | |
83 | | The expiry time is the number of minutes that each reset token will be
84 | | considered valid. This security feature keeps tokens short-lived so
85 | | they have less time to be guessed. You may change this as needed.
86 | |
87 | | The throttle setting is the number of seconds a user must wait before
88 | | generating more password reset tokens. This prevents the user from
89 | | quickly generating a very large amount of password reset tokens.
90 | |
91 | */
92 |
93 | 'passwords' => [
94 | 'users' => [
95 | 'provider' => 'users',
96 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
97 | 'expire' => 60,
98 | 'throttle' => 60,
99 | ],
100 | ],
101 |
102 | /*
103 | |--------------------------------------------------------------------------
104 | | Password Confirmation Timeout
105 | |--------------------------------------------------------------------------
106 | |
107 | | Here you may define the number of seconds before a password confirmation
108 | | window expires and users are asked to re-enter their password via the
109 | | confirmation screen. By default, the timeout lasts for three hours.
110 | |
111 | */
112 |
113 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
114 |
115 | ];
116 |
--------------------------------------------------------------------------------
/config/queue.php:
--------------------------------------------------------------------------------
1 | env('QUEUE_CONNECTION', 'database'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Queue Connections
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure the connection options for every queue backend
24 | | used by your application. An example configuration is provided for
25 | | each backend supported by Laravel. You're also free to add more.
26 | |
27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
28 | | "deferred", "background", "failover", "null"
29 | |
30 | */
31 |
32 | 'connections' => [
33 |
34 | 'sync' => [
35 | 'driver' => 'sync',
36 | ],
37 |
38 | 'database' => [
39 | 'driver' => 'database',
40 | 'connection' => env('DB_QUEUE_CONNECTION'),
41 | 'table' => env('DB_QUEUE_TABLE', 'jobs'),
42 | 'queue' => env('DB_QUEUE', 'default'),
43 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
44 | 'after_commit' => false,
45 | ],
46 |
47 | 'beanstalkd' => [
48 | 'driver' => 'beanstalkd',
49 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
50 | 'queue' => env('BEANSTALKD_QUEUE', 'default'),
51 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
52 | 'block_for' => 0,
53 | 'after_commit' => false,
54 | ],
55 |
56 | 'sqs' => [
57 | 'driver' => 'sqs',
58 | 'key' => env('AWS_ACCESS_KEY_ID'),
59 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
60 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
61 | 'queue' => env('SQS_QUEUE', 'default'),
62 | 'suffix' => env('SQS_SUFFIX'),
63 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
64 | 'after_commit' => false,
65 | ],
66 |
67 | 'redis' => [
68 | 'driver' => 'redis',
69 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
70 | 'queue' => env('REDIS_QUEUE', 'default'),
71 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
72 | 'block_for' => null,
73 | 'after_commit' => false,
74 | ],
75 |
76 | 'deferred' => [
77 | 'driver' => 'deferred',
78 | ],
79 |
80 | 'background' => [
81 | 'driver' => 'background',
82 | ],
83 |
84 | 'failover' => [
85 | 'driver' => 'failover',
86 | 'connections' => [
87 | 'database',
88 | 'deferred',
89 | ],
90 | ],
91 |
92 | ],
93 |
94 | /*
95 | |--------------------------------------------------------------------------
96 | | Job Batching
97 | |--------------------------------------------------------------------------
98 | |
99 | | The following options configure the database and table that store job
100 | | batching information. These options can be updated to any database
101 | | connection and table which has been defined by your application.
102 | |
103 | */
104 |
105 | 'batching' => [
106 | 'database' => env('DB_CONNECTION', 'sqlite'),
107 | 'table' => 'job_batches',
108 | ],
109 |
110 | /*
111 | |--------------------------------------------------------------------------
112 | | Failed Queue Jobs
113 | |--------------------------------------------------------------------------
114 | |
115 | | These options configure the behavior of failed queue job logging so you
116 | | can control how and where failed jobs are stored. Laravel ships with
117 | | support for storing failed jobs in a simple file or in a database.
118 | |
119 | | Supported drivers: "database-uuids", "dynamodb", "file", "null"
120 | |
121 | */
122 |
123 | 'failed' => [
124 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
125 | 'database' => env('DB_CONNECTION', 'sqlite'),
126 | 'table' => 'failed_jobs',
127 | ],
128 |
129 | ];
130 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 | env('APP_NAME', 'Laravel'),
17 | 'admin_email' => env('ADMIN_EMAIL', 'test@example.com'),
18 |
19 | /*
20 | |--------------------------------------------------------------------------
21 | | Application Environment
22 | |--------------------------------------------------------------------------
23 | |
24 | | This value determines the "environment" your application is currently
25 | | running in. This may determine how you prefer to configure various
26 | | services the application utilizes. Set this in your ".env" file.
27 | |
28 | */
29 |
30 | 'env' => env('APP_ENV', 'production'),
31 |
32 | /*
33 | |--------------------------------------------------------------------------
34 | | Application Debug Mode
35 | |--------------------------------------------------------------------------
36 | |
37 | | When your application is in debug mode, detailed error messages with
38 | | stack traces will be shown on every error that occurs within your
39 | | application. If disabled, a simple generic error page is shown.
40 | |
41 | */
42 |
43 | 'debug' => (bool) env('APP_DEBUG', false),
44 |
45 | /*
46 | |--------------------------------------------------------------------------
47 | | Application URL
48 | |--------------------------------------------------------------------------
49 | |
50 | | This URL is used by the console to properly generate URLs when using
51 | | the Artisan command line tool. You should set this to the root of
52 | | the application so that it's available within Artisan commands.
53 | |
54 | */
55 |
56 | 'url' => env('APP_URL', 'http://localhost'),
57 |
58 | /*
59 | |--------------------------------------------------------------------------
60 | | Application Timezone
61 | |--------------------------------------------------------------------------
62 | |
63 | | Here you may specify the default timezone for your application, which
64 | | will be used by the PHP date and date-time functions. The timezone
65 | | is set to "UTC" by default as it is suitable for most use cases.
66 | |
67 | */
68 |
69 | 'timezone' => env('APP_TIMEZONE', 'UTC'),
70 |
71 | /*
72 | |--------------------------------------------------------------------------
73 | | Application Locale Configuration
74 | |--------------------------------------------------------------------------
75 | |
76 | | The application locale determines the default locale that will be used
77 | | by Laravel's translation / localization methods. This option can be
78 | | set to any locale for which you plan to have translation strings.
79 | |
80 | */
81 |
82 | 'locale' => env('APP_LOCALE', 'en'),
83 |
84 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
85 |
86 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
87 |
88 | /*
89 | |--------------------------------------------------------------------------
90 | | Encryption Key
91 | |--------------------------------------------------------------------------
92 | |
93 | | This key is utilized by Laravel's encryption services and should be set
94 | | to a random, 32 character string to ensure that all encrypted values
95 | | are secure. You should do this prior to deploying the application.
96 | |
97 | */
98 |
99 | 'cipher' => 'AES-256-CBC',
100 |
101 | 'key' => env('APP_KEY'),
102 |
103 | 'previous_keys' => [
104 | ...array_filter(
105 | explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
106 | ),
107 | ],
108 |
109 | /*
110 | |--------------------------------------------------------------------------
111 | | Maintenance Mode Driver
112 | |--------------------------------------------------------------------------
113 | |
114 | | These configuration options determine the driver used to determine and
115 | | manage Laravel's "maintenance mode" status. The "cache" driver will
116 | | allow maintenance mode to be controlled across multiple machines.
117 | |
118 | | Supported drivers: "file", "cache"
119 | |
120 | */
121 |
122 | 'maintenance' => [
123 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
124 | 'store' => env('APP_MAINTENANCE_STORE', 'database'),
125 | ],
126 |
127 | ];
128 |
--------------------------------------------------------------------------------
/config/logging.php:
--------------------------------------------------------------------------------
1 | env('LOG_CHANNEL', 'stack'),
22 |
23 | /*
24 | |--------------------------------------------------------------------------
25 | | Deprecations Log Channel
26 | |--------------------------------------------------------------------------
27 | |
28 | | This option controls the log channel that should be used to log warnings
29 | | regarding deprecated PHP and library features. This allows you to get
30 | | your application ready for upcoming major versions of dependencies.
31 | |
32 | */
33 |
34 | 'deprecations' => [
35 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
36 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false),
37 | ],
38 |
39 | /*
40 | |--------------------------------------------------------------------------
41 | | Log Channels
42 | |--------------------------------------------------------------------------
43 | |
44 | | Here you may configure the log channels for your application. Laravel
45 | | utilizes the Monolog PHP logging library, which includes a variety
46 | | of powerful log handlers and formatters that you're free to use.
47 | |
48 | | Available drivers: "single", "daily", "slack", "syslog",
49 | | "errorlog", "monolog", "custom", "stack"
50 | |
51 | */
52 |
53 | 'channels' => [
54 |
55 | 'stack' => [
56 | 'driver' => 'stack',
57 | 'channels' => explode(',', (string) env('LOG_STACK', 'single')),
58 | 'ignore_exceptions' => false,
59 | ],
60 |
61 | 'single' => [
62 | 'driver' => 'single',
63 | 'path' => storage_path('logs/laravel.log'),
64 | 'level' => env('LOG_LEVEL', 'debug'),
65 | 'replace_placeholders' => true,
66 | ],
67 |
68 | 'daily' => [
69 | 'driver' => 'daily',
70 | 'path' => storage_path('logs/laravel.log'),
71 | 'level' => env('LOG_LEVEL', 'debug'),
72 | 'days' => env('LOG_DAILY_DAYS', 14),
73 | 'replace_placeholders' => true,
74 | ],
75 |
76 | 'slack' => [
77 | 'driver' => 'slack',
78 | 'url' => env('LOG_SLACK_WEBHOOK_URL'),
79 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
80 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
81 | 'level' => env('LOG_LEVEL', 'critical'),
82 | 'replace_placeholders' => true,
83 | ],
84 |
85 | 'papertrail' => [
86 | 'driver' => 'monolog',
87 | 'level' => env('LOG_LEVEL', 'debug'),
88 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
89 | 'handler_with' => [
90 | 'host' => env('PAPERTRAIL_URL'),
91 | 'port' => env('PAPERTRAIL_PORT'),
92 | 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
93 | ],
94 | 'processors' => [PsrLogMessageProcessor::class],
95 | ],
96 |
97 | 'stderr' => [
98 | 'driver' => 'monolog',
99 | 'level' => env('LOG_LEVEL', 'debug'),
100 | 'handler' => StreamHandler::class,
101 | 'handler_with' => [
102 | 'stream' => 'php://stderr',
103 | ],
104 | 'formatter' => env('LOG_STDERR_FORMATTER'),
105 | 'processors' => [PsrLogMessageProcessor::class],
106 | ],
107 |
108 | 'syslog' => [
109 | 'driver' => 'syslog',
110 | 'level' => env('LOG_LEVEL', 'debug'),
111 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
112 | 'replace_placeholders' => true,
113 | ],
114 |
115 | 'errorlog' => [
116 | 'driver' => 'errorlog',
117 | 'level' => env('LOG_LEVEL', 'debug'),
118 | 'replace_placeholders' => true,
119 | ],
120 |
121 | 'null' => [
122 | 'driver' => 'monolog',
123 | 'handler' => NullHandler::class,
124 | ],
125 |
126 | 'emergency' => [
127 | 'path' => storage_path('logs/laravel.log'),
128 | ],
129 |
130 | ],
131 |
132 | ];
133 |
--------------------------------------------------------------------------------
/public/js/filament/support/async-alpine.js:
--------------------------------------------------------------------------------
1 | (()=>{(()=>{var d=Object.defineProperty,m=t=>d(t,"__esModule",{value:!0}),f=(t,e)=>{m(t);for(var i in e)d(t,i,{get:e[i],enumerable:!0})},o={};f(o,{eager:()=>g,event:()=>w,idle:()=>y,media:()=>b,visible:()=>E});var c=()=>!0,g=c,v=({component:t,argument:e})=>new Promise(i=>{if(e)window.addEventListener(e,()=>i(),{once:!0});else{let n=a=>{a.detail.id===t.id&&(window.removeEventListener("async-alpine:load",n),i())};window.addEventListener("async-alpine:load",n)}}),w=v,x=()=>new Promise(t=>{"requestIdleCallback"in window?window.requestIdleCallback(t):setTimeout(t,200)}),y=x,A=({argument:t})=>new Promise(e=>{if(!t)return console.log("Async Alpine: media strategy requires a media query. Treating as 'eager'"),e();let i=window.matchMedia(`(${t})`);i.matches?e():i.addEventListener("change",e,{once:!0})}),b=A,$=({component:t,argument:e})=>new Promise(i=>{let n=e||"0px 0px 0px 0px",a=new IntersectionObserver(r=>{r[0].isIntersecting&&(a.disconnect(),i())},{rootMargin:n});a.observe(t.el)}),E=$;function P(t){let e=q(t),i=u(e);return i.type==="method"?{type:"expression",operator:"&&",parameters:[i]}:i}function q(t){let e=/\s*([()])\s*|\s*(\|\||&&|\|)\s*|\s*((?:[^()&|]+\([^()]+\))|[^()&|]+)\s*/g,i=[],n;for(;(n=e.exec(t))!==null;){let[,a,r,s]=n;if(a!==void 0)i.push({type:"parenthesis",value:a});else if(r!==void 0)i.push({type:"operator",value:r==="|"?"&&":r});else{let p={type:"method",method:s.trim()};s.includes("(")&&(p.method=s.substring(0,s.indexOf("(")).trim(),p.argument=s.substring(s.indexOf("(")+1,s.indexOf(")"))),s.method==="immediate"&&(s.method="eager"),i.push(p)}}return i}function u(t){let e=h(t);for(;t.length>0&&(t[0].value==="&&"||t[0].value==="|"||t[0].value==="||");){let i=t.shift().value,n=h(t);e.type==="expression"&&e.operator===i?e.parameters.push(n):e={type:"expression",operator:i,parameters:[e,n]}}return e}function h(t){if(t[0].value==="("){t.shift();let e=u(t);return t[0].value===")"&&t.shift(),e}else return t.shift()}var _="__internal_",l={Alpine:null,_options:{prefix:"ax-",alpinePrefix:"x-",root:"load",inline:"load-src",defaultStrategy:"eager"},_alias:!1,_data:{},_realIndex:0,get _index(){return this._realIndex++},init(t,e={}){return this.Alpine=t,this._options={...this._options,...e},this},start(){return this._processInline(),this._setupComponents(),this._mutations(),this},data(t,e=!1){return this._data[t]={loaded:!1,download:e},this},url(t,e){!t||!e||(this._data[t]||this.data(t),this._data[t].download=()=>import(this._parseUrl(e)))},alias(t){this._alias=t},_processInline(){let t=document.querySelectorAll(`[${this._options.prefix}${this._options.inline}]`);for(let e of t)this._inlineElement(e)},_inlineElement(t){let e=t.getAttribute(`${this._options.alpinePrefix}data`),i=t.getAttribute(`${this._options.prefix}${this._options.inline}`);if(!e||!i)return;let n=this._parseName(e);this.url(n,i)},_setupComponents(){let t=document.querySelectorAll(`[${this._options.prefix}${this._options.root}]`);for(let e of t)this._setupComponent(e)},_setupComponent(t){let e=t.getAttribute(`${this._options.alpinePrefix}data`);t.setAttribute(`${this._options.alpinePrefix}ignore`,"");let i=this._parseName(e),n=t.getAttribute(`${this._options.prefix}${this._options.root}`)||this._options.defaultStrategy;this._componentStrategy({name:i,strategy:n,el:t,id:t.id||this._index})},async _componentStrategy(t){let e=P(t.strategy);await this._generateRequirements(t,e),await this._download(t.name),this._activate(t)},_generateRequirements(t,e){if(e.type==="expression"){if(e.operator==="&&")return Promise.all(e.parameters.map(i=>this._generateRequirements(t,i)));if(e.operator==="||")return Promise.any(e.parameters.map(i=>this._generateRequirements(t,i)))}return o[e.method]?o[e.method]({component:t,argument:e.argument}):!1},async _download(t){if(t.startsWith(_)||(this._handleAlias(t),!this._data[t]||this._data[t].loaded))return;let e=await this._getModule(t);this.Alpine.data(t,e),this._data[t].loaded=!0},async _getModule(t){if(!this._data[t])return;let e=await this._data[t].download(t);return typeof e=="function"?e:e[t]||e.default||Object.values(e)[0]||!1},_activate(t){this.Alpine.destroyTree(t.el),t.el.removeAttribute(`${this._options.alpinePrefix}ignore`),t.el._x_ignore=!1,this.Alpine.initTree(t.el)},_mutations(){new MutationObserver(t=>{for(let e of t)if(e.addedNodes)for(let i of e.addedNodes)i.nodeType===1&&(i.hasAttribute(`${this._options.prefix}${this._options.root}`)&&this._mutationEl(i),i.querySelectorAll(`[${this._options.prefix}${this._options.root}]`).forEach(n=>this._mutationEl(n)))}).observe(document,{attributes:!0,childList:!0,subtree:!0})},_mutationEl(t){t.hasAttribute(`${this._options.prefix}${this._options.inline}`)&&this._inlineElement(t),this._setupComponent(t)},_handleAlias(t){if(!(!this._alias||this._data[t])){if(typeof this._alias=="function"){this.data(t,this._alias);return}this.url(t,this._alias.replaceAll("[name]",t))}},_parseName(t){return(t||"").split(/[({]/g)[0]||`${_}${this._index}`},_parseUrl(t){return new RegExp("^(?:[a-z+]+:)?//","i").test(t)?t:new URL(t,document.baseURI).href}};document.addEventListener("alpine:init",()=>{window.AsyncAlpine=l,l.init(Alpine,window.AsyncAlpineOptions||{}),document.dispatchEvent(new CustomEvent("async-alpine:init")),l.start()})})();})();
2 |
--------------------------------------------------------------------------------
/config/essentials.php:
--------------------------------------------------------------------------------
1 | true,
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Automatically Eager Load Relationships
24 | |--------------------------------------------------------------------------
25 | |
26 | | This option allows you to automatically eagerly load relationships
27 | | for your models. It reduces N+1 query issues and improves
28 | | performance without needing with() everywhere.
29 | |
30 | | Enabled by default.
31 | |
32 | | Note: This option is only available in Laravel 12.8 and above.
33 | |
34 | */
35 |
36 | NunoMaduro\Essentials\Configurables\AutomaticallyEagerLoadRelationships::class => true,
37 |
38 | /*
39 | |--------------------------------------------------------------------------
40 | | Fake Sleep when running tests
41 | |--------------------------------------------------------------------------
42 | |
43 | | This option allows you to fake sleep when running tests. When
44 | | enabled, the framework will fake sleep for all tests.
45 | |
46 | | Enabled by default.
47 | |
48 | */
49 |
50 | NunoMaduro\Essentials\Configurables\FakeSleep::class => true,
51 |
52 | /*
53 | |--------------------------------------------------------------------------
54 | | Force HTTPS scheme
55 | |--------------------------------------------------------------------------
56 | |
57 | | This option allows you to force the HTTPS scheme for your
58 | | application. When enabled, all URLs generated by the Laravel
59 | | will use the HTTPS scheme.
60 | |
61 | | Enabled by default.
62 | |
63 | */
64 |
65 | NunoMaduro\Essentials\Configurables\ForceScheme::class => true,
66 |
67 | /*
68 | |--------------------------------------------------------------------------
69 | | Per-environment overrides
70 | |--------------------------------------------------------------------------
71 | |
72 | | Specify environments for which each configurable should be active.
73 | |
74 | */
75 |
76 | 'environments' => [
77 | NunoMaduro\Essentials\Configurables\ForceScheme::class => ['production'],
78 | ],
79 |
80 | /*
81 | |--------------------------------------------------------------------------
82 | | Immutable Carbon Dates
83 | |--------------------------------------------------------------------------
84 | |
85 | | This option allows you to make all Carbon dates immutable in
86 | | your application. When enabled, all date functions will
87 | | return CarbonImmutable instances instead of Carbon.
88 | |
89 | | Enabled by default.
90 | |
91 | */
92 |
93 | NunoMaduro\Essentials\Configurables\ImmutableDates::class => true,
94 |
95 | /*
96 | |--------------------------------------------------------------------------
97 | | Prevent Stray Requests when running tests
98 | |--------------------------------------------------------------------------
99 | |
100 | | This option allows you to prevent stray requests when running
101 | | tests. When enabled, the framework will prevent requests
102 | | from being sent during tests unless faked.
103 | |
104 | | Enabled by default.
105 | |
106 | */
107 |
108 | NunoMaduro\Essentials\Configurables\PreventStrayRequests::class => true,
109 |
110 | /*
111 | |--------------------------------------------------------------------------
112 | | Prohibit Destructive Commands
113 | |--------------------------------------------------------------------------
114 | |
115 | | This option allows you to prohibit destructive commands
116 | | from being run in your application. When enabled, the
117 | | framework will prevent commands that could potentially
118 | | destroy data from being run in your application.
119 | |
120 | | Enabled by default.
121 | |
122 | */
123 |
124 | NunoMaduro\Essentials\Configurables\ProhibitDestructiveCommands::class => true,
125 |
126 | /*
127 | |--------------------------------------------------------------------------
128 | | Set Default Password complexity
129 | |--------------------------------------------------------------------------
130 | |
131 | | This option sets the default password complexity for your
132 | | application to be at least 12 characters long, maximum
133 | | 255 characters long, and not compromised.
134 | |
135 | | Enabled by default.
136 | |
137 | */
138 |
139 | NunoMaduro\Essentials\Configurables\SetDefaultPassword::class => true,
140 |
141 | /*
142 | |--------------------------------------------------------------------------
143 | | Model should be strict
144 | |--------------------------------------------------------------------------
145 | |
146 | | This option allows you to enable strict mode for your
147 | | application. It will prevent lazy loading, silently discarding
148 | | attributes and prevents accessing missing attributes.
149 | |
150 | | Enabled by default.
151 | |
152 | */
153 |
154 | NunoMaduro\Essentials\Configurables\ShouldBeStrict::class => true,
155 |
156 | /*
157 | |--------------------------------------------------------------------------
158 | | Unguard models
159 | |--------------------------------------------------------------------------
160 | |
161 | | This option allows you to enable unguard mode for your
162 | | models. When enabled, the framework will unguard
163 | | all models, allowing you to mass assign any attributes.
164 | |
165 | | Disabled by default.
166 | |
167 | */
168 |
169 | NunoMaduro\Essentials\Configurables\Unguard::class => false,
170 |
171 | ];
172 |
--------------------------------------------------------------------------------
/public/js/filament/notifications/notifications.js:
--------------------------------------------------------------------------------
1 | (()=>{var O=Object.create;var N=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var H=Object.getPrototypeOf,W=Object.prototype.hasOwnProperty;var d=(i,t)=>()=>(t||i((t={exports:{}}).exports,t),t.exports);var j=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Y(t))!W.call(i,n)&&n!==e&&N(i,n,{get:()=>t[n],enumerable:!(s=V(t,n))||s.enumerable});return i};var J=(i,t,e)=>(e=i!=null?O(H(i)):{},j(t||!i||!i.__esModule?N(e,"default",{value:i,enumerable:!0}):e,i));var S=d((ut,_)=>{var v,g=typeof global<"u"&&(global.crypto||global.msCrypto);g&&g.getRandomValues&&(y=new Uint8Array(16),v=function(){return g.getRandomValues(y),y});var y;v||(T=new Array(16),v=function(){for(var i=0,t;i<16;i++)(i&3)===0&&(t=Math.random()*4294967296),T[i]=t>>>((i&3)<<3)&255;return T});var T;_.exports=v});var C=d((ct,U)=>{var P=[];for(f=0;f<256;++f)P[f]=(f+256).toString(16).substr(1);var f;function K(i,t){var e=t||0,s=P;return s[i[e++]]+s[i[e++]]+s[i[e++]]+s[i[e++]]+"-"+s[i[e++]]+s[i[e++]]+"-"+s[i[e++]]+s[i[e++]]+"-"+s[i[e++]]+s[i[e++]]+"-"+s[i[e++]]+s[i[e++]]+s[i[e++]]+s[i[e++]]+s[i[e++]]+s[i[e++]]}U.exports=K});var R=d((lt,F)=>{var Q=S(),X=C(),a=Q(),Z=[a[0]|1,a[1],a[2],a[3],a[4],a[5]],b=(a[6]<<8|a[7])&16383,D=0,A=0;function tt(i,t,e){var s=t&&e||0,n=t||[];i=i||{};var r=i.clockseq!==void 0?i.clockseq:b,o=i.msecs!==void 0?i.msecs:new Date().getTime(),h=i.nsecs!==void 0?i.nsecs:A+1,l=o-D+(h-A)/1e4;if(l<0&&i.clockseq===void 0&&(r=r+1&16383),(l<0||o>D)&&i.nsecs===void 0&&(h=0),h>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");D=o,A=h,b=r,o+=122192928e5;var c=((o&268435455)*1e4+h)%4294967296;n[s++]=c>>>24&255,n[s++]=c>>>16&255,n[s++]=c>>>8&255,n[s++]=c&255;var u=o/4294967296*1e4&268435455;n[s++]=u>>>8&255,n[s++]=u&255,n[s++]=u>>>24&15|16,n[s++]=u>>>16&255,n[s++]=r>>>8|128,n[s++]=r&255;for(var $=i.node||Z,m=0;m<6;++m)n[s+m]=$[m];return t||X(n)}F.exports=tt});var G=d((dt,B)=>{var it=S(),et=C();function st(i,t,e){var s=t&&e||0;typeof i=="string"&&(t=i=="binary"?new Array(16):null,i=null),i=i||{};var n=i.random||(i.rng||it)();if(n[6]=n[6]&15|64,n[8]=n[8]&63|128,t)for(var r=0;r<16;++r)t[s+r]=n[r];return t||et(n)}B.exports=st});var M=d((ft,L)=>{var nt=R(),I=G(),E=I;E.v1=nt;E.v4=I;L.exports=E});function k(i,t=()=>{}){let e=!1;return function(){e?t.apply(this,arguments):(e=!0,i.apply(this,arguments))}}var q=i=>{i.data("notificationComponent",({notification:t})=>({isShown:!1,computedStyle:null,transitionDuration:null,transitionEasing:null,init(){this.computedStyle=window.getComputedStyle(this.$el),this.transitionDuration=parseFloat(this.computedStyle.transitionDuration)*1e3,this.transitionEasing=this.computedStyle.transitionTimingFunction,this.configureTransitions(),this.configureAnimations(),t.duration&&t.duration!=="persistent"&&setTimeout(()=>{if(!this.$el.matches(":hover")){this.close();return}this.$el.addEventListener("mouseleave",()=>this.close())},t.duration),this.isShown=!0},configureTransitions(){let e=this.computedStyle.display,s=()=>{i.mutateDom(()=>{this.$el.style.setProperty("display",e),this.$el.style.setProperty("visibility","visible")}),this.$el._x_isShown=!0},n=()=>{i.mutateDom(()=>{this.$el._x_isShown?this.$el.style.setProperty("visibility","hidden"):this.$el.style.setProperty("display","none")})},r=k(o=>o?s():n(),o=>{this.$el._x_toggleAndCascadeWithTransitions(this.$el,o,s,n)});i.effect(()=>r(this.isShown))},configureAnimations(){let e;Livewire.hook("commit",({component:s,commit:n,succeed:r,fail:o,respond:h})=>{s.snapshot.data.isFilamentNotificationsComponent&&requestAnimationFrame(()=>{let l=()=>this.$el.getBoundingClientRect().top,c=l();h(()=>{e=()=>{this.isShown&&this.$el.animate([{transform:`translateY(${c-l()}px)`},{transform:"translateY(0px)"}],{duration:this.transitionDuration,easing:this.transitionEasing})},this.$el.getAnimations().forEach(u=>u.finish())}),r(({snapshot:u,effect:$})=>{e()})})})},close(){this.isShown=!1,setTimeout(()=>window.dispatchEvent(new CustomEvent("notificationClosed",{detail:{id:t.id}})),this.transitionDuration)},markAsRead(){window.dispatchEvent(new CustomEvent("markedNotificationAsRead",{detail:{id:t.id}}))},markAsUnread(){window.dispatchEvent(new CustomEvent("markedNotificationAsUnread",{detail:{id:t.id}}))}}))};var z=J(M(),1),p=class{constructor(){return this.id((0,z.v4)()),this}id(t){return this.id=t,this}title(t){return this.title=t,this}body(t){return this.body=t,this}actions(t){return this.actions=t,this}status(t){return this.status=t,this}color(t){return this.color=t,this}icon(t){return this.icon=t,this}iconColor(t){return this.iconColor=t,this}duration(t){return this.duration=t,this}seconds(t){return this.duration(t*1e3),this}persistent(){return this.duration("persistent"),this}danger(){return this.status("danger"),this}info(){return this.status("info"),this}success(){return this.status("success"),this}warning(){return this.status("warning"),this}view(t){return this.view=t,this}viewData(t){return this.viewData=t,this}send(){return window.dispatchEvent(new CustomEvent("notificationSent",{detail:{notification:this}})),this}},w=class{constructor(t){return this.name(t),this}name(t){return this.name=t,this}color(t){return this.color=t,this}dispatch(t,e){return this.event(t),this.eventData(e),this}dispatchSelf(t,e){return this.dispatch(t,e),this.dispatchDirection="self",this}dispatchTo(t,e,s){return this.dispatch(e,s),this.dispatchDirection="to",this.dispatchToComponent=t,this}emit(t,e){return this.dispatch(t,e),this}emitSelf(t,e){return this.dispatchSelf(t,e),this}emitTo(t,e,s){return this.dispatchTo(t,e,s),this}dispatchDirection(t){return this.dispatchDirection=t,this}dispatchToComponent(t){return this.dispatchToComponent=t,this}event(t){return this.event=t,this}eventData(t){return this.eventData=t,this}extraAttributes(t){return this.extraAttributes=t,this}icon(t){return this.icon=t,this}iconPosition(t){return this.iconPosition=t,this}outlined(t=!0){return this.isOutlined=t,this}disabled(t=!0){return this.isDisabled=t,this}label(t){return this.label=t,this}close(t=!0){return this.shouldClose=t,this}openUrlInNewTab(t=!0){return this.shouldOpenUrlInNewTab=t,this}size(t){return this.size=t,this}url(t){return this.url=t,this}view(t){return this.view=t,this}button(){return this.view("filament::components.button.index"),this}grouped(){return this.view("filament::components.dropdown.list.item"),this}iconButton(){return this.view("filament::components.icon-button"),this}link(){return this.view("filament::components.link"),this}},x=class{constructor(t){return this.actions(t),this}actions(t){return this.actions=t.map(e=>e.grouped()),this}color(t){return this.color=t,this}icon(t){return this.icon=t,this}iconPosition(t){return this.iconPosition=t,this}label(t){return this.label=t,this}tooltip(t){return this.tooltip=t,this}};window.FilamentNotificationAction=w;window.FilamentNotificationActionGroup=x;window.FilamentNotification=p;document.addEventListener("alpine:init",()=>{window.Alpine.plugin(q)});})();
2 |
--------------------------------------------------------------------------------
/database/migrations/2025_07_24_073400_create_permission_tables.php:
--------------------------------------------------------------------------------
1 | engine('InnoDB');
25 | $blueprint->bigIncrements('id'); // permission id
26 | $blueprint->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
27 | $blueprint->string('guard_name'); // For MyISAM use string('guard_name', 25);
28 | $blueprint->timestamps();
29 |
30 | $blueprint->unique(['name', 'guard_name']);
31 | });
32 |
33 | Schema::create($tableNames['roles'], static function (Blueprint $blueprint) use ($teams, $columnNames): void {
34 | // $table->engine('InnoDB');
35 | $blueprint->bigIncrements('id'); // role id
36 | if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
37 | $blueprint->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
38 | $blueprint->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
39 | }
40 |
41 | $blueprint->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
42 | $blueprint->string('guard_name'); // For MyISAM use string('guard_name', 25);
43 | $blueprint->timestamps();
44 | if ($teams || config('permission.testing')) {
45 | $blueprint->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
46 | } else {
47 | $blueprint->unique(['name', 'guard_name']);
48 | }
49 | });
50 |
51 | Schema::create($tableNames['model_has_permissions'], static function (Blueprint $blueprint) use ($tableNames, $columnNames, $pivotPermission, $teams): void {
52 | $blueprint->unsignedBigInteger($pivotPermission);
53 |
54 | $blueprint->string('model_type');
55 | $blueprint->unsignedBigInteger($columnNames['model_morph_key']);
56 | $blueprint->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
57 |
58 | $blueprint->foreign($pivotPermission)
59 | ->references('id') // permission id
60 | ->on($tableNames['permissions'])
61 | ->onDelete('cascade');
62 | if ($teams) {
63 | $blueprint->unsignedBigInteger($columnNames['team_foreign_key']);
64 | $blueprint->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
65 |
66 | $blueprint->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
67 | 'model_has_permissions_permission_model_type_primary');
68 | } else {
69 | $blueprint->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
70 | 'model_has_permissions_permission_model_type_primary');
71 | }
72 |
73 | });
74 |
75 | Schema::create($tableNames['model_has_roles'], static function (Blueprint $blueprint) use ($tableNames, $columnNames, $pivotRole, $teams): void {
76 | $blueprint->unsignedBigInteger($pivotRole);
77 |
78 | $blueprint->string('model_type');
79 | $blueprint->unsignedBigInteger($columnNames['model_morph_key']);
80 | $blueprint->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
81 |
82 | $blueprint->foreign($pivotRole)
83 | ->references('id') // role id
84 | ->on($tableNames['roles'])
85 | ->onDelete('cascade');
86 | if ($teams) {
87 | $blueprint->unsignedBigInteger($columnNames['team_foreign_key']);
88 | $blueprint->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
89 |
90 | $blueprint->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
91 | 'model_has_roles_role_model_type_primary');
92 | } else {
93 | $blueprint->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
94 | 'model_has_roles_role_model_type_primary');
95 | }
96 | });
97 |
98 | Schema::create($tableNames['role_has_permissions'], static function (Blueprint $blueprint) use ($tableNames, $pivotRole, $pivotPermission): void {
99 | $blueprint->unsignedBigInteger($pivotPermission);
100 | $blueprint->unsignedBigInteger($pivotRole);
101 |
102 | $blueprint->foreign($pivotPermission)
103 | ->references('id') // permission id
104 | ->on($tableNames['permissions'])
105 | ->onDelete('cascade');
106 |
107 | $blueprint->foreign($pivotRole)
108 | ->references('id') // role id
109 | ->on($tableNames['roles'])
110 | ->onDelete('cascade');
111 |
112 | $blueprint->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
113 | });
114 |
115 | app(\Illuminate\Contracts\Cache\Factory::class)
116 | ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
117 | ->forget(config('permission.cache.key'));
118 | }
119 |
120 | /**
121 | * Reverse the migrations.
122 | */
123 | public function down(): void
124 | {
125 | $tableNames = config('permission.table_names');
126 |
127 | throw_if(empty($tableNames), new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'));
128 |
129 | Schema::drop($tableNames['role_has_permissions']);
130 | Schema::drop($tableNames['model_has_roles']);
131 | Schema::drop($tableNames['model_has_permissions']);
132 | Schema::drop($tableNames['roles']);
133 | Schema::drop($tableNames['permissions']);
134 | }
135 | };
136 |
--------------------------------------------------------------------------------
/config/database.php:
--------------------------------------------------------------------------------
1 | env('DB_CONNECTION', 'sqlite'),
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Database Connections
24 | |--------------------------------------------------------------------------
25 | |
26 | | Below are all of the database connections defined for your application.
27 | | An example configuration is provided for each database system which
28 | | is supported by Laravel. You're free to add / remove connections.
29 | |
30 | */
31 |
32 | 'connections' => [
33 |
34 | 'sqlite' => [
35 | 'driver' => 'sqlite',
36 | 'url' => env('DB_URL'),
37 | 'database' => env('DB_DATABASE', database_path('database.sqlite')),
38 | 'prefix' => '',
39 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
40 | 'busy_timeout' => null,
41 | 'journal_mode' => null,
42 | 'synchronous' => null,
43 | 'transaction_mode' => 'DEFERRED',
44 | ],
45 |
46 | 'mysql' => [
47 | 'driver' => 'mysql',
48 | 'url' => env('DB_URL'),
49 | 'host' => env('DB_HOST', '127.0.0.1'),
50 | 'port' => env('DB_PORT', '3306'),
51 | 'database' => env('DB_DATABASE', 'laravel'),
52 | 'username' => env('DB_USERNAME', 'root'),
53 | 'password' => env('DB_PASSWORD', ''),
54 | 'unix_socket' => env('DB_SOCKET', ''),
55 | 'charset' => env('DB_CHARSET', 'utf8mb4'),
56 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
57 | 'prefix' => '',
58 | 'prefix_indexes' => true,
59 | 'strict' => true,
60 | 'engine' => null,
61 | 'options' => extension_loaded('pdo_mysql') ? array_filter([
62 | (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
63 | ]) : [],
64 | ],
65 |
66 | 'mariadb' => [
67 | 'driver' => 'mariadb',
68 | 'url' => env('DB_URL'),
69 | 'host' => env('DB_HOST', '127.0.0.1'),
70 | 'port' => env('DB_PORT', '3306'),
71 | 'database' => env('DB_DATABASE', 'laravel'),
72 | 'username' => env('DB_USERNAME', 'root'),
73 | 'password' => env('DB_PASSWORD', ''),
74 | 'unix_socket' => env('DB_SOCKET', ''),
75 | 'charset' => env('DB_CHARSET', 'utf8mb4'),
76 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
77 | 'prefix' => '',
78 | 'prefix_indexes' => true,
79 | 'strict' => true,
80 | 'engine' => null,
81 | 'options' => extension_loaded('pdo_mysql') ? array_filter([
82 | (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
83 | ]) : [],
84 | ],
85 |
86 | 'pgsql' => [
87 | 'driver' => 'pgsql',
88 | 'url' => env('DB_URL'),
89 | 'host' => env('DB_HOST', '127.0.0.1'),
90 | 'port' => env('DB_PORT', '5432'),
91 | 'database' => env('DB_DATABASE', 'laravel'),
92 | 'username' => env('DB_USERNAME', 'root'),
93 | 'password' => env('DB_PASSWORD', ''),
94 | 'charset' => env('DB_CHARSET', 'utf8'),
95 | 'prefix' => '',
96 | 'prefix_indexes' => true,
97 | 'search_path' => 'public',
98 | 'sslmode' => 'prefer',
99 | ],
100 |
101 | 'sqlsrv' => [
102 | 'driver' => 'sqlsrv',
103 | 'url' => env('DB_URL'),
104 | 'host' => env('DB_HOST', 'localhost'),
105 | 'port' => env('DB_PORT', '1433'),
106 | 'database' => env('DB_DATABASE', 'laravel'),
107 | 'username' => env('DB_USERNAME', 'root'),
108 | 'password' => env('DB_PASSWORD', ''),
109 | 'charset' => env('DB_CHARSET', 'utf8'),
110 | 'prefix' => '',
111 | 'prefix_indexes' => true,
112 | // 'encrypt' => env('DB_ENCRYPT', 'yes'),
113 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
114 | ],
115 |
116 | ],
117 |
118 | /*
119 | |--------------------------------------------------------------------------
120 | | Migration Repository Table
121 | |--------------------------------------------------------------------------
122 | |
123 | | This table keeps track of all the migrations that have already run for
124 | | your application. Using this information, we can determine which of
125 | | the migrations on disk haven't actually been run on the database.
126 | |
127 | */
128 |
129 | 'migrations' => [
130 | 'table' => 'migrations',
131 | 'update_date_on_publish' => true,
132 | ],
133 |
134 | /*
135 | |--------------------------------------------------------------------------
136 | | Redis Databases
137 | |--------------------------------------------------------------------------
138 | |
139 | | Redis is an open source, fast, and advanced key-value store that also
140 | | provides a richer body of commands than a typical key-value system
141 | | such as Memcached. You may define your connection settings here.
142 | |
143 | */
144 |
145 | 'redis' => [
146 |
147 | 'client' => env('REDIS_CLIENT', 'phpredis'),
148 |
149 | 'options' => [
150 | 'cluster' => env('REDIS_CLUSTER', 'redis'),
151 | 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
152 | 'persistent' => env('REDIS_PERSISTENT', false),
153 | ],
154 |
155 | 'default' => [
156 | 'url' => env('REDIS_URL'),
157 | 'host' => env('REDIS_HOST', '127.0.0.1'),
158 | 'username' => env('REDIS_USERNAME'),
159 | 'password' => env('REDIS_PASSWORD'),
160 | 'port' => env('REDIS_PORT', '6379'),
161 | 'database' => env('REDIS_DB', '0'),
162 | 'max_retries' => env('REDIS_MAX_RETRIES', 3),
163 | 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
164 | 'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
165 | 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
166 | ],
167 |
168 | 'cache' => [
169 | 'url' => env('REDIS_URL'),
170 | 'host' => env('REDIS_HOST', '127.0.0.1'),
171 | 'username' => env('REDIS_USERNAME'),
172 | 'password' => env('REDIS_PASSWORD'),
173 | 'port' => env('REDIS_PORT', '6379'),
174 | 'database' => env('REDIS_CACHE_DB', '1'),
175 | 'max_retries' => env('REDIS_MAX_RETRIES', 3),
176 | 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
177 | 'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
178 | 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
179 | ],
180 |
181 | ],
182 |
183 | ];
184 |
--------------------------------------------------------------------------------
/config/permission.php:
--------------------------------------------------------------------------------
1 | [
6 |
7 | /*
8 | * When using the "HasPermissions" trait from this package, we need to know which
9 | * Eloquent model should be used to retrieve your permissions. Of course, it
10 | * is often just the "Permission" model but you may use whatever you like.
11 | *
12 | * The model you want to use as a Permission model needs to implement the
13 | * `Spatie\Permission\Contracts\Permission` contract.
14 | */
15 |
16 | 'permission' => Spatie\Permission\Models\Permission::class,
17 |
18 | /*
19 | * When using the "HasRoles" trait from this package, we need to know which
20 | * Eloquent model should be used to retrieve your roles. Of course, it
21 | * is often just the "Role" model but you may use whatever you like.
22 | *
23 | * The model you want to use as a Role model needs to implement the
24 | * `Spatie\Permission\Contracts\Role` contract.
25 | */
26 |
27 | 'role' => Spatie\Permission\Models\Role::class,
28 |
29 | ],
30 |
31 | 'table_names' => [
32 |
33 | /*
34 | * When using the "HasRoles" trait from this package, we need to know which
35 | * table should be used to retrieve your roles. We have chosen a basic
36 | * default value but you may easily change it to any table you like.
37 | */
38 |
39 | 'roles' => 'roles',
40 |
41 | /*
42 | * When using the "HasPermissions" trait from this package, we need to know which
43 | * table should be used to retrieve your permissions. We have chosen a basic
44 | * default value but you may easily change it to any table you like.
45 | */
46 |
47 | 'permissions' => 'permissions',
48 |
49 | /*
50 | * When using the "HasPermissions" trait from this package, we need to know which
51 | * table should be used to retrieve your models permissions. We have chosen a
52 | * basic default value but you may easily change it to any table you like.
53 | */
54 |
55 | 'model_has_permissions' => 'model_has_permissions',
56 |
57 | /*
58 | * When using the "HasRoles" trait from this package, we need to know which
59 | * table should be used to retrieve your models roles. We have chosen a
60 | * basic default value but you may easily change it to any table you like.
61 | */
62 |
63 | 'model_has_roles' => 'model_has_roles',
64 |
65 | /*
66 | * When using the "HasRoles" trait from this package, we need to know which
67 | * table should be used to retrieve your roles permissions. We have chosen a
68 | * basic default value but you may easily change it to any table you like.
69 | */
70 |
71 | 'role_has_permissions' => 'role_has_permissions',
72 | ],
73 |
74 | 'column_names' => [
75 | /*
76 | * Change this if you want to name the related pivots other than defaults
77 | */
78 | 'role_pivot_key' => null, // default 'role_id',
79 | 'permission_pivot_key' => null, // default 'permission_id',
80 |
81 | /*
82 | * Change this if you want to name the related model primary key other than
83 | * `model_id`.
84 | *
85 | * For example, this would be nice if your primary keys are all UUIDs. In
86 | * that case, name this `model_uuid`.
87 | */
88 |
89 | 'model_morph_key' => 'model_id',
90 |
91 | /*
92 | * Change this if you want to use the teams feature and your related model's
93 | * foreign key is other than `team_id`.
94 | */
95 |
96 | 'team_foreign_key' => 'team_id',
97 | ],
98 |
99 | /*
100 | * When set to true, the method for checking permissions will be registered on the gate.
101 | * Set this to false if you want to implement custom logic for checking permissions.
102 | */
103 |
104 | 'register_permission_check_method' => true,
105 |
106 | /*
107 | * When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered
108 | * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated
109 | * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it.
110 | */
111 | 'register_octane_reset_listener' => false,
112 |
113 | /*
114 | * Events will fire when a role or permission is assigned/unassigned:
115 | * \Spatie\Permission\Events\RoleAttached
116 | * \Spatie\Permission\Events\RoleDetached
117 | * \Spatie\Permission\Events\PermissionAttached
118 | * \Spatie\Permission\Events\PermissionDetached
119 | *
120 | * To enable, set to true, and then create listeners to watch these events.
121 | */
122 | 'events_enabled' => false,
123 |
124 | /*
125 | * Teams Feature.
126 | * When set to true the package implements teams using the 'team_foreign_key'.
127 | * If you want the migrations to register the 'team_foreign_key', you must
128 | * set this to true before doing the migration.
129 | * If you already did the migration then you must make a new migration to also
130 | * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions'
131 | * (view the latest version of this package's migration file)
132 | */
133 |
134 | 'teams' => false,
135 |
136 | /*
137 | * The class to use to resolve the permissions team id
138 | */
139 | 'team_resolver' => \Spatie\Permission\DefaultTeamResolver::class,
140 |
141 | /*
142 | * Passport Client Credentials Grant
143 | * When set to true the package will use Passports Client to check permissions
144 | */
145 |
146 | 'use_passport_client_credentials' => false,
147 |
148 | /*
149 | * When set to true, the required permission names are added to exception messages.
150 | * This could be considered an information leak in some contexts, so the default
151 | * setting is false here for optimum safety.
152 | */
153 |
154 | 'display_permission_in_exception' => false,
155 |
156 | /*
157 | * When set to true, the required role names are added to exception messages.
158 | * This could be considered an information leak in some contexts, so the default
159 | * setting is false here for optimum safety.
160 | */
161 |
162 | 'display_role_in_exception' => false,
163 |
164 | /*
165 | * By default wildcard permission lookups are disabled.
166 | * See documentation to understand supported syntax.
167 | */
168 |
169 | 'enable_wildcard_permission' => false,
170 |
171 | /*
172 | * The class to use for interpreting wildcard permissions.
173 | * If you need to modify delimiters, override the class and specify its name here.
174 | */
175 | // 'wildcard_permission' => Spatie\Permission\WildcardPermission::class,
176 |
177 | /* Cache-specific settings */
178 |
179 | 'cache' => [
180 |
181 | /*
182 | * By default all permissions are cached for 24 hours to speed up performance.
183 | * When permissions or roles are updated the cache is flushed automatically.
184 | */
185 |
186 | 'expiration_time' => \DateInterval::createFromDateString('24 hours'),
187 |
188 | /*
189 | * The cache key used to store all permissions.
190 | */
191 |
192 | 'key' => 'spatie.permission.cache',
193 |
194 | /*
195 | * You may optionally indicate a specific cache driver to use for permission and
196 | * role caching using any of the `store` drivers listed in the cache.php config
197 | * file. Using 'default' here means to use the `default` set in cache.php.
198 | */
199 |
200 | 'store' => 'default',
201 | ],
202 | ];
203 |
--------------------------------------------------------------------------------
/config/session.php:
--------------------------------------------------------------------------------
1 | env('SESSION_DRIVER', 'database'),
22 |
23 | /*
24 | |--------------------------------------------------------------------------
25 | | Session Lifetime
26 | |--------------------------------------------------------------------------
27 | |
28 | | Here you may specify the number of minutes that you wish the session
29 | | to be allowed to remain idle before it expires. If you want them
30 | | to expire immediately when the browser is closed then you may
31 | | indicate that via the expire_on_close configuration option.
32 | |
33 | */
34 |
35 | 'lifetime' => (int) env('SESSION_LIFETIME', 120),
36 |
37 | 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
38 |
39 | /*
40 | |--------------------------------------------------------------------------
41 | | Session Encryption
42 | |--------------------------------------------------------------------------
43 | |
44 | | This option allows you to easily specify that all of your session data
45 | | should be encrypted before it's stored. All encryption is performed
46 | | automatically by Laravel and you may use the session like normal.
47 | |
48 | */
49 |
50 | 'encrypt' => env('SESSION_ENCRYPT', false),
51 |
52 | /*
53 | |--------------------------------------------------------------------------
54 | | Session File Location
55 | |--------------------------------------------------------------------------
56 | |
57 | | When utilizing the "file" session driver, the session files are placed
58 | | on disk. The default storage location is defined here; however, you
59 | | are free to provide another location where they should be stored.
60 | |
61 | */
62 |
63 | 'files' => storage_path('framework/sessions'),
64 |
65 | /*
66 | |--------------------------------------------------------------------------
67 | | Session Database Connection
68 | |--------------------------------------------------------------------------
69 | |
70 | | When using the "database" or "redis" session drivers, you may specify a
71 | | connection that should be used to manage these sessions. This should
72 | | correspond to a connection in your database configuration options.
73 | |
74 | */
75 |
76 | 'connection' => env('SESSION_CONNECTION'),
77 |
78 | /*
79 | |--------------------------------------------------------------------------
80 | | Session Database Table
81 | |--------------------------------------------------------------------------
82 | |
83 | | When using the "database" session driver, you may specify the table to
84 | | be used to store sessions. Of course, a sensible default is defined
85 | | for you; however, you're welcome to change this to another table.
86 | |
87 | */
88 |
89 | 'table' => env('SESSION_TABLE', 'sessions'),
90 |
91 | /*
92 | |--------------------------------------------------------------------------
93 | | Session Cache Store
94 | |--------------------------------------------------------------------------
95 | |
96 | | When using one of the framework's cache driven session backends, you may
97 | | define the cache store which should be used to store the session data
98 | | between requests. This must match one of your defined cache stores.
99 | |
100 | | Affects: "dynamodb", "memcached", "redis"
101 | |
102 | */
103 |
104 | 'store' => env('SESSION_STORE'),
105 |
106 | /*
107 | |--------------------------------------------------------------------------
108 | | Session Sweeping Lottery
109 | |--------------------------------------------------------------------------
110 | |
111 | | Some session drivers must manually sweep their storage location to get
112 | | rid of old sessions from storage. Here are the chances that it will
113 | | happen on a given request. By default, the odds are 2 out of 100.
114 | |
115 | */
116 |
117 | 'lottery' => [2, 100],
118 |
119 | /*
120 | |--------------------------------------------------------------------------
121 | | Session Cookie Name
122 | |--------------------------------------------------------------------------
123 | |
124 | | Here you may change the name of the session cookie that is created by
125 | | the framework. Typically, you should not need to change this value
126 | | since doing so does not grant a meaningful security improvement.
127 | |
128 | */
129 |
130 | 'cookie' => env(
131 | 'SESSION_COOKIE',
132 | Str::slug((string) env('APP_NAME', 'laravel')).'-session'
133 | ),
134 |
135 | /*
136 | |--------------------------------------------------------------------------
137 | | Session Cookie Path
138 | |--------------------------------------------------------------------------
139 | |
140 | | The session cookie path determines the path for which the cookie will
141 | | be regarded as available. Typically, this will be the root path of
142 | | your application, but you're free to change this when necessary.
143 | |
144 | */
145 |
146 | 'path' => env('SESSION_PATH', '/'),
147 |
148 | /*
149 | |--------------------------------------------------------------------------
150 | | Session Cookie Domain
151 | |--------------------------------------------------------------------------
152 | |
153 | | This value determines the domain and subdomains the session cookie is
154 | | available to. By default, the cookie will be available to the root
155 | | domain without subdomains. Typically, this shouldn't be changed.
156 | |
157 | */
158 |
159 | 'domain' => env('SESSION_DOMAIN'),
160 |
161 | /*
162 | |--------------------------------------------------------------------------
163 | | HTTPS Only Cookies
164 | |--------------------------------------------------------------------------
165 | |
166 | | By setting this option to true, session cookies will only be sent back
167 | | to the server if the browser has a HTTPS connection. This will keep
168 | | the cookie from being sent to you when it can't be done securely.
169 | |
170 | */
171 |
172 | 'secure' => env('SESSION_SECURE_COOKIE'),
173 |
174 | /*
175 | |--------------------------------------------------------------------------
176 | | HTTP Access Only
177 | |--------------------------------------------------------------------------
178 | |
179 | | Setting this value to true will prevent JavaScript from accessing the
180 | | value of the cookie and the cookie will only be accessible through
181 | | the HTTP protocol. It's unlikely you should disable this option.
182 | |
183 | */
184 |
185 | 'http_only' => env('SESSION_HTTP_ONLY', true),
186 |
187 | /*
188 | |--------------------------------------------------------------------------
189 | | Same-Site Cookies
190 | |--------------------------------------------------------------------------
191 | |
192 | | This option determines how your cookies behave when cross-site requests
193 | | take place, and can be used to mitigate CSRF attacks. By default, we
194 | | will set this value to "lax" to permit secure cross-site requests.
195 | |
196 | | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
197 | |
198 | | Supported: "lax", "strict", "none", null
199 | |
200 | */
201 |
202 | 'same_site' => env('SESSION_SAME_SITE', 'lax'),
203 |
204 | /*
205 | |--------------------------------------------------------------------------
206 | | Partitioned Cookies
207 | |--------------------------------------------------------------------------
208 | |
209 | | Setting this value to true will tie the cookie to the top-level site for
210 | | a cross-site context. Partitioned cookies are accepted by the browser
211 | | when flagged "secure" and the Same-Site attribute is set to "none".
212 | |
213 | */
214 |
215 | 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
216 |
217 | ];
218 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/color-picker.js:
--------------------------------------------------------------------------------
1 | var c=(e,t=0,r=1)=>e>r?r:eMath.round(r*e)/r;var at={grad:360/400,turn:360,rad:360/(Math.PI*2)},F=e=>G(v(e)),v=e=>(e[0]==="#"&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:e.length===4?a(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:e.length===8?a(parseInt(e.substring(6,8),16)/255,2):1}),nt=(e,t="deg")=>Number(e)*(at[t]||1),it=e=>{let r=/hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i.exec(e);return r?lt({h:nt(r[1],r[2]),s:Number(r[3]),l:Number(r[4]),a:r[5]===void 0?1:Number(r[5])/(r[6]?100:1)}):{h:0,s:0,v:0,a:1}},J=it,lt=({h:e,s:t,l:r,a:o})=>(t*=(r<50?r:100-r)/100,{h:e,s:t>0?2*t/(r+t)*100:0,v:r+t,a:o}),X=e=>ct(A(e)),Y=({h:e,s:t,v:r,a:o})=>{let s=(200-t)*r/100;return{h:a(e),s:a(s>0&&s<200?t*r/100/(s<=100?s:200-s)*100:0),l:a(s/2),a:a(o,2)}};var d=e=>{let{h:t,s:r,l:o}=Y(e);return`hsl(${t}, ${r}%, ${o}%)`},$=e=>{let{h:t,s:r,l:o,a:s}=Y(e);return`hsla(${t}, ${r}%, ${o}%, ${s})`},A=({h:e,s:t,v:r,a:o})=>{e=e/360*6,t=t/100,r=r/100;let s=Math.floor(e),n=r*(1-t),i=r*(1-(e-s)*t),l=r*(1-(1-e+s)*t),q=s%6;return{r:a([r,i,n,n,l,r][q]*255),g:a([l,r,r,i,n,n][q]*255),b:a([n,n,l,r,r,i][q]*255),a:a(o,2)}},B=e=>{let{r:t,g:r,b:o}=A(e);return`rgb(${t}, ${r}, ${o})`},D=e=>{let{r:t,g:r,b:o,a:s}=A(e);return`rgba(${t}, ${r}, ${o}, ${s})`};var I=e=>{let r=/rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i.exec(e);return r?G({r:Number(r[1])/(r[2]?100/255:1),g:Number(r[3])/(r[4]?100/255:1),b:Number(r[5])/(r[6]?100/255:1),a:r[7]===void 0?1:Number(r[7])/(r[8]?100:1)}):{h:0,s:0,v:0,a:1}},U=I,b=e=>{let t=e.toString(16);return t.length<2?"0"+t:t},ct=({r:e,g:t,b:r,a:o})=>{let s=o<1?b(a(o*255)):"";return"#"+b(e)+b(t)+b(r)+s},G=({r:e,g:t,b:r,a:o})=>{let s=Math.max(e,t,r),n=s-Math.min(e,t,r),i=n?s===e?(t-r)/n:s===t?2+(r-e)/n:4+(e-t)/n:0;return{h:a(60*(i<0?i+6:i)),s:a(s?n/s*100:0),v:a(s/255*100),a:o}};var L=(e,t)=>{if(e===t)return!0;for(let r in e)if(e[r]!==t[r])return!1;return!0},h=(e,t)=>e.replace(/\s/g,"")===t.replace(/\s/g,""),K=(e,t)=>e.toLowerCase()===t.toLowerCase()?!0:L(v(e),v(t));var Q={},H=e=>{let t=Q[e];return t||(t=document.createElement("template"),t.innerHTML=e,Q[e]=t),t},f=(e,t,r)=>{e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))};var m=!1,O=e=>"touches"in e,pt=e=>m&&!O(e)?!1:(m||(m=O(e)),!0),W=(e,t)=>{let r=O(t)?t.touches[0]:t,o=e.el.getBoundingClientRect();f(e.el,"move",e.getMove({x:c((r.pageX-(o.left+window.pageXOffset))/o.width),y:c((r.pageY-(o.top+window.pageYOffset))/o.height)}))},ut=(e,t)=>{let r=t.keyCode;r>40||e.xy&&r<37||r<33||(t.preventDefault(),f(e.el,"move",e.getMove({x:r===39?.01:r===37?-.01:r===34?.05:r===33?-.05:r===35?1:r===36?-1:0,y:r===40?.01:r===38?-.01:0},!0)))},u=class{constructor(t,r,o,s){let n=H(``);t.appendChild(n.content.cloneNode(!0));let i=t.querySelector(`[part=${r}]`);i.addEventListener("mousedown",this),i.addEventListener("touchstart",this),i.addEventListener("keydown",this),this.el=i,this.xy=s,this.nodes=[i.firstChild,i]}set dragging(t){let r=t?document.addEventListener:document.removeEventListener;r(m?"touchmove":"mousemove",this),r(m?"touchend":"mouseup",this)}handleEvent(t){switch(t.type){case"mousedown":case"touchstart":if(t.preventDefault(),!pt(t)||!m&&t.button!=0)return;this.el.focus(),W(this,t),this.dragging=!0;break;case"mousemove":case"touchmove":t.preventDefault(),W(this,t);break;case"mouseup":case"touchend":this.dragging=!1;break;case"keydown":ut(this,t);break}}style(t){t.forEach((r,o)=>{for(let s in r)this.nodes[o].style.setProperty(s,r[s])})}};var S=class extends u{constructor(t){super(t,"hue",'aria-label="Hue" aria-valuemin="0" aria-valuemax="360"',!1)}update({h:t}){this.h=t,this.style([{left:`${t/360*100}%`,color:d({h:t,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuenow",`${a(t)}`)}getMove(t,r){return{h:r?c(this.h+t.x*360,0,360):360*t.x}}};var T=class extends u{constructor(t){super(t,"saturation",'aria-label="Color"',!0)}update(t){this.hsva=t,this.style([{top:`${100-t.v}%`,left:`${t.s}%`,color:d(t)},{"background-color":d({h:t.h,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuetext",`Saturation ${a(t.s)}%, Brightness ${a(t.v)}%`)}getMove(t,r){return{s:r?c(this.hsva.s+t.x*100,0,100):t.x*100,v:r?c(this.hsva.v-t.y*100,0,100):Math.round(100-t.y*100)}}};var Z=':host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:"";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}';var tt="[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}";var rt="[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}";var w=Symbol("same"),R=Symbol("color"),et=Symbol("hsva"),_=Symbol("update"),ot=Symbol("parts"),g=Symbol("css"),x=Symbol("sliders"),p=class extends HTMLElement{static get observedAttributes(){return["color"]}get[g](){return[Z,tt,rt]}get[x](){return[T,S]}get color(){return this[R]}set color(t){if(!this[w](t)){let r=this.colorModel.toHsva(t);this[_](r),this[R]=t}}constructor(){super();let t=H(``),r=this.attachShadow({mode:"open"});r.appendChild(t.content.cloneNode(!0)),r.addEventListener("move",this),this[ot]=this[x].map(o=>new o(r))}connectedCallback(){if(this.hasOwnProperty("color")){let t=this.color;delete this.color,this.color=t}else this.color||(this.color=this.colorModel.defaultColor)}attributeChangedCallback(t,r,o){let s=this.colorModel.fromAttr(o);this[w](s)||(this.color=s)}handleEvent(t){let r=this[et],o={...r,...t.detail};this[_](o);let s;!L(o,r)&&!this[w](s=this.colorModel.fromHsva(o))&&(this[R]=s,f(this,"color-changed",{value:s}))}[w](t){return this.color&&this.colorModel.equal(t,this.color)}[_](t){this[et]=t,this[ot].forEach(r=>r.update(t))}};var dt={defaultColor:"#000",toHsva:F,fromHsva:({h:e,s:t,v:r})=>X({h:e,s:t,v:r,a:1}),equal:K,fromAttr:e=>e},y=class extends p{get colorModel(){return dt}};var P=class extends y{};customElements.define("hex-color-picker",P);var ht={defaultColor:"hsl(0, 0%, 0%)",toHsva:J,fromHsva:d,equal:h,fromAttr:e=>e},M=class extends p{get colorModel(){return ht}};var z=class extends M{};customElements.define("hsl-string-color-picker",z);var mt={defaultColor:"rgb(0, 0, 0)",toHsva:U,fromHsva:B,equal:h,fromAttr:e=>e},C=class extends p{get colorModel(){return mt}};var V=class extends C{};customElements.define("rgb-string-color-picker",V);var k=class extends u{constructor(t){super(t,"alpha",'aria-label="Alpha" aria-valuemin="0" aria-valuemax="1"',!1)}update(t){this.hsva=t;let r=$({...t,a:0}),o=$({...t,a:1}),s=t.a*100;this.style([{left:`${s}%`,color:$(t)},{"--gradient":`linear-gradient(90deg, ${r}, ${o}`}]);let n=a(s);this.el.setAttribute("aria-valuenow",`${n}`),this.el.setAttribute("aria-valuetext",`${n}%`)}getMove(t,r){return{a:r?c(this.hsva.a+t.x):t.x}}};var st=`[part=alpha]{flex:0 0 24px}[part=alpha]::after{display:block;content:"";position:absolute;top:0;left:0;right:0;bottom:0;border-radius:inherit;background-image:var(--gradient);box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part^=alpha]{background-color:#fff;background-image:url('data:image/svg+xml,')}[part=alpha-pointer]{top:50%}`;var E=class extends p{get[g](){return[...super[g],st]}get[x](){return[...super[x],k]}};var ft={defaultColor:"rgba(0, 0, 0, 1)",toHsva:I,fromHsva:D,equal:h,fromAttr:e=>e},N=class extends E{get colorModel(){return ft}};var j=class extends N{};customElements.define("rgba-string-color-picker",j);function gt({isAutofocused:e,isDisabled:t,isLive:r,isLiveDebounced:o,isLiveOnBlur:s,liveDebounce:n,state:i}){return{state:i,init(){this.state===null||this.state===""||this.setState(this.state),e&&this.togglePanelVisibility(this.$refs.input),this.$refs.input.addEventListener("change",l=>{this.setState(l.target.value)}),this.$refs.panel.addEventListener("color-changed",l=>{this.setState(l.detail.value),!(s||!(r||o))&&setTimeout(()=>{this.state===l.detail.value&&this.commitState()},o?n:250)}),(r||o||s)&&new MutationObserver(()=>this.isOpen()?null:this.commitState()).observe(this.$refs.panel,{attributes:!0,childList:!0})},togglePanelVisibility(){t||this.$refs.panel.toggle(this.$refs.input)},setState(l){this.state=l,this.$refs.input.value=l,this.$refs.panel.color=l},isOpen(){return this.$refs.panel.style.display==="block"},commitState(){JSON.stringify(this.$wire.__instance.canonical)!==JSON.stringify(this.$wire.__instance.ephemeral)&&this.$wire.$commit()}}}export{gt as default};
2 |
--------------------------------------------------------------------------------
/public/js/filament/filament/app.js:
--------------------------------------------------------------------------------
1 | (()=>{var te=Object.create,x=Object.defineProperty,re=Object.getPrototypeOf,ne=Object.prototype.hasOwnProperty,ie=Object.getOwnPropertyNames,ae=Object.getOwnPropertyDescriptor,se=t=>x(t,"__esModule",{value:!0}),oe=(t,n)=>()=>(n||(n={exports:{}},t(n.exports,n)),n.exports),le=(t,n,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let l of ie(n))!ne.call(t,l)&&l!=="default"&&x(t,l,{get:()=>n[l],enumerable:!(i=ae(n,l))||i.enumerable});return t},fe=t=>le(se(x(t!=null?te(re(t)):{},"default",t&&t.__esModule&&"default"in t?{get:()=>t.default,enumerable:!0}:{value:t,enumerable:!0})),t),ue=oe((t,n)=>{(function(i,l,g){if(!i)return;for(var c={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},_={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},y={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},G={option:"alt",command:"meta",return:"enter",escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},M,b=1;b<20;++b)c[111+b]="f"+b;for(b=0;b<=9;++b)c[b+96]=b.toString();function P(e,r,s){if(e.addEventListener){e.addEventListener(r,s,!1);return}e.attachEvent("on"+r,s)}function T(e){if(e.type=="keypress"){var r=String.fromCharCode(e.which);return e.shiftKey||(r=r.toLowerCase()),r}return c[e.which]?c[e.which]:_[e.which]?_[e.which]:String.fromCharCode(e.which).toLowerCase()}function V(e,r){return e.sort().join(",")===r.sort().join(",")}function J(e){var r=[];return e.shiftKey&&r.push("shift"),e.altKey&&r.push("alt"),e.ctrlKey&&r.push("ctrl"),e.metaKey&&r.push("meta"),r}function H(e){if(e.preventDefault){e.preventDefault();return}e.returnValue=!1}function F(e){if(e.stopPropagation){e.stopPropagation();return}e.cancelBubble=!0}function C(e){return e=="shift"||e=="ctrl"||e=="alt"||e=="meta"}function B(){if(!M){M={};for(var e in c)e>95&&e<112||c.hasOwnProperty(e)&&(M[c[e]]=e)}return M}function X(e,r,s){return s||(s=B()[e]?"keydown":"keypress"),s=="keypress"&&r.length&&(s="keydown"),s}function Y(e){return e==="+"?["+"]:(e=e.replace(/\+{2}/g,"+plus"),e.split("+"))}function R(e,r){var s,h,k,S=[];for(s=Y(e),k=0;k1){Z(a,m,f,p);return}u=R(a,p),r._callbacks[u.key]=r._callbacks[u.key]||[],z(u.key,u.modifiers,{type:u.action},o,a,d),r._callbacks[u.key][o?"unshift":"push"]({callback:f,modifiers:u.modifiers,action:u.action,seq:o,level:d,combo:a})}r._bindMultiple=function(a,f,p){for(var o=0;o-1||I(r,s.target))return!1;if("composedPath"in e&&typeof e.composedPath=="function"){var h=e.composedPath()[0];h!==e.target&&(r=h)}return r.tagName=="INPUT"||r.tagName=="SELECT"||r.tagName=="TEXTAREA"||r.isContentEditable},v.prototype.handleKey=function(){var e=this;return e._handleKey.apply(e,arguments)},v.addKeycodes=function(e){for(var r in e)e.hasOwnProperty(r)&&(c[r]=e[r]);M=null},v.init=function(){var e=v(l);for(var r in e)r.charAt(0)!=="_"&&(v[r]=(function(s){return function(){return e[s].apply(e,arguments)}})(r))},v.init(),i.Mousetrap=v,typeof n<"u"&&n.exports&&(n.exports=v),typeof define=="function"&&define.amd&&define(function(){return v})})(typeof window<"u"?window:null,typeof window<"u"?document:null)}),q=fe(ue());(function(t){if(t){var n={},i=t.prototype.stopCallback;t.prototype.stopCallback=function(l,g,c,_){var y=this;return y.paused?!0:n[c]||n[_]?!1:i.call(y,l,g,c)},t.prototype.bindGlobal=function(l,g,c){var _=this;if(_.bind(l,g,c),l instanceof Array){for(var y=0;y{t.directive("mousetrap",(n,{modifiers:i,expression:l},{evaluate:g})=>{let c=()=>l?g(l):n.click();i=i.map(_=>_.replace(/--/g," ").replace(/-/g,"+").replace(/\bslash\b/g,"/")),i.includes("global")&&(i=i.filter(_=>_!=="global"),q.default.bindGlobal(i,_=>{_.preventDefault(),c()})),q.default.bind(i,_=>{_.preventDefault(),c()}),document.addEventListener("livewire:navigating",()=>{q.default.unbind(i)},{once:!0})})},W=pe;var j=()=>({isOpen:window.Alpine.$persist(!0).as("isOpen"),isOpenDesktop:window.Alpine.$persist(!0).as("isOpenDesktop"),collapsedGroups:window.Alpine.$persist(null).as("collapsedGroups"),init(){this.resizeObserver=null,this.setUpResizeObserver(),document.addEventListener("livewire:navigated",()=>{this.setUpResizeObserver()})},setUpResizeObserver(){this.resizeObserver&&this.resizeObserver.disconnect();let t=window.innerWidth;this.resizeObserver=new ResizeObserver(()=>{let n=window.innerWidth,i=t>=1024,l=n<1024,g=n>=1024;i&&l?(this.isOpenDesktop=this.isOpen,this.isOpen&&this.close()):!i&&g&&(this.isOpen=this.isOpenDesktop),t=n}),this.resizeObserver.observe(document.body),window.innerWidth<1024?this.isOpen&&(this.isOpenDesktop=!0,this.close()):this.isOpenDesktop=this.isOpen},groupIsCollapsed(t){return this.collapsedGroups.includes(t)},collapseGroup(t){this.collapsedGroups.includes(t)||(this.collapsedGroups=this.collapsedGroups.concat(t))},toggleCollapsedGroup(t){this.collapsedGroups=this.collapsedGroups.includes(t)?this.collapsedGroups.filter(n=>n!==t):this.collapsedGroups.concat(t)},close(){this.isOpen=!1,window.innerWidth>=1024&&(this.isOpenDesktop=!1)},open(){this.isOpen=!0,window.innerWidth>=1024&&(this.isOpenDesktop=!0)}});document.addEventListener("alpine:init",()=>{let t=localStorage.getItem("theme")??getComputedStyle(document.documentElement).getPropertyValue("--default-theme-mode");window.Alpine.store("theme",t==="dark"||t==="system"&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"),window.addEventListener("theme-changed",n=>{let i=n.detail;localStorage.setItem("theme",i),i==="system"&&(i=window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"),window.Alpine.store("theme",i)}),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",n=>{localStorage.getItem("theme")==="system"&&window.Alpine.store("theme",n.matches?"dark":"light")}),window.Alpine.effect(()=>{window.Alpine.store("theme")==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")})});var U=window.history.replaceState,ce=window.history.pushState;window.history.replaceState=function(t,n,i){t?.url instanceof URL&&(t.url=t.url.toString());let l=i||t?.url||window.location.href,g=window.location.href;if(l!==g){U.call(window.history,t,n,i);return}try{let c=window.history.state;JSON.stringify(t)!==JSON.stringify(c)&&U.call(window.history,t,n,i)}catch{U.call(window.history,t,n,i)}};window.history.pushState=function(t,n,i){t?.url instanceof URL&&(t.url=t.url.toString()),ce.call(window.history,t,n,i)};document.addEventListener("DOMContentLoaded",()=>{setTimeout(()=>{let t=document.querySelector(".fi-main-sidebar .fi-sidebar-item.fi-active");if((!t||t.offsetParent===null)&&(t=document.querySelector(".fi-main-sidebar .fi-sidebar-group.fi-active")),!t||t.offsetParent===null)return;let n=document.querySelector(".fi-main-sidebar .fi-sidebar-nav");n&&n.scrollTo(0,t.offsetTop-window.innerHeight/2)},10)});window.setUpUnsavedDataChangesAlert=({body:t,livewireComponent:n,$wire:i})=>{window.addEventListener("beforeunload",l=>{window.jsMd5(JSON.stringify(i.data).replace(/\\/g,""))===i.savedDataHash||i?.__instance?.effects?.redirect||(l.preventDefault(),l.returnValue=!0)})};window.setUpSpaModeUnsavedDataChangesAlert=({body:t,resolveLivewireComponentUsing:n,$wire:i})=>{let l=()=>i?.__instance?.effects?.redirect?!1:window.jsMd5(JSON.stringify(i.data).replace(/\\/g,""))!==i.savedDataHash,g=()=>confirm(t);document.addEventListener("livewire:navigate",c=>{if(typeof n()<"u"){if(!l()||g())return;c.preventDefault()}}),window.addEventListener("beforeunload",c=>{l()&&(c.preventDefault(),c.returnValue=!0)})};window.setUpUnsavedActionChangesAlert=({resolveLivewireComponentUsing:t,$wire:n})=>{window.addEventListener("beforeunload",i=>{if(!(typeof t()>"u")&&(n.mountedActions?.length??0)&&!n?.__instance?.effects?.redirect){i.preventDefault(),i.returnValue=!0;return}})};document.addEventListener("alpine:init",()=>{window.Alpine.plugin(W),window.Alpine.store("sidebar",j())});})();
2 |
--------------------------------------------------------------------------------
/config/ide-helper.php:
--------------------------------------------------------------------------------
1 | '_ide_helper.php',
15 |
16 | /*
17 | |--------------------------------------------------------------------------
18 | | Models filename
19 | |--------------------------------------------------------------------------
20 | |
21 | | The default filename for the models helper file
22 | |
23 | */
24 |
25 | 'models_filename' => '_ide_helper_models.php',
26 |
27 | /*
28 | |--------------------------------------------------------------------------
29 | | Where to write the PhpStorm specific meta file
30 | |--------------------------------------------------------------------------
31 | |
32 | | PhpStorm also supports the directory `.phpstorm.meta.php/` with arbitrary
33 | | files in it, should you need additional files for your project; e.g.
34 | | `.phpstorm.meta.php/laravel_ide_Helper.php'.
35 | |
36 | */
37 | 'meta_filename' => '.phpstorm.meta.php',
38 |
39 | /*
40 | |--------------------------------------------------------------------------
41 | | Fluent helpers
42 | |--------------------------------------------------------------------------
43 | |
44 | | Set to true to generate commonly used Fluent methods
45 | |
46 | */
47 |
48 | 'include_fluent' => false,
49 |
50 | /*
51 | |--------------------------------------------------------------------------
52 | | Factory Builders
53 | |--------------------------------------------------------------------------
54 | |
55 | | Set to true to generate factory generators for better factory()
56 | | method auto-completion.
57 | |
58 | | Deprecated for Laravel 8 or latest.
59 | |
60 | */
61 |
62 | 'include_factory_builders' => false,
63 |
64 | /*
65 | |--------------------------------------------------------------------------
66 | | Write Model Magic methods
67 | |--------------------------------------------------------------------------
68 | |
69 | | Set to false to disable write magic methods of model
70 | |
71 | */
72 |
73 | 'write_model_magic_where' => false,
74 |
75 | /*
76 | |--------------------------------------------------------------------------
77 | | Write Model External Eloquent Builder methods
78 | |--------------------------------------------------------------------------
79 | |
80 | | Set to false to disable write external eloquent builder methods
81 | |
82 | */
83 |
84 | 'write_model_external_builder_methods' => true,
85 |
86 | /*
87 | |--------------------------------------------------------------------------
88 | | Write Model relation count properties
89 | |--------------------------------------------------------------------------
90 | |
91 | | Set to false to disable writing of relation count properties to model DocBlocks.
92 | |
93 | */
94 |
95 | 'write_model_relation_count_properties' => true,
96 |
97 | /*
98 | |--------------------------------------------------------------------------
99 | | Write Eloquent Model Mixins
100 | |--------------------------------------------------------------------------
101 | |
102 | | This will add the necessary DocBlock mixins to the model class
103 | | contained in the Laravel Framework. This helps the IDE with
104 | | auto-completion.
105 | |
106 | | Please be aware that this setting changes a file within the /vendor directory.
107 | |
108 | */
109 |
110 | 'write_eloquent_model_mixins' => false,
111 |
112 | /*
113 | |--------------------------------------------------------------------------
114 | | Helper files to include
115 | |--------------------------------------------------------------------------
116 | |
117 | | Include helper files. By default not included, but can be toggled with the
118 | | -- helpers (-H) option. Extra helper files can be included.
119 | |
120 | */
121 |
122 | 'include_helpers' => false,
123 |
124 | 'helper_files' => [
125 | base_path().'/vendor/laravel/framework/src/Illuminate/Support/helpers.php',
126 | ],
127 |
128 | /*
129 | |--------------------------------------------------------------------------
130 | | Model locations to include
131 | |--------------------------------------------------------------------------
132 | |
133 | | Define in which directories the ide-helper:models command should look
134 | | for models.
135 | |
136 | | glob patterns are supported to easier reach models in sub-directories,
137 | | e.g. `app/Services/* /Models` (without the space)
138 | |
139 | */
140 |
141 | 'model_locations' => [
142 | 'app',
143 | ],
144 |
145 | /*
146 | |--------------------------------------------------------------------------
147 | | Models to ignore
148 | |--------------------------------------------------------------------------
149 | |
150 | | Define which models should be ignored.
151 | |
152 | */
153 |
154 | 'ignored_models' => [
155 |
156 | ],
157 |
158 | /*
159 | |--------------------------------------------------------------------------
160 | | Models hooks
161 | |--------------------------------------------------------------------------
162 | |
163 | | Define which hook classes you want to run for models to add custom information
164 | |
165 | | Hooks should implement Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface.
166 | |
167 | */
168 |
169 | 'model_hooks' => [
170 | // App\Support\IdeHelper\MyModelHook::class
171 | ],
172 |
173 | /*
174 | |--------------------------------------------------------------------------
175 | | Extra classes
176 | |--------------------------------------------------------------------------
177 | |
178 | | These implementations are not really extended, but called with magic functions
179 | |
180 | */
181 |
182 | 'extra' => [
183 | 'Eloquent' => [\Illuminate\Database\Eloquent\Builder::class, \Illuminate\Database\Query\Builder::class],
184 | 'Session' => [\Illuminate\Session\Store::class],
185 | ],
186 |
187 | 'magic' => [],
188 |
189 | /*
190 | |--------------------------------------------------------------------------
191 | | Interface implementations
192 | |--------------------------------------------------------------------------
193 | |
194 | | These interfaces will be replaced with the implementing class. Some interfaces
195 | | are detected by the helpers, others can be listed below.
196 | |
197 | */
198 |
199 | 'interfaces' => [
200 |
201 | ],
202 |
203 | /*
204 | |--------------------------------------------------------------------------
205 | | Support for camel cased models
206 | |--------------------------------------------------------------------------
207 | |
208 | | There are some Laravel packages (such as Eloquence) that allow for accessing
209 | | Eloquent model properties via camel case, instead of snake case.
210 | |
211 | | Enabling this option will support these packages by saving all model
212 | | properties as camel case, instead of snake case.
213 | |
214 | | For example, normally you would see this:
215 | |
216 | | * @property \Illuminate\Support\Carbon $created_at
217 | | * @property \Illuminate\Support\Carbon $updated_at
218 | |
219 | | With this enabled, the properties will be this:
220 | |
221 | | * @property \Illuminate\Support\Carbon $createdAt
222 | | * @property \Illuminate\Support\Carbon $updatedAt
223 | |
224 | | Note, it is currently an all-or-nothing option.
225 | |
226 | */
227 | 'model_camel_case_properties' => false,
228 |
229 | /*
230 | |--------------------------------------------------------------------------
231 | | Property Casts
232 | |--------------------------------------------------------------------------
233 | |
234 | | Cast the given "real type" to the given "type".
235 | |
236 | */
237 | 'type_overrides' => [
238 | 'integer' => 'int',
239 | 'boolean' => 'bool',
240 | ],
241 |
242 | /*
243 | |--------------------------------------------------------------------------
244 | | Include DocBlocks from classes
245 | |--------------------------------------------------------------------------
246 | |
247 | | Include DocBlocks from classes to allow additional code inspection for
248 | | magic methods and properties.
249 | |
250 | */
251 | 'include_class_docblocks' => false,
252 |
253 | /*
254 | |--------------------------------------------------------------------------
255 | | Force FQN usage
256 | |--------------------------------------------------------------------------
257 | |
258 | | Use the fully qualified (class) name in docBlock,
259 | | event if class exists in a given file
260 | | or there is an import (use className) of a given class
261 | |
262 | */
263 | 'force_fqn' => false,
264 |
265 | /*
266 | |--------------------------------------------------------------------------
267 | | Use generics syntax
268 | |--------------------------------------------------------------------------
269 | |
270 | | Use generics syntax within DocBlocks,
271 | | e.g. `Collection` instead of `Collection|User[]`.
272 | |
273 | */
274 | 'use_generics_annotations' => true,
275 |
276 | /*
277 | |--------------------------------------------------------------------------
278 | | Additional relation types
279 | |--------------------------------------------------------------------------
280 | |
281 | | Sometimes it's needed to create custom relation types. The key of the array
282 | | is the Relationship Method name. The value of the array is the canonical class
283 | | name of the Relationship, e.g. `'relationName' => RelationShipClass::class`.
284 | |
285 | */
286 | 'additional_relation_types' => [],
287 |
288 | /*
289 | |--------------------------------------------------------------------------
290 | | Additional relation return types
291 | |--------------------------------------------------------------------------
292 | |
293 | | When using custom relation types its possible for the class name to not contain
294 | | the proper return type of the relation. The key of the array is the relationship
295 | | method name. The value of the array is the return type of the relation ('many'
296 | | or 'morphTo').
297 | | e.g. `'relationName' => 'many'`.
298 | |
299 | */
300 | 'additional_relation_return_types' => [],
301 |
302 | /*
303 | |--------------------------------------------------------------------------
304 | | Run artisan commands after migrations to generate model helpers
305 | |--------------------------------------------------------------------------
306 | |
307 | | The specified commands should run after migrations are finished running.
308 | |
309 | */
310 | 'post_migrate' => [
311 | // 'ide-helper:models --nowrite',
312 | ],
313 |
314 | ];
315 |
--------------------------------------------------------------------------------
/public/js/filament/tables/tables.js:
--------------------------------------------------------------------------------
1 | (()=>{var M=Math.min,L=Math.max,B=Math.round,W=Math.floor,S=e=>({x:e,y:e});function J(e,t,i){return L(e,M(t,i))}function j(e,t){return typeof e=="function"?e(t):e}function H(e){return e.split("-")[0]}function Q(e){return e.split("-")[1]}function Z(e){return e==="x"?"y":"x"}function oe(e){return e==="y"?"height":"width"}var Pe=new Set(["top","bottom"]);function z(e){return Pe.has(H(e))?"y":"x"}function se(e){return Z(z(e))}function De(e){return{top:0,right:0,bottom:0,left:0,...e}}function re(e){return typeof e!="number"?De(e):{top:e,right:e,bottom:e,left:e}}function E(e){let{x:t,y:i,width:n,height:s}=e;return{width:n,height:s,top:i,left:t,right:t+n,bottom:i+s,x:t,y:i}}function le(e,t,i){let{reference:n,floating:s}=e,l=z(t),o=se(t),r=oe(o),c=H(t),a=l==="y",f=n.x+n.width/2-s.width/2,d=n.y+n.height/2-s.height/2,h=n[r]/2-s[r]/2,u;switch(c){case"top":u={x:f,y:n.y-s.height};break;case"bottom":u={x:f,y:n.y+n.height};break;case"right":u={x:n.x+n.width,y:d};break;case"left":u={x:n.x-s.width,y:d};break;default:u={x:n.x,y:n.y}}switch(Q(t)){case"start":u[o]-=h*(i&&a?-1:1);break;case"end":u[o]+=h*(i&&a?-1:1);break}return u}var ce=async(e,t,i)=>{let{placement:n="bottom",strategy:s="absolute",middleware:l=[],platform:o}=i,r=l.filter(Boolean),c=await(o.isRTL==null?void 0:o.isRTL(t)),a=await o.getElementRects({reference:e,floating:t,strategy:s}),{x:f,y:d}=le(a,n,c),h=n,u={},m=0;for(let p=0;p{let{x:g,y:x}=w;return{x:g,y:x}}},...c}=j(e,t),a={x:i,y:n},f=await ae(t,c),d=z(H(s)),h=Z(d),u=a[h],m=a[d];if(l){let w=h==="y"?"top":"left",g=h==="y"?"bottom":"right",x=u+f[w],y=u-f[g];u=J(x,u,y)}if(o){let w=d==="y"?"top":"left",g=d==="y"?"bottom":"right",x=m+f[w],y=m-f[g];m=J(x,m,y)}let p=r.fn({...t,[h]:u,[d]:m});return{...p,data:{x:p.x-i,y:p.y-n,enabled:{[h]:l,[d]:o}}}}}};function X(){return typeof window<"u"}function P(e){return he(e)?(e.nodeName||"").toLowerCase():"#document"}function v(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function O(e){var t;return(t=(he(e)?e.ownerDocument:e.document)||window.document)==null?void 0:t.documentElement}function he(e){return X()?e instanceof Node||e instanceof v(e).Node:!1}function R(e){return X()?e instanceof Element||e instanceof v(e).Element:!1}function T(e){return X()?e instanceof HTMLElement||e instanceof v(e).HTMLElement:!1}function ue(e){return!X()||typeof ShadowRoot>"u"?!1:e instanceof ShadowRoot||e instanceof v(e).ShadowRoot}var $e=new Set(["inline","contents"]);function V(e){let{overflow:t,overflowX:i,overflowY:n,display:s}=C(e);return/auto|scroll|overlay|hidden|clip/.test(t+n+i)&&!$e.has(s)}var Ve=new Set(["table","td","th"]);function me(e){return Ve.has(P(e))}var Ne=[":popover-open",":modal"];function _(e){return Ne.some(t=>{try{return e.matches(t)}catch{return!1}})}var Be=["transform","translate","scale","rotate","perspective"],We=["transform","translate","scale","rotate","perspective","filter"],He=["paint","layout","strict","content"];function Y(e){let t=G(),i=R(e)?C(e):e;return Be.some(n=>i[n]?i[n]!=="none":!1)||(i.containerType?i.containerType!=="normal":!1)||!t&&(i.backdropFilter?i.backdropFilter!=="none":!1)||!t&&(i.filter?i.filter!=="none":!1)||We.some(n=>(i.willChange||"").includes(n))||He.some(n=>(i.contain||"").includes(n))}function ge(e){let t=k(e);for(;T(t)&&!D(t);){if(Y(t))return t;if(_(t))return null;t=k(t)}return null}function G(){return typeof CSS>"u"||!CSS.supports?!1:CSS.supports("-webkit-backdrop-filter","none")}var ze=new Set(["html","body","#document"]);function D(e){return ze.has(P(e))}function C(e){return v(e).getComputedStyle(e)}function I(e){return R(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function k(e){if(P(e)==="html")return e;let t=e.assignedSlot||e.parentNode||ue(e)&&e.host||O(e);return ue(t)?t.host:t}function pe(e){let t=k(e);return D(t)?e.ownerDocument?e.ownerDocument.body:e.body:T(t)&&V(t)?t:pe(t)}function $(e,t,i){var n;t===void 0&&(t=[]),i===void 0&&(i=!0);let s=pe(e),l=s===((n=e.ownerDocument)==null?void 0:n.body),o=v(s);if(l){let r=K(o);return t.concat(o,o.visualViewport||[],V(s)?s:[],r&&i?$(r):[])}return t.concat(s,$(s,[],i))}function K(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function be(e){let t=C(e),i=parseFloat(t.width)||0,n=parseFloat(t.height)||0,s=T(e),l=s?e.offsetWidth:i,o=s?e.offsetHeight:n,r=B(i)!==l||B(n)!==o;return r&&(i=l,n=o),{width:i,height:n,$:r}}function te(e){return R(e)?e:e.contextElement}function N(e){let t=te(e);if(!T(t))return S(1);let i=t.getBoundingClientRect(),{width:n,height:s,$:l}=be(t),o=(l?B(i.width):i.width)/n,r=(l?B(i.height):i.height)/s;return(!o||!Number.isFinite(o))&&(o=1),(!r||!Number.isFinite(r))&&(r=1),{x:o,y:r}}var _e=S(0);function ve(e){let t=v(e);return!G()||!t.visualViewport?_e:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function Ie(e,t,i){return t===void 0&&(t=!1),!i||t&&i!==v(e)?!1:t}function F(e,t,i,n){t===void 0&&(t=!1),i===void 0&&(i=!1);let s=e.getBoundingClientRect(),l=te(e),o=S(1);t&&(n?R(n)&&(o=N(n)):o=N(e));let r=Ie(l,i,n)?ve(l):S(0),c=(s.left+r.x)/o.x,a=(s.top+r.y)/o.y,f=s.width/o.x,d=s.height/o.y;if(l){let h=v(l),u=n&&R(n)?v(n):n,m=h,p=K(m);for(;p&&n&&u!==m;){let w=N(p),g=p.getBoundingClientRect(),x=C(p),y=g.left+(p.clientLeft+parseFloat(x.paddingLeft))*w.x,A=g.top+(p.clientTop+parseFloat(x.paddingTop))*w.y;c*=w.x,a*=w.y,f*=w.x,d*=w.y,c+=y,a+=A,m=v(p),p=K(m)}}return E({width:f,height:d,x:c,y:a})}function q(e,t){let i=I(e).scrollLeft;return t?t.left+i:F(O(e)).left+i}function Re(e,t){let i=e.getBoundingClientRect(),n=i.left+t.scrollLeft-q(e,i),s=i.top+t.scrollTop;return{x:n,y:s}}function Ue(e){let{elements:t,rect:i,offsetParent:n,strategy:s}=e,l=s==="fixed",o=O(n),r=t?_(t.floating):!1;if(n===o||r&&l)return i;let c={scrollLeft:0,scrollTop:0},a=S(1),f=S(0),d=T(n);if((d||!d&&!l)&&((P(n)!=="body"||V(o))&&(c=I(n)),T(n))){let u=F(n);a=N(n),f.x=u.x+n.clientLeft,f.y=u.y+n.clientTop}let h=o&&!d&&!l?Re(o,c):S(0);return{width:i.width*a.x,height:i.height*a.y,x:i.x*a.x-c.scrollLeft*a.x+f.x+h.x,y:i.y*a.y-c.scrollTop*a.y+f.y+h.y}}function je(e){return Array.from(e.getClientRects())}function Xe(e){let t=O(e),i=I(e),n=e.ownerDocument.body,s=L(t.scrollWidth,t.clientWidth,n.scrollWidth,n.clientWidth),l=L(t.scrollHeight,t.clientHeight,n.scrollHeight,n.clientHeight),o=-i.scrollLeft+q(e),r=-i.scrollTop;return C(n).direction==="rtl"&&(o+=L(t.clientWidth,n.clientWidth)-s),{width:s,height:l,x:o,y:r}}var we=25;function Ye(e,t){let i=v(e),n=O(e),s=i.visualViewport,l=n.clientWidth,o=n.clientHeight,r=0,c=0;if(s){l=s.width,o=s.height;let f=G();(!f||f&&t==="fixed")&&(r=s.offsetLeft,c=s.offsetTop)}let a=q(n);if(a<=0){let f=n.ownerDocument,d=f.body,h=getComputedStyle(d),u=f.compatMode==="CSS1Compat"&&parseFloat(h.marginLeft)+parseFloat(h.marginRight)||0,m=Math.abs(n.clientWidth-d.clientWidth-u);m<=we&&(l-=m)}else a<=we&&(l+=a);return{width:l,height:o,x:r,y:c}}var Ge=new Set(["absolute","fixed"]);function Ke(e,t){let i=F(e,!0,t==="fixed"),n=i.top+e.clientTop,s=i.left+e.clientLeft,l=T(e)?N(e):S(1),o=e.clientWidth*l.x,r=e.clientHeight*l.y,c=s*l.x,a=n*l.y;return{width:o,height:r,x:c,y:a}}function xe(e,t,i){let n;if(t==="viewport")n=Ye(e,i);else if(t==="document")n=Xe(O(e));else if(R(t))n=Ke(t,i);else{let s=ve(e);n={x:t.x-s.x,y:t.y-s.y,width:t.width,height:t.height}}return E(n)}function Ce(e,t){let i=k(e);return i===t||!R(i)||D(i)?!1:C(i).position==="fixed"||Ce(i,t)}function qe(e,t){let i=t.get(e);if(i)return i;let n=$(e,[],!1).filter(r=>R(r)&&P(r)!=="body"),s=null,l=C(e).position==="fixed",o=l?k(e):e;for(;R(o)&&!D(o);){let r=C(o),c=Y(o);!c&&r.position==="fixed"&&(s=null),(l?!c&&!s:!c&&r.position==="static"&&!!s&&Ge.has(s.position)||V(o)&&!c&&Ce(e,o))?n=n.filter(f=>f!==o):s=r,o=k(o)}return t.set(e,n),n}function Je(e){let{element:t,boundary:i,rootBoundary:n,strategy:s}=e,o=[...i==="clippingAncestors"?_(t)?[]:qe(t,this._c):[].concat(i),n],r=o[0],c=o.reduce((a,f)=>{let d=xe(t,f,s);return a.top=L(d.top,a.top),a.right=M(d.right,a.right),a.bottom=M(d.bottom,a.bottom),a.left=L(d.left,a.left),a},xe(t,r,s));return{width:c.right-c.left,height:c.bottom-c.top,x:c.left,y:c.top}}function Qe(e){let{width:t,height:i}=be(e);return{width:t,height:i}}function Ze(e,t,i){let n=T(t),s=O(t),l=i==="fixed",o=F(e,!0,l,t),r={scrollLeft:0,scrollTop:0},c=S(0);function a(){c.x=q(s)}if(n||!n&&!l)if((P(t)!=="body"||V(s))&&(r=I(t)),n){let u=F(t,!0,l,t);c.x=u.x+t.clientLeft,c.y=u.y+t.clientTop}else s&&a();l&&!n&&s&&a();let f=s&&!n&&!l?Re(s,r):S(0),d=o.left+r.scrollLeft-c.x-f.x,h=o.top+r.scrollTop-c.y-f.y;return{x:d,y:h,width:o.width,height:o.height}}function ee(e){return C(e).position==="static"}function ye(e,t){if(!T(e)||C(e).position==="fixed")return null;if(t)return t(e);let i=e.offsetParent;return O(e)===i&&(i=i.ownerDocument.body),i}function Ae(e,t){let i=v(e);if(_(e))return i;if(!T(e)){let s=k(e);for(;s&&!D(s);){if(R(s)&&!ee(s))return s;s=k(s)}return i}let n=ye(e,t);for(;n&&me(n)&&ee(n);)n=ye(n,t);return n&&D(n)&&ee(n)&&!Y(n)?i:n||ge(e)||i}var et=async function(e){let t=this.getOffsetParent||Ae,i=this.getDimensions,n=await i(e.floating);return{reference:Ze(e.reference,await t(e.floating),e.strategy),floating:{x:0,y:0,width:n.width,height:n.height}}};function tt(e){return C(e).direction==="rtl"}var nt={convertOffsetParentRelativeRectToViewportRelativeRect:Ue,getDocumentElement:O,getClippingRect:Je,getOffsetParent:Ae,getElementRects:et,getClientRects:je,getDimensions:Qe,getScale:N,isElement:R,isRTL:tt};function Se(e,t){return e.x===t.x&&e.y===t.y&&e.width===t.width&&e.height===t.height}function it(e,t){let i=null,n,s=O(e);function l(){var r;clearTimeout(n),(r=i)==null||r.disconnect(),i=null}function o(r,c){r===void 0&&(r=!1),c===void 0&&(c=1),l();let a=e.getBoundingClientRect(),{left:f,top:d,width:h,height:u}=a;if(r||t(),!h||!u)return;let m=W(d),p=W(s.clientWidth-(f+h)),w=W(s.clientHeight-(d+u)),g=W(f),y={rootMargin:-m+"px "+-p+"px "+-w+"px "+-g+"px",threshold:L(0,M(1,c))||1},A=!0;function b(ie){let U=ie[0].intersectionRatio;if(U!==c){if(!A)return o();U?o(!1,U):n=setTimeout(()=>{o(!1,1e-7)},1e3)}U===1&&!Se(a,e.getBoundingClientRect())&&o(),A=!1}try{i=new IntersectionObserver(b,{...y,root:s.ownerDocument})}catch{i=new IntersectionObserver(b,y)}i.observe(e)}return o(!0),l}function Oe(e,t,i,n){n===void 0&&(n={});let{ancestorScroll:s=!0,ancestorResize:l=!0,elementResize:o=typeof ResizeObserver=="function",layoutShift:r=typeof IntersectionObserver=="function",animationFrame:c=!1}=n,a=te(e),f=s||l?[...a?$(a):[],...$(t)]:[];f.forEach(g=>{s&&g.addEventListener("scroll",i,{passive:!0}),l&&g.addEventListener("resize",i)});let d=a&&r?it(a,i):null,h=-1,u=null;o&&(u=new ResizeObserver(g=>{let[x]=g;x&&x.target===a&&u&&(u.unobserve(t),cancelAnimationFrame(h),h=requestAnimationFrame(()=>{var y;(y=u)==null||y.observe(t)})),i()}),a&&!c&&u.observe(a),u.observe(t));let m,p=c?F(e):null;c&&w();function w(){let g=F(e);p&&!Se(p,g)&&i(),p=g,m=requestAnimationFrame(w)}return i(),()=>{var g;f.forEach(x=>{s&&x.removeEventListener("scroll",i),l&&x.removeEventListener("resize",i)}),d?.(),(g=u)==null||g.disconnect(),u=null,c&&cancelAnimationFrame(m)}}var Te=fe;var Le=de;var ke=(e,t,i)=>{let n=new Map,s={platform:nt,...i},l={...s.platform,_c:n};return ce(e,t,{...s,platform:l})};var Ee=({areGroupsCollapsedByDefault:e,canTrackDeselectedRecords:t,currentSelectionLivewireProperty:i,maxSelectableRecords:n,selectsCurrentPageOnly:s,$wire:l})=>({areFiltersOpen:!1,checkboxClickController:null,groupVisibility:[],isLoading:!1,selectedRecords:new Set,deselectedRecords:new Set,isTrackingDeselectedRecords:!1,shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,entangledSelectedRecords:i?l.$entangle(i):null,cleanUpFiltersDropdown:null,init(){this.livewireId=this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value,l.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),l.$on("scrollToTopOfTable",()=>this.$root.scrollIntoView({block:"start",inline:"nearest"})),i&&(n!==1?this.selectedRecords=new Set(this.entangledSelectedRecords):this.selectedRecords=new Set(this.entangledSelectedRecords?[this.entangledSelectedRecords]:[])),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:o})=>{o.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction(...o){l.set("isTrackingDeselectedTableRecords",this.isTrackingDeselectedRecords,!1),l.set("selectedTableRecords",[...this.selectedRecords],!1),l.set("deselectedTableRecords",[...this.deselectedRecords],!1),l.mountAction(...o)},toggleSelectRecordsOnPage(){let o=this.getRecordsOnPage();if(this.areRecordsSelected(o)){this.deselectRecords(o);return}this.selectRecords(o)},toggleSelectRecords(o){this.areRecordsSelected(o)?this.deselectRecords(o):this.selectRecords(o)},getSelectedRecordsCount(){return this.isTrackingDeselectedRecords?(this.$refs.allSelectableRecordsCount?.value??this.deselectedRecords.size)-this.deselectedRecords.size:this.selectedRecords.size},getRecordsOnPage(){let o=[];for(let r of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])o.push(r.value);return o},selectRecords(o){n===1&&(this.deselectAllRecords(),o=o.slice(0,1));for(let r of o)if(!this.isRecordSelected(r)){if(this.isTrackingDeselectedRecords){this.deselectedRecords.delete(r);continue}this.selectedRecords.add(r)}this.updatedSelectedRecords()},deselectRecords(o){for(let r of o){if(this.isTrackingDeselectedRecords){this.deselectedRecords.add(r);continue}this.selectedRecords.delete(r)}this.updatedSelectedRecords()},updatedSelectedRecords(){if(n!==1){this.entangledSelectedRecords=[...this.selectedRecords];return}this.entangledSelectedRecords=[...this.selectedRecords][0]??null},toggleSelectedRecord(o){if(this.isRecordSelected(o)){this.deselectRecords([o]);return}this.selectRecords([o])},async selectAllRecords(){if(!t||s){this.isLoading=!0,this.selectedRecords=new Set(await l.getAllSelectableTableRecordKeys()),this.updatedSelectedRecords(),this.isLoading=!1;return}this.isTrackingDeselectedRecords=!0,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},canSelectAllRecords(){if(s){let c=this.getRecordsOnPage();return!this.areRecordsSelected(c)&&this.areRecordsToggleable(c)}let o=parseInt(this.$refs.allSelectableRecordsCount?.value);if(!o)return!1;let r=this.getSelectedRecordsCount();return o===r?!1:n===null||o<=n},deselectAllRecords(){this.isTrackingDeselectedRecords=!1,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},isRecordSelected(o){return this.isTrackingDeselectedRecords?!this.deselectedRecords.has(o):this.selectedRecords.has(o)},areRecordsSelected(o){return o.every(r=>this.isRecordSelected(r))},areRecordsToggleable(o){if(n===null||n===1)return!0;let r=o.filter(c=>this.isRecordSelected(c));return r.length===o.length?!0:this.getSelectedRecordsCount()+(o.length-r.length)<=n},toggleCollapseGroup(o){this.isGroupCollapsed(o)?e?this.groupVisibility.push(o):this.groupVisibility.splice(this.groupVisibility.indexOf(o),1):e?this.groupVisibility.splice(this.groupVisibility.indexOf(o),1):this.groupVisibility.push(o)},isGroupCollapsed(o){return e?!this.groupVisibility.includes(o):this.groupVisibility.includes(o)},resetCollapsedGroups(){this.groupVisibility=[]},watchForCheckboxClicks(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:o}=this.checkboxClickController;this.$root?.addEventListener("click",r=>r.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(r,r.target),{signal:o})},handleCheckboxClick(o,r){if(!this.lastChecked){this.lastChecked=r;return}if(o.shiftKey){let c=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!c.includes(this.lastChecked)){this.lastChecked=r;return}let a=c.indexOf(this.lastChecked),f=c.indexOf(r),d=[a,f].sort((u,m)=>u-m),h=[];for(let u=d[0];u<=d[1];u++)h.push(c[u].value);if(r.checked){if(!this.areRecordsToggleable(h)){r.checked=!1,this.deselectRecords([r.value]);return}this.selectRecords(h)}else this.deselectRecords(h)}this.lastChecked=r},toggleFiltersDropdown(){if(this.areFiltersOpen=!this.areFiltersOpen,this.areFiltersOpen){let o=Oe(this.$refs.filtersTriggerActionContainer,this.$refs.filtersContentContainer,async()=>{let{x:a,y:f}=await ke(this.$refs.filtersTriggerActionContainer,this.$refs.filtersContentContainer,{placement:"bottom-end",middleware:[Te(8),Le({padding:8})]});Object.assign(this.$refs.filtersContentContainer.style,{left:`${a}px`,top:`${f}px`})}),r=a=>{let f=this.$refs.filtersTriggerActionContainer,d=this.$refs.filtersContentContainer;d&&d.contains(a.target)||f&&f.contains(a.target)||(this.areFiltersOpen=!1,this.cleanUpFiltersDropdown&&(this.cleanUpFiltersDropdown(),this.cleanUpFiltersDropdown=null))};document.addEventListener("mousedown",r),document.addEventListener("touchstart",r,{passive:!0});let c=a=>{a.key==="Escape"&&r(a)};document.addEventListener("keydown",c),this.cleanUpFiltersDropdown=()=>{o(),document.removeEventListener("mousedown",r),document.removeEventListener("touchstart",r,{passive:!0}),document.removeEventListener("keydown",c)}}else this.cleanUpFiltersDropdown&&(this.cleanUpFiltersDropdown(),this.cleanUpFiltersDropdown=null)}});function ne({columns:e,isLive:t}){return{error:void 0,isLoading:!1,deferredColumns:[],columns:e,isLive:t,hasReordered:!1,init(){if(!this.columns||this.columns.length===0){this.columns=[];return}this.deferredColumns=JSON.parse(JSON.stringify(this.columns))},get groupedColumns(){let i={};return this.deferredColumns.filter(n=>n.type==="group").forEach(n=>{i[n.name]=this.calculateGroupedColumns(n)}),i},calculateGroupedColumns(i){if((i?.columns?.filter(r=>!r.isHidden)??[]).length===0)return{hidden:!0,checked:!1,disabled:!1,indeterminate:!1};let s=i.columns.filter(r=>!r.isHidden&&r.isToggleable!==!1);if(s.length===0)return{checked:!0,disabled:!0,indeterminate:!1};let l=s.filter(r=>r.isToggled).length,o=i.columns.filter(r=>!r.isHidden&&r.isToggleable===!1);return l===0&&o.length>0?{checked:!0,disabled:!1,indeterminate:!0}:l===0?{checked:!1,disabled:!1,indeterminate:!1}:l===s.length?{checked:!0,disabled:!1,indeterminate:!1}:{checked:!0,disabled:!1,indeterminate:!0}},getColumn(i,n=null){return n?this.deferredColumns.find(l=>l.type==="group"&&l.name===n)?.columns?.find(l=>l.name===i):this.deferredColumns.find(s=>s.name===i)},toggleGroup(i){let n=this.deferredColumns.find(c=>c.type==="group"&&c.name===i);if(!n?.columns)return;let s=this.calculateGroupedColumns(n);if(s.disabled)return;let o=n.columns.filter(c=>c.isToggleable!==!1).some(c=>c.isToggled),r=s.indeterminate?!0:!o;n.columns.filter(c=>c.isToggleable!==!1).forEach(c=>{c.isToggled=r}),this.deferredColumns=[...this.deferredColumns],this.isLive&&this.applyTableColumnManager()},toggleColumn(i,n=null){let s=this.getColumn(i,n);!s||s.isToggleable===!1||(s.isToggled=!s.isToggled,this.deferredColumns=[...this.deferredColumns],this.isLive&&this.applyTableColumnManager())},reorderColumns(i){let n=i.map(s=>s.split("::"));this.reorderTopLevel(n),this.hasReordered=!0,this.isLive&&this.applyTableColumnManager()},reorderGroupColumns(i,n){let s=this.deferredColumns.find(r=>r.type==="group"&&r.name===n);if(!s)return;let l=i.map(r=>r.split("::")),o=[];l.forEach(([r,c])=>{let a=s.columns.find(f=>f.name===c);a&&o.push(a)}),s.columns=o,this.deferredColumns=[...this.deferredColumns],this.hasReordered=!0,this.isLive&&this.applyTableColumnManager()},reorderTopLevel(i){let n=this.deferredColumns,s=[];i.forEach(([l,o])=>{let r=n.find(c=>l==="group"?c.type==="group"&&c.name===o:l==="column"?c.type!=="group"&&c.name===o:!1);r&&s.push(r)}),this.deferredColumns=s},async applyTableColumnManager(){this.isLoading=!0;try{this.columns=JSON.parse(JSON.stringify(this.deferredColumns)),await this.$wire.call("applyTableColumnManager",this.columns,this.hasReordered),this.hasReordered=!1,this.error=void 0}catch(i){this.error="Failed to update column visibility",console.error("Table toggle columns error:",i)}finally{this.isLoading=!1}}}}document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentTable",Ee),window.Alpine.data("filamentTableColumnManager",ne)});})();
2 |
--------------------------------------------------------------------------------