├── .nvmrc
├── public
├── favicon.ico
├── robots.txt
├── js
│ └── filament
│ │ ├── schemas
│ │ ├── components
│ │ │ ├── actions.js
│ │ │ ├── tabs.js
│ │ │ └── wizard.js
│ │ └── schemas.js
│ │ ├── forms
│ │ └── components
│ │ │ ├── textarea.js
│ │ │ ├── tags-input.js
│ │ │ ├── key-value.js
│ │ │ ├── checkbox-list.js
│ │ │ └── color-picker.js
│ │ ├── tables
│ │ ├── components
│ │ │ ├── columns
│ │ │ │ ├── checkbox.js
│ │ │ │ ├── toggle.js
│ │ │ │ └── text-input.js
│ │ │ └── table.js
│ │ └── tables.js
│ │ ├── actions
│ │ └── actions.js
│ │ ├── notifications
│ │ └── notifications.js
│ │ └── filament
│ │ └── app.js
├── 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
├── .htaccess
└── css
│ └── filament
│ └── support
│ └── support.css
├── .tool-versions
├── database
├── .gitignore
├── seeders
│ └── DatabaseSeeder.php
├── migrations
│ ├── 0001_01_01_000001_create_cache_table.php
│ ├── 2025_07_20_033509_create_media_table.php
│ ├── 0001_01_01_000000_create_users_table.php
│ ├── 0001_01_01_000002_create_jobs_table.php
│ └── 2025_07_20_024239_create_telescope_entries_table.php
└── factories
│ └── UserFactory.php
├── bootstrap
├── cache
│ └── .gitignore
├── providers.php
└── app.php
├── resources
├── js
│ ├── app.js
│ └── bootstrap.js
├── views
│ ├── components
│ │ ├── post.blade.php
│ │ ├── sidebar.blade.php
│ │ ├── navbar.blade.php
│ │ ├── feed.blade.php
│ │ └── layouts
│ │ │ └── guest.blade.php
│ └── welcome.blade.php
├── css
│ ├── filament
│ │ └── admin
│ │ │ └── theme.css
│ └── app.css
└── README.md
├── storage
├── logs
│ └── .gitignore
├── app
│ ├── private
│ │ └── .gitignore
│ ├── public
│ │ └── .gitignore
│ └── .gitignore
├── debugbar
│ └── .gitignore
└── framework
│ ├── testing
│ └── .gitignore
│ ├── views
│ └── .gitignore
│ ├── cache
│ ├── data
│ │ └── .gitignore
│ └── .gitignore
│ ├── sessions
│ └── .gitignore
│ └── .gitignore
├── .husky
└── pre-commit
├── .ncurc.json
├── .prettierignore
├── DEVELOPMENT.md
├── routes
├── web.php
└── console.php
├── tests
├── Unit
│ └── ExampleTest.php
├── TestCase.php
├── Feature
│ └── ExampleTest.php
├── Browser
│ ├── HomeTest.php
│ └── Filament
│ │ └── Admin
│ │ └── Auth
│ │ └── LoginTest.php
├── Arch
│ └── PresetTest.php
└── Pest.php
├── app
├── Http
│ └── Controllers
│ │ └── Controller.php
├── Filament
│ ├── README.md
│ └── Shared
│ │ └── Pages
│ │ └── LoginPage.php
├── Models
│ └── User.php
└── Providers
│ ├── TelescopeServiceProvider.php
│ ├── Filament
│ └── AdminPanelProvider.php
│ └── AppServiceProvider.php
├── phpstan.neon
├── .prettierrc
├── .gitattributes
├── .editorconfig
├── vite.config.js
├── docker
├── redis.Dockerfile
├── mailpit.Dockerfile
└── postgres.Dockerfile
├── artisan
├── .gitignore
├── LICENSE
├── config
├── services.php
├── filesystems.php
├── cache.php
├── mail.php
├── queue.php
├── auth.php
├── app.php
├── logging.php
├── database.php
├── telescope.php
├── session.php
└── debugbar.php
├── package.json
├── .env.testing
├── .env.docker
├── .env.example
├── phpunit.xml
├── docker-compose.env.yml
├── Makefile
├── pint.json
├── Taskfile.yml
├── rector.php
├── composer.json
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22.19.0
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | nodejs 22.19.0
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | echo "#Lint Staged"
2 | npx lint-staged
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 |
--------------------------------------------------------------------------------
/resources/views/components/post.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/resources/views/components/sidebar.blade.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !private/
3 | !public/
4 | !.gitignore
5 |
--------------------------------------------------------------------------------
/resources/views/components/navbar.blade.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/components/feed.blade.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.ncurc.json:
--------------------------------------------------------------------------------
1 | {
2 | "reject": [],
3 | "interactive": true,
4 | "format": ["group"]
5 | }
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .env*
4 | vendor/
5 | public/
6 | .git
7 | package-lock.json
8 | composer.lock
9 | storage/
10 |
--------------------------------------------------------------------------------
/DEVELOPMENT.md:
--------------------------------------------------------------------------------
1 | # Annotations
2 |
3 | [//]: # (Todas suas anotações sobre o projeto. Esse arquivo será mais importante que boa parte do projeto!)
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 | view('welcome'));
8 |
--------------------------------------------------------------------------------
/resources/js/bootstrap.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | window.axios = axios;
3 |
4 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
5 |
--------------------------------------------------------------------------------
/tests/Unit/ExampleTest.php:
--------------------------------------------------------------------------------
1 | toBeTrue();
7 | });
8 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | ({isSticky:!1,enableSticky(){this.isSticky=this.$el.getBoundingClientRect().top>0},disableSticky(){this.isSticky=!1}});export{i as default};
2 |
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-greek-wght-normal-AXVTPQD5.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-greek-wght-normal-AXVTPQD5.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-greek-wght-normal-IRE366VL.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-greek-wght-normal-IRE366VL.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-greek-wght-normal-N43DBLU2.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-greek-wght-normal-N43DBLU2.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-latin-wght-normal-NRMW37G5.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-latin-wght-normal-NRMW37G5.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-latin-wght-normal-O25CN4JL.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-latin-wght-normal-O25CN4JL.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-latin-wght-normal-OPIJAQLS.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-latin-wght-normal-OPIJAQLS.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-EWLSKVKN.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-EWLSKVKN.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-JEOLYBOO.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-JEOLYBOO.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-R5CMSONN.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-R5CMSONN.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-7GGTF7EK.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-7GGTF7EK.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-EOVOK2B5.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-EOVOK2B5.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-ZEVLMORV.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-ZEVLMORV.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-5SRY4DMZ.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-5SRY4DMZ.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-GZCIV3NH.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-GZCIV3NH.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-HA22NDSG.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-HA22NDSG.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-CE5GGD3W.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-CE5GGD3W.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-TWG5UU7E.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-TWG5UU7E.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-ASVAGXXE.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-ASVAGXXE.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-IYF56FF6.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-IYF56FF6.woff2
--------------------------------------------------------------------------------
/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-XKHXBTUO.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3pontos-tech/fullstack-test/HEAD/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-XKHXBTUO.woff2
--------------------------------------------------------------------------------
/resources/css/filament/admin/theme.css:
--------------------------------------------------------------------------------
1 | @import '../../../../vendor/filament/filament/resources/css/theme.css';
2 |
3 | @source '../../../../app/Filament/Admin/**/*';
4 | @source '../../../../resources/views/filament/admin/**/*';
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "endOfLine": "lf",
5 | "printWidth": 120,
6 | "semi": true,
7 | "plugins": ["prettier-plugin-blade", "prettier-plugin-tailwindcss"]
8 | }
9 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | get('/');
7 |
8 | $response->assertStatus(200);
9 | });
10 |
--------------------------------------------------------------------------------
/tests/Browser/HomeTest.php:
--------------------------------------------------------------------------------
1 | assertSee('Reddit')
8 | ->assertNoSmoke();
9 | })->skip();
10 |
--------------------------------------------------------------------------------
/bootstrap/providers.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
10 | })->purpose('Display an inspiring quote');
11 |
--------------------------------------------------------------------------------
/resources/README.md:
--------------------------------------------------------------------------------
1 | # Componentes
2 |
3 | Já existe uma base de componentes no projeto, você não necessáriamente precisa seguir à risca.
4 |
5 | Crie algo que seja confortável para você manter, ou siga o que já está no projeto.
6 |
7 | > Não necessáriamente o que existe hoje suporta tudo que precisamos, mas você terá que justificar sua escolha.
--------------------------------------------------------------------------------
/resources/views/welcome.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.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 | [{Makefile,**.mk}]
18 | indent_style = tab
19 |
20 | [{Dockerfile,*.Dockerfile}]
21 | indent_style = space
22 | indent_size = 4
--------------------------------------------------------------------------------
/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/filament/admin/theme.css', 'resources/css/app.css', 'resources/js/app.js'],
9 | refresh: true,
10 | }),
11 | tailwindcss(),
12 | ],
13 | });
14 |
--------------------------------------------------------------------------------
/tests/Arch/PresetTest.php:
--------------------------------------------------------------------------------
1 | preset()
7 | ->laravel()
8 | ->ignoring('App\Providers');
9 |
10 | arch('application must follow the defined PHP architectural rules')
11 | ->preset()
12 | ->php();
13 |
14 | arch('application must follow security best practices')
15 | ->preset()
16 | ->security();
17 |
--------------------------------------------------------------------------------
/docker/redis.Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1.15.0
2 |
3 | FROM redis:7-alpine3.21@sha256:bb186d083732f669da90be8b0f975a37812b15e913465bb14d845db72a4e3e08
4 |
5 | ENV TZ=America/Sao_Paulo
6 |
7 | RUN set -xeu; \
8 | apk update;\
9 | apk add --no-cache tzdata nano ca-certificates;\
10 | ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\
11 | echo "${TZ}" > /etc/timezone;\
12 | update-ca-certificates;\
13 | rm -rf /var/cache/apk/*;
14 |
--------------------------------------------------------------------------------
/resources/css/app.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss';
2 |
3 | @source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
4 | @source '../../storage/framework/views/*.php';
5 | @source '../**/*.blade.php';
6 | @source '../**/*.js';
7 |
8 | @theme {
9 | --font-sans:
10 | 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
11 | 'Segoe UI Symbol', 'Noto Color Emoji';
12 | }
13 |
--------------------------------------------------------------------------------
/docker/mailpit.Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1.15.0
2 |
3 | FROM axllent/mailpit:v1.27.8@sha256:6abc8e633df15eaf785cfcf38bae48e66f64beecdc03121e249d0f9ec15f0707
4 |
5 | ENV TZ=America/Sao_Paulo
6 |
7 | RUN set -xeu;\
8 | apk update;\
9 | apk add --no-cache tzdata nano ca-certificates;\
10 | ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\
11 | echo "${TZ}" > /etc/timezone; \
12 | update-ca-certificates;\
13 | rm -rf /var/cache/apk/*;
14 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | handleCommand(new ArgvInput);
17 |
18 | exit($status);
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | .env
4 | .env.backup
5 | .env.production
6 | .phpactor.json
7 | .phpstorm.meta.php
8 | .phpunit.result.cache
9 | .pint.result.cache
10 | /.fleet
11 | /.idea
12 | /.nova
13 | /.phpunit.cache
14 | /.rector.cache
15 | /.vscode
16 | /.zed
17 | /auth.json
18 | /node_modules
19 | /public/build
20 | /public/hot
21 | /public/storage
22 | /storage/*.key
23 | /storage/pail
24 | /vendor
25 | Homestead.json
26 | Homestead.yaml
27 | Thumbs.db
28 | _ide_helper.php
29 | tests/Browser/Screenshots
30 |
--------------------------------------------------------------------------------
/database/seeders/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | isLocal()) {
18 | User::factory()->admin()->create();
19 | }
20 |
21 | User::factory(10)->create();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/docker/postgres.Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1.15.0
2 |
3 | FROM postgres:17-alpine3.22@sha256:d5f196a551b5cef1c70853c6dd588f456d16ca4ea733e3f31c75bc1ae2f65f3f
4 |
5 | ENV TZ=America/Sao_Paulo
6 |
7 | RUN set -eux;\
8 | apk update;\
9 | apk add --no-cache tzdata nano ca-certificates;\
10 | apk add --no-cache postgresql-contrib postgis;\
11 | ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\
12 | echo "${TZ}" > /etc/timezone;\
13 | update-ca-certificates;\
14 | rm -rf /var/cache/apk/*;
15 |
--------------------------------------------------------------------------------
/app/Filament/Shared/Pages/LoginPage.php:
--------------------------------------------------------------------------------
1 | isProduction()) {
16 | $this->form->fill([
17 | 'email' => 'admin@admin.com',
18 | 'password' => 'password',
19 | 'remember' => true,
20 | ]);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/Browser/Filament/Admin/Auth/LoginTest.php:
--------------------------------------------------------------------------------
1 | create([
9 | 'email' => 'gvieira18@sycorax.com',
10 | 'password' => 'password',
11 | ]);
12 |
13 | visit('/admin/login')
14 | ->fill('form.email', $user->email)
15 | ->fill('form.password', 'password')
16 | ->submit()
17 | ->assertSee('Dashboard');
18 |
19 | $this->assertAuthenticated();
20 | })->skip();
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 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | withRouting(
11 | web: __DIR__.'/../routes/web.php',
12 | commands: __DIR__.'/../routes/console.php',
13 | health: '/up',
14 | )
15 | ->withMiddleware(function (Middleware $middleware): void {
16 | //
17 | })
18 | ->withExceptions(function (Exceptions $exceptions): void {
19 | //
20 | })->create();
21 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | handleRequest(Request::capture());
23 |
--------------------------------------------------------------------------------
/resources/views/components/layouts/guest.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Reddit-like Home • Laravel
8 |
9 |
10 |
11 |
12 |
13 |
14 | @vite(['resources/css/app.css', 'resources/js/app.js'])
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ $slot }}
22 |
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000001_create_cache_table.php:
--------------------------------------------------------------------------------
1 | string('key')->primary();
18 | $table->mediumText('value');
19 | $table->integer('expiration');
20 | });
21 |
22 | Schema::create('cache_locks', function (Blueprint $table): void {
23 | $table->string('key')->primary();
24 | $table->string('owner');
25 | $table->integer('expiration');
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Gabriel Vieira
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | [
20 | 'token' => env('POSTMARK_TOKEN'),
21 | ],
22 |
23 | 'resend' => [
24 | 'key' => env('RESEND_KEY'),
25 | ],
26 |
27 | 'ses' => [
28 | 'key' => env('AWS_ACCESS_KEY_ID'),
29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
31 | ],
32 |
33 | 'slack' => [
34 | 'notifications' => [
35 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
36 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
37 | ],
38 | ],
39 |
40 | ];
41 |
--------------------------------------------------------------------------------
/database/migrations/2025_07_20_033509_create_media_table.php:
--------------------------------------------------------------------------------
1 | id()->generatedAs();
15 |
16 | $table->morphs('model');
17 | $table->uuid()->nullable()->unique();
18 | $table->string('collection_name');
19 | $table->string('name');
20 | $table->string('file_name');
21 | $table->string('mime_type')->nullable();
22 | $table->string('disk');
23 | $table->string('conversions_disk')->nullable();
24 | $table->unsignedBigInteger('size');
25 | $table->json('manipulations');
26 | $table->json('custom_properties');
27 | $table->json('generated_conversions');
28 | $table->json('responsive_images');
29 | $table->unsignedInteger('order_column')->nullable()->index();
30 |
31 | $table->timestampsTz();
32 | });
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@gvieira18/sycorax",
3 | "license": "MIT",
4 | "$schema": "https://json.schemastore.org/package.json",
5 | "private": true,
6 | "type": "module",
7 | "engines": {
8 | "node": "^22"
9 | },
10 | "scripts": {
11 | "build": "vite build",
12 | "dev": "vite",
13 | "format": "npx prettier --write \"**/*.{js,ts,json,yml,md,css,blade.php}\"",
14 | "prepare": "npx -y husky",
15 | "outdated": "npx npm-check-updates"
16 | },
17 | "devDependencies": {
18 | "@tailwindcss/vite": "4.1.13",
19 | "axios": "1.12.2",
20 | "concurrently": "9.2.1",
21 | "husky": "9.1.7",
22 | "laravel-vite-plugin": "2.0.1",
23 | "lint-staged": "16.2.0",
24 | "npm-check-updates": "18.3.0",
25 | "playwright": "1.55.1",
26 | "prettier": "3.6.2",
27 | "prettier-plugin-blade": "2.1.21",
28 | "prettier-plugin-tailwindcss": "0.6.14",
29 | "tailwindcss": "4.1.13",
30 | "vite": "7.1.7"
31 | },
32 | "lint-staged": {
33 | "*.{js,ts,json,yml,md,css,blade.php}": [
34 | "prettier --write"
35 | ],
36 | "*.php": [
37 | "vendor/bin/pint",
38 | "vendor/bin/rector process"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/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.testing:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=base64:QR0ZFC+lpAKXTfvo0N82jnq7U9Q9bgNcy2xw1qDSYMo=
4 | APP_DEBUG=true
5 | APP_URL=http://127.0.0.1:8000
6 | APP_TIMEZONE=UTC
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | DEBUGBAR_ENABLED=false
13 | TELESCOPE_ENABLED=false
14 |
15 | APP_MAINTENANCE_DRIVER=file
16 | # APP_MAINTENANCE_STORE=database
17 |
18 | PHP_CLI_SERVER_WORKERS=4
19 |
20 | BCRYPT_ROUNDS=12
21 |
22 | LOG_CHANNEL=stack
23 | LOG_STACK=single
24 | LOG_DEPRECATIONS_CHANNEL=null
25 | LOG_LEVEL=debug
26 |
27 | DB_CONNECTION=sqlite
28 | DB_DATABASE=:memory:
29 |
30 | SESSION_DRIVER=database
31 | SESSION_LIFETIME=120
32 | SESSION_ENCRYPT=false
33 | SESSION_PATH=/
34 | SESSION_DOMAIN=null
35 |
36 | BROADCAST_CONNECTION=log
37 | FILESYSTEM_DISK=local
38 | QUEUE_CONNECTION=database
39 |
40 | CACHE_STORE=database
41 | # CACHE_PREFIX=
42 |
43 | MEMCACHED_HOST=127.0.0.1
44 |
45 | REDIS_CLIENT=phpredis
46 | REDIS_HOST=127.0.0.1
47 | REDIS_PASSWORD=null
48 | REDIS_PORT=6379
49 |
50 | MAIL_MAILER=log
51 | MAIL_SCHEME=null
52 | MAIL_HOST=127.0.0.1
53 | MAIL_PORT=1025
54 | MAIL_USERNAME=null
55 | MAIL_PASSWORD=null
56 | MAIL_FROM_ADDRESS="noreply@localhost.com"
57 | MAIL_FROM_NAME="${APP_NAME}"
58 |
59 | AWS_ACCESS_KEY_ID=
60 | AWS_SECRET_ACCESS_KEY=
61 | AWS_DEFAULT_REGION=us-east-1
62 | AWS_BUCKET=
63 | AWS_USE_PATH_STYLE_ENDPOINT=false
64 |
65 | VITE_APP_NAME="${APP_NAME}"
66 |
--------------------------------------------------------------------------------
/.env.docker:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://127.0.0.1:8000
6 | APP_TIMEZONE=UTC
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | DEBUGBAR_ENABLED=false
13 | TELESCOPE_ENABLED=false
14 |
15 | APP_MAINTENANCE_DRIVER=file
16 | # APP_MAINTENANCE_STORE=database
17 |
18 | PHP_CLI_SERVER_WORKERS=4
19 |
20 | BCRYPT_ROUNDS=12
21 |
22 | LOG_CHANNEL=stack
23 | LOG_STACK=single
24 | LOG_DEPRECATIONS_CHANNEL=null
25 | LOG_LEVEL=debug
26 |
27 | DB_CONNECTION=pgsql
28 | DB_HOST=127.0.0.1
29 | DB_PORT=5432
30 | DB_DATABASE=dev_sycorax
31 | DB_USERNAME=postgres
32 | DB_PASSWORD=postgres
33 |
34 | SESSION_DRIVER=redis
35 | SESSION_LIFETIME=120
36 | SESSION_ENCRYPT=false
37 | SESSION_PATH=/
38 | SESSION_DOMAIN=null
39 |
40 | BROADCAST_CONNECTION=log
41 | FILESYSTEM_DISK=local
42 | QUEUE_CONNECTION=redis
43 |
44 | CACHE_STORE=redis
45 | # CACHE_PREFIX=
46 |
47 | MEMCACHED_HOST=127.0.0.1
48 |
49 | REDIS_CLIENT=phpredis
50 | REDIS_HOST=127.0.0.1
51 | REDIS_PASSWORD=null
52 | REDIS_PORT=6379
53 |
54 | MAIL_MAILER=smtp
55 | MAIL_SCHEME=null
56 | MAIL_HOST=127.0.0.1
57 | MAIL_PORT=1025
58 | MAIL_USERNAME=null
59 | MAIL_PASSWORD=null
60 | MAIL_FROM_ADDRESS="noreply@localhost.com"
61 | MAIL_FROM_NAME="${APP_NAME}"
62 |
63 | AWS_ACCESS_KEY_ID=
64 | AWS_SECRET_ACCESS_KEY=
65 | AWS_DEFAULT_REGION=us-east-1
66 | AWS_BUCKET=
67 | AWS_USE_PATH_STYLE_ENDPOINT=false
68 |
69 | VITE_APP_NAME="${APP_NAME}"
70 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://127.0.0.1:8000
6 | APP_TIMEZONE=UTC
7 |
8 | APP_LOCALE=en
9 | APP_FALLBACK_LOCALE=en
10 | APP_FAKER_LOCALE=en_US
11 |
12 | DEBUGBAR_ENABLED=false
13 | TELESCOPE_ENABLED=false
14 |
15 | APP_MAINTENANCE_DRIVER=file
16 | # APP_MAINTENANCE_STORE=database
17 |
18 | PHP_CLI_SERVER_WORKERS=4
19 |
20 | BCRYPT_ROUNDS=12
21 |
22 | LOG_CHANNEL=stack
23 | LOG_STACK=single
24 | LOG_DEPRECATIONS_CHANNEL=null
25 | LOG_LEVEL=debug
26 |
27 | DB_CONNECTION=sqlite
28 | #DB_HOST=127.0.0.1
29 | #DB_PORT=5432
30 | DB_DATABASE=database/database.sqlite
31 | #DB_USERNAME=postgres
32 | #DB_PASSWORD=postgres
33 |
34 | SESSION_DRIVER=database
35 | SESSION_LIFETIME=120
36 | SESSION_ENCRYPT=false
37 | SESSION_PATH=/
38 | SESSION_DOMAIN=null
39 |
40 | BROADCAST_CONNECTION=log
41 | FILESYSTEM_DISK=local
42 | QUEUE_CONNECTION=database
43 |
44 | CACHE_STORE=database
45 | # CACHE_PREFIX=
46 |
47 | MEMCACHED_HOST=127.0.0.1
48 |
49 | REDIS_CLIENT=phpredis
50 | REDIS_HOST=127.0.0.1
51 | REDIS_PASSWORD=null
52 | REDIS_PORT=6379
53 |
54 | MAIL_MAILER=log
55 | MAIL_SCHEME=null
56 | MAIL_HOST=127.0.0.1
57 | MAIL_PORT=1025
58 | MAIL_USERNAME=null
59 | MAIL_PASSWORD=null
60 | MAIL_FROM_ADDRESS="noreply@localhost.com"
61 | MAIL_FROM_NAME="${APP_NAME}"
62 |
63 | AWS_ACCESS_KEY_ID=
64 | AWS_SECRET_ACCESS_KEY=
65 | AWS_DEFAULT_REGION=us-east-1
66 | AWS_BUCKET=
67 | AWS_USE_PATH_STYLE_ENDPOINT=false
68 |
69 | VITE_APP_NAME="${APP_NAME}"
70 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id()->generatedAs();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->timestampTz('email_verified_at')->nullable();
21 | $table->string('password');
22 | $table->rememberToken();
23 | $table->timestampsTz();
24 | });
25 |
26 | Schema::create('password_reset_tokens', function (Blueprint $table): void {
27 | $table->string('email')->primary();
28 | $table->string('token');
29 | $table->timestampTz('created_at')->nullable();
30 | });
31 |
32 | Schema::create('sessions', function (Blueprint $table): void {
33 | $table->string('id')->primary();
34 | $table->foreignId('user_id')->nullable()->index();
35 | $table->string('ip_address', 45)->nullable();
36 | $table->text('user_agent')->nullable();
37 | $table->longText('payload');
38 | $table->integer('last_activity')->index();
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | tests/Arch
10 |
11 |
12 | tests/Unit
13 |
14 |
15 | tests/Feature
16 |
17 |
18 | tests/Browser
19 |
20 |
21 |
22 |
23 | app
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | final class UserFactory extends Factory
16 | {
17 | protected $model = User::class;
18 |
19 | /**
20 | * The current password being used by the factory.
21 | */
22 | private static ?string $password = null;
23 |
24 | /**
25 | * Define the model's default state.
26 | *
27 | * @return array
28 | */
29 | public function definition(): array
30 | {
31 | return [
32 | 'name' => fake()->name(),
33 | 'email' => fake()->unique()->safeEmail(),
34 | 'email_verified_at' => now(),
35 | 'password' => self::$password ??= Hash::make('password'),
36 | 'remember_token' => Str::random(10),
37 | ];
38 | }
39 |
40 | /**
41 | * Indicate that the model's email address should be unverified.
42 | */
43 | public function unverified(): self
44 | {
45 | return $this->state(fn (array $attributes): array => [
46 | 'email_verified_at' => null,
47 | ]);
48 | }
49 |
50 | /**
51 | * Create a self-contained admin
52 | */
53 | public function admin(): self
54 | {
55 | return $this->state(fn (array $attributes): array => [
56 | 'name' => 'admin',
57 | 'email' => 'admin@admin.com',
58 | ]);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/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.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 |
--------------------------------------------------------------------------------
/app/Models/User.php:
--------------------------------------------------------------------------------
1 | */
20 | use HasFactory;
21 |
22 | use InteractsWithMedia;
23 | use Notifiable;
24 |
25 | /**
26 | * The attributes that are mass assignable.
27 | *
28 | * @var list
29 | */
30 | protected $fillable = [
31 | 'name',
32 | 'email',
33 | 'password',
34 | ];
35 |
36 | /**
37 | * The attributes that should be hidden for serialization.
38 | *
39 | * @var list
40 | */
41 | protected $hidden = [
42 | 'password',
43 | 'remember_token',
44 | ];
45 |
46 | public function canAccessPanel(Panel $panel): bool
47 | {
48 | return true;
49 | }
50 |
51 | public function getFilamentAvatarUrl(): ?string
52 | {
53 | $avatar = $this->getFirstMedia('profile-pictures');
54 |
55 | return $avatar?->getUrl();
56 | }
57 |
58 | /**
59 | * Get the attributes that should be cast.
60 | *
61 | * @return array
62 | */
63 | protected function casts(): array
64 | {
65 | return [
66 | 'email_verified_at' => 'datetime',
67 | 'password' => 'hashed',
68 | ];
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/public/js/filament/schemas/schemas.js:
--------------------------------------------------------------------------------
1 | (()=>{var d=()=>({isSticky:!1,enableSticky(){this.isSticky=this.$el.getBoundingClientRect().top>0},disableSticky(){this.isSticky=!1}});var m=function(n,e,i){let t=n;if(e.startsWith("/")&&(i=!0,e=e.slice(1)),i)return e;for(;e.startsWith("../");)t=t.includes(".")?t.slice(0,t.lastIndexOf(".")):null,e=e.slice(3);return["",null,void 0].includes(t)?e:["",null,void 0].includes(e)?t:`${t}.${e}`},u=n=>{let e=Alpine.findClosest(n,i=>i.__livewire);if(!e)throw"Could not find Livewire component in DOM tree.";return e.__livewire};document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentSchema",({livewireId:n})=>({handleFormValidationError(e){e.detail.livewireId===n&&this.$nextTick(()=>{let i=this.$el.querySelector("[data-validation-error]");if(!i)return;let t=i;for(;t;)t.dispatchEvent(new CustomEvent("expand")),t=t.parentNode;setTimeout(()=>i.closest("[data-field-wrapper]").scrollIntoView({behavior:"smooth",block:"start",inline:"start"}),200)})}})),window.Alpine.data("filamentSchemaComponent",({path:n,containerPath:e,isLive:i,$wire:t})=>({$statePath:n,$get:(r,l)=>t.$get(m(e,r,l)),$set:(r,l,a,o=null)=>(o??(o=i),t.$set(m(e,r,a),l,o)),get $state(){return t.$get(n)}})),window.Alpine.data("filamentActionsSchemaComponent",d),Livewire.hook("commit",({component:n,commit:e,respond:i,succeed:t,fail:r})=>{t(({snapshot:l,effects:a})=>{a.dispatches?.forEach(o=>{if(!o.params?.awaitSchemaComponent)return;let s=Array.from(n.el.querySelectorAll(`[wire\\:partial="schema-component::${o.params.awaitSchemaComponent}"]`)).filter(c=>u(c)===n);if(s.length!==1){if(s.length>1)throw`Multiple schema components found with key [${o.params.awaitSchemaComponent}].`;window.addEventListener(`schema-component-${n.id}-${o.params.awaitSchemaComponent}-loaded`,()=>{window.dispatchEvent(new CustomEvent(o.name,{detail:o.params}))},{once:!0})}})})})});})();
2 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000002_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | id()->generatedAs();
18 | $table->string('queue')->index();
19 | $table->longText('payload');
20 | $table->unsignedTinyInteger('attempts');
21 | $table->unsignedInteger('reserved_at')->nullable();
22 | $table->unsignedInteger('available_at');
23 | $table->unsignedInteger('created_at');
24 | });
25 |
26 | Schema::create('job_batches', function (Blueprint $table): void {
27 | $table->string('id')->primary();
28 | $table->string('name');
29 | $table->integer('total_jobs');
30 | $table->integer('pending_jobs');
31 | $table->integer('failed_jobs');
32 | $table->longText('failed_job_ids');
33 | $table->mediumText('options')->nullable();
34 | $table->integer('cancelled_at')->nullable();
35 | $table->integer('created_at');
36 | $table->integer('finished_at')->nullable();
37 | });
38 |
39 | Schema::create('failed_jobs', function (Blueprint $table): void {
40 | $table->id()->generatedAs();
41 | $table->string('uuid')->unique();
42 | $table->text('connection');
43 | $table->text('queue');
44 | $table->longText('payload');
45 | $table->longText('exception');
46 | $table->timestampTz('failed_at')->useCurrent();
47 | });
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/docker-compose.env.yml:
--------------------------------------------------------------------------------
1 | services:
2 | sycorax-db:
3 | build:
4 | dockerfile: docker/postgres.Dockerfile
5 | context: .
6 | image: postgres.local:17
7 | container_name: sycorax-db
8 | restart: no
9 | hostname: dev
10 | environment:
11 | POSTGRES_USER: postgres
12 | POSTGRES_PASSWORD: postgres
13 | POSTGRES_DB: dev_sycorax
14 | ports:
15 | - '5432:5432'
16 | volumes:
17 | - sycorax-db:/var/lib/postgresql/data
18 | networks:
19 | - dev-sycorax
20 | healthcheck:
21 | test: ['CMD-SHELL', 'pg_isready --quiet --username=postgres --dbname=postgres || exit 1']
22 | interval: 10s
23 | retries: 5
24 | timeout: 10s
25 | start_period: 15s
26 |
27 | sycorax-redis:
28 | build:
29 | dockerfile: docker/redis.Dockerfile
30 | context: .
31 | image: redis.local:7
32 | container_name: sycorax-redis
33 | restart: no
34 | hostname: dev
35 | ports:
36 | - '6379:6379'
37 | networks:
38 | - dev-sycorax
39 | healthcheck:
40 | test: ['CMD-SHELL', 'redis-cli --raw incr ping || exit 1']
41 | interval: 10s
42 | retries: 5
43 | timeout: 10s
44 | start_period: 15s
45 |
46 | sycorax-mailpit:
47 | build:
48 | dockerfile: docker/mailpit.Dockerfile
49 | context: .
50 | image: mailpit.local:1
51 | container_name: sycorax-mailpit
52 | restart: no
53 | hostname: dev
54 | ports:
55 | - '1025:1025'
56 | - '8025:8025'
57 | networks:
58 | - dev-sycorax
59 | healthcheck:
60 | test: ['CMD', '/mailpit', 'readyz']
61 | interval: 10s
62 | retries: 5
63 | timeout: 10s
64 | start_period: 10s
65 |
66 | volumes:
67 | sycorax-db:
68 | name: sycorax-db
69 |
70 | networks:
71 | dev-sycorax:
72 | name: dev-sycorax
73 | driver: bridge
74 | driver_opts:
75 | com.docker.network.bridge.name: dev-sycorax
76 | com.docker.network.bridge.host_binding_ipv4: '127.0.0.1'
77 |
--------------------------------------------------------------------------------
/tests/Pest.php:
--------------------------------------------------------------------------------
1 | group('feature')->in('Feature');
20 | pest()->group('unit')->in('Unit');
21 | pest()->group('browser')->in('Browser');
22 |
23 | pest()->extend(TestCase::class)
24 | ->use(RefreshDatabase::class)
25 | ->in('Feature', 'Unit', 'Browser');
26 |
27 | pest()->browser()->inFirefox();
28 |
29 | pest()->printer()->compact();
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Expectations
34 | |--------------------------------------------------------------------------
35 | |
36 | | When you're writing tests, you often need to check that values meet certain conditions. The
37 | | "expect()" function gives you access to a set of "expectations" methods that you can use
38 | | to assert different things. Of course, you may extend the Expectation API at any time.
39 | |
40 | */
41 |
42 | expect()->extend('toBeOne', fn () => $this->toBe(1));
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Functions
47 | |--------------------------------------------------------------------------
48 | |
49 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your
50 | | project that you don't want to repeat in every file. Here you can also expose helpers as
51 | | global functions to help you to reduce the number of lines of code in your test files.
52 | |
53 | */
54 |
55 | function something(): void
56 | {
57 | // ..
58 | }
59 |
--------------------------------------------------------------------------------
/database/migrations/2025_07_20_024239_create_telescope_entries_table.php:
--------------------------------------------------------------------------------
1 | getConnection());
25 |
26 | $schema->create('telescope_entries', function (Blueprint $table): void {
27 | $table->bigIncrements('sequence');
28 | $table->uuid('uuid');
29 | $table->uuid('batch_id');
30 | $table->string('family_hash')->nullable();
31 | $table->boolean('should_display_on_index')->default(true);
32 | $table->string('type', 20);
33 | $table->longText('content');
34 | $table->dateTimeTz('created_at')->nullable();
35 |
36 | $table->unique('uuid');
37 | $table->index('batch_id');
38 | $table->index('family_hash');
39 | $table->index('created_at');
40 | $table->index(['type', 'should_display_on_index']);
41 | });
42 |
43 | $schema->create('telescope_entries_tags', function (Blueprint $table): void {
44 | $table->uuid('entry_uuid');
45 | $table->string('tag');
46 |
47 | $table->primary(['entry_uuid', 'tag']);
48 | $table->index('tag');
49 |
50 | $table->foreign('entry_uuid')
51 | ->references('uuid')
52 | ->on('telescope_entries')
53 | ->onDelete('cascade');
54 | });
55 |
56 | $schema->create('telescope_monitoring', function (Blueprint $table): void {
57 | $table->string('tag')->primary();
58 | });
59 | }
60 | };
61 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := help
2 |
3 | .PHONY: help
4 | help: ## Show available commands
5 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
6 |
7 | .PHONY: pint
8 | pint: ## Run Pint code style fixer
9 | @export XDEBUG_MODE=off
10 | @$(CURDIR)/vendor/bin/pint --parallel
11 | @unset XDEBUG_MODE
12 |
13 | .PHONY: test-pint
14 | test-pint: ## Run Pint code style fixer in test mode
15 | @export XDEBUG_MODE=off
16 | @$(CURDIR)/vendor/bin/pint --test --parallel
17 | @unset XDEBUG_MODE=off
18 |
19 | .PHONY: rector
20 | rector: ## Run Rector
21 | @$(CURDIR)/vendor/bin/rector process
22 |
23 | .PHONY: test-rector
24 | test-rector: ## Run Rector in test mode
25 | @$(CURDIR)/vendor/bin/rector process --dry-run
26 |
27 | .PHONY: phpstan
28 | phpstan: ## Run PHPStan
29 | @$(CURDIR)/vendor/bin/phpstan analyse --ansi
30 |
31 | .PHONY: test-phpstan
32 | test-phpstan: ## Run PHPStan in test mode
33 | @$(CURDIR)/vendor/bin/phpstan analyse --ansi
34 |
35 | .PHONY: format
36 | format: rector pint ## Run Pint and Rector and try to fixes the source code
37 |
38 | .PHONY: check
39 | check: test-rector test-pint test-phpstan ## Run Pint, PHPStan with Rector in dry-run mode
40 |
41 | .PHONY: test
42 | test: ## Run all tests
43 | @$(CURDIR)/vendor/bin/pest --parallel --compact
44 |
45 | .PHONY: test-unit
46 | test-unit: ## Run unit tests
47 | @$(CURDIR)/vendor/bin/pest --parallel --compact --group=unit
48 |
49 | .PHONY: test-feature
50 | test-feature: ## Run feature tests
51 | @$(CURDIR)/vendor/bin/pest --parallel --compact --group=feature
52 |
53 | .PHONY: test-browser
54 | test-browser: ## Run browser tests
55 | @$(CURDIR)/vendor/bin/pest --parallel --compact --group=browser
56 |
57 | .PHONY: migrate-fresh
58 | migrate-fresh: ## Run migrations and seed the database
59 | @php artisan migrate:fresh --seed
60 |
61 | .PHONY: env-up
62 | env-up: ## Start the development environment
63 | @docker compose --file docker-compose.env.yml up --detach
64 |
65 | .PHONY: env-down
66 | env-down: ## Start the development environment
67 | @docker compose --file docker-compose.env.yml down --rmi all --volumes
68 |
69 | .PHONY: dev
70 | dev: ## Start the server
71 | @composer run-script dev
72 |
--------------------------------------------------------------------------------
/pint.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "laravel",
3 | "cache-file": ".pint.result.cache",
4 | "notPath": ["tests/TestCase.php"],
5 | "rules": {
6 | "operator_linebreak": true,
7 | "no_unused_imports": true,
8 | "array_push": true,
9 | "concat_space": { "spacing": "none" },
10 | "backtick_to_shell_exec": true,
11 | "date_time_immutable": true,
12 | "declare_strict_types": true,
13 | "lowercase_keywords": true,
14 | "lowercase_static_reference": true,
15 | "final_class": true,
16 | "final_internal_class": {
17 | "annotation_include": [],
18 | "annotation_exclude": ["internal"]
19 | },
20 | "final_public_method_for_abstract_class": true,
21 | "fully_qualified_strict_types": true,
22 | "global_namespace_import": {
23 | "import_classes": true,
24 | "import_constants": true,
25 | "import_functions": true
26 | },
27 | "mb_str_functions": true,
28 | "modernize_types_casting": true,
29 | "new_with_parentheses": false,
30 | "no_superfluous_elseif": true,
31 | "no_useless_else": true,
32 | "no_multiple_statements_per_line": true,
33 | "ordered_class_elements": {
34 | "order": [
35 | "use_trait",
36 | "case",
37 | "constant",
38 | "constant_public",
39 | "constant_protected",
40 | "constant_private",
41 | "property_public",
42 | "property_protected",
43 | "property_private",
44 | "construct",
45 | "destruct",
46 | "magic",
47 | "phpunit",
48 | "method_abstract",
49 | "method_public_static",
50 | "method_public",
51 | "method_protected_static",
52 | "method_protected",
53 | "method_private_static",
54 | "method_private"
55 | ],
56 | "sort_algorithm": "none"
57 | },
58 | "ordered_interfaces": true,
59 | "ordered_traits": true,
60 | "protected_to_private": true,
61 | "self_accessor": true,
62 | "self_static_accessor": true,
63 | "strict_comparison": true,
64 | "visibility_required": true
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/Providers/TelescopeServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->hasDebugModeEnabled()
21 | && $this->app->isLocal()
22 | && $this->hasProviderToRegister()
23 | && $this->hasEnvEnabled();
24 |
25 | Log::notice('Registering Telescope Service Provider');
26 |
27 | if (! $canRegister) {
28 | return;
29 | }
30 |
31 | $this->registerTelescopeServiceProvider();
32 | $this->setNightMode();
33 | $this->hideSensitiveRequestDetails();
34 | $this->setFilter();
35 | }
36 |
37 | public function setFilter(): void
38 | {
39 | Telescope::filter(fn (): bool => true);
40 | Telescope::filterBatch(fn (): bool => true);
41 | }
42 |
43 | /**
44 | * Register the Telescope gate.
45 | *
46 | * This gate determines who can access Telescope in non-local environments.
47 | */
48 | protected function gate(): void
49 | {
50 | Gate::define('viewTelescope', fn (User $user): bool => in_array($user->email, [
51 | //
52 | ]));
53 | }
54 |
55 | private function hasEnvEnabled(): bool
56 | {
57 | return config('telescope.enabled');
58 | }
59 |
60 | /**
61 | * Prevent sensitive request details from being logged by Telescope.
62 | */
63 | private function hideSensitiveRequestDetails(): void
64 | {
65 | Telescope::hideRequestParameters(['_token']);
66 | Telescope::hideRequestHeaders([
67 | 'cookie',
68 | 'x-csrf-token',
69 | 'x-xsrf-token',
70 | ]);
71 | }
72 |
73 | private function hasProviderToRegister(): bool
74 | {
75 | return class_exists(\Laravel\Telescope\TelescopeServiceProvider::class);
76 | }
77 |
78 | private function registerTelescopeServiceProvider(): void
79 | {
80 | $this->app->register(provider: \Laravel\Telescope\TelescopeServiceProvider::class);
81 | }
82 |
83 | private function setNightMode(): void
84 | {
85 | Telescope::night();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/Providers/Filament/AdminPanelProvider.php:
--------------------------------------------------------------------------------
1 | default()
31 | ->id('admin')
32 | ->path('admin')
33 | ->login(LoginPage::class)
34 | ->colors([
35 | 'primary' => Color::Amber,
36 | ])
37 | ->viteTheme('resources/css/filament/admin/theme.css')
38 | ->discoverResources(in: app_path('Filament/Admin/Resources'), for: 'App\\Filament\\Admin\\Resources')
39 | ->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
40 | ->pages([
41 | Dashboard::class,
42 | ])
43 | ->discoverWidgets(in: app_path('Filament/Admin/Widgets'), for: 'App\\Filament\\Admin\\Widgets')
44 | ->sidebarFullyCollapsibleOnDesktop()
45 | ->widgets([
46 | AccountWidget::class,
47 | FilamentInfoWidget::class,
48 | ])
49 | ->middleware([
50 | EncryptCookies::class,
51 | AddQueuedCookiesToResponse::class,
52 | StartSession::class,
53 | AuthenticateSession::class,
54 | ShareErrorsFromSession::class,
55 | VerifyCsrfToken::class,
56 | SubstituteBindings::class,
57 | DisableBladeIconComponents::class,
58 | DispatchServingFilamentEvent::class,
59 | ])
60 | ->authMiddleware([
61 | Authenticate::class,
62 | ]);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Taskfile.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | silent: true
4 |
5 | env:
6 | WORK_DIR: $(pwd)
7 |
8 | tasks:
9 | default:
10 | aliases:
11 | - help
12 | - h
13 | desc: Show available commands
14 | cmds:
15 | - task --list-all --sort none
16 |
17 | pint:
18 | desc: Run Pint code style fixer
19 | cmds:
20 | - export XDEBUG_MODE=off
21 | - '{{ .ROOT_DIR }}/vendor/bin/pint --parallel'
22 | - unset XDEBUG_MODE
23 |
24 | test-pint:
25 | desc: Run Pint code style fixer in test mode
26 | cmds:
27 | - '{{ .ROOT_DIR }}/vendor/bin/pint --parallel --test'
28 |
29 | rector:
30 | desc: Run Rector
31 | cmds:
32 | - '{{ .ROOT_DIR }}/vendor/bin/rector process'
33 |
34 | test-rector:
35 | desc: Run Rector in test mode
36 | cmds:
37 | - '{{ .ROOT_DIR }}/vendor/bin/rector process --dry-run'
38 |
39 | phpstan:
40 | desc: Run PHPStan
41 | cmds:
42 | - '{{ .ROOT_DIR }}/vendor/bin/phpstan analyse --ansi'
43 |
44 | test-phpstan:
45 | desc: Run PHPStan in test mode
46 | cmds:
47 | - '{{ .ROOT_DIR }}/vendor/bin/phpstan analyse --ansi'
48 |
49 | format:
50 | desc: Run Pint and Rector and try to fixes the source code
51 | aliases: [f]
52 | cmds:
53 | - task: rector
54 | - task: pint
55 |
56 | check:
57 | desc: Run Pint, PHPStan with Rector in dry-run mode
58 | aliases:
59 | - c
60 | cmds:
61 | - task: test-rector
62 | - task: test-pint
63 | - task: test-phpstan
64 |
65 | test:
66 | desc: Run all tests
67 | aliases: [t]
68 | cmd: '{{ .ROOT_DIR }}/vendor/bin/pest --parallel --compact'
69 |
70 | test-unit:
71 | desc: Run unit tests
72 | aliases: [tu]
73 | cmd: '{{ .ROOT_DIR }}/vendor/bin/pest --parallel --compact --group=unit'
74 |
75 | test-feature:
76 | desc: Run feature tests
77 | aliases: [tf]
78 | cmd: '{{ .ROOT_DIR }}/vendor/bin/pest --parallel --compact --group=feature'
79 |
80 | test-browser:
81 | desc: Run browser tests
82 | aliases: [tb]
83 | cmd: '{{ .ROOT_DIR }}/vendor/bin/pest --parallel --compact --group=browser'
84 |
85 | migrate-fresh:
86 | desc: Run migrations and seed the database
87 | aliases: [mf]
88 | cmd: 'php artisan migrate:fresh --seed'
89 |
90 | env-up:
91 | desc: Start the development environment
92 | aliases: [eu]
93 | cmd: 'docker compose --file docker-compose.env.yml up --detach'
94 |
95 | env-down:
96 | desc: Stop the development environment
97 | aliases: [ed]
98 | cmd: 'docker compose --file docker-compose.env.yml down --rmi all --volumes'
99 |
100 | dev:
101 | desc: Start the server
102 | aliases: [d]
103 | cmd: 'composer run-script dev'
104 |
--------------------------------------------------------------------------------
/config/filesystems.php:
--------------------------------------------------------------------------------
1 | env('FILESYSTEM_DISK', 'local'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Filesystem Disks
23 | |--------------------------------------------------------------------------
24 | |
25 | | Below you may configure as many filesystem disks as necessary, and you
26 | | may even configure multiple disks for the same driver. Examples for
27 | | most supported storage drivers are configured here for reference.
28 | |
29 | | Supported drivers: "local", "ftp", "sftp", "s3"
30 | |
31 | */
32 |
33 | 'disks' => [
34 |
35 | 'local' => [
36 | 'driver' => 'local',
37 | 'root' => storage_path('app/private'),
38 | 'serve' => true,
39 | 'throw' => false,
40 | 'report' => false,
41 | ],
42 |
43 | 'public' => [
44 | 'driver' => 'local',
45 | 'root' => storage_path('app/public'),
46 | 'url' => env('APP_URL').'/storage',
47 | 'visibility' => 'public',
48 | 'throw' => false,
49 | 'report' => false,
50 | ],
51 |
52 | 's3' => [
53 | 'driver' => 's3',
54 | 'key' => env('AWS_ACCESS_KEY_ID'),
55 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
56 | 'region' => env('AWS_DEFAULT_REGION'),
57 | 'bucket' => env('AWS_BUCKET'),
58 | 'url' => env('AWS_URL'),
59 | 'endpoint' => env('AWS_ENDPOINT'),
60 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
61 | 'throw' => false,
62 | 'report' => false,
63 | ],
64 |
65 | ],
66 |
67 | /*
68 | |--------------------------------------------------------------------------
69 | | Symbolic Links
70 | |--------------------------------------------------------------------------
71 | |
72 | | Here you may configure the symbolic links that will be created when the
73 | | `storage:link` Artisan command is executed. The array keys should be
74 | | the locations of the links and the values should be their targets.
75 | |
76 | */
77 |
78 | 'links' => [
79 | public_path('storage') => storage_path('app/public'),
80 | ],
81 |
82 | ];
83 |
--------------------------------------------------------------------------------
/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}
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | registerTelescope();
25 | $this->registerDebugbar();
26 | }
27 |
28 | /**
29 | * Bootstrap any application services.
30 | */
31 | public function boot(): void
32 | {
33 | $this->configureCommands();
34 | $this->configureDatabase();
35 | $this->configureDates();
36 | $this->configureVite();
37 | $this->configureUrl();
38 | $this->configureHttp();
39 | }
40 |
41 | /**
42 | * Configure the application's commands
43 | */
44 | private function configureCommands(): void
45 | {
46 | DB::prohibitDestructiveCommands($this->app->isProduction());
47 | }
48 |
49 | /**
50 | * Configure the application's models
51 | */
52 | private function configureDatabase(): void
53 | {
54 | Model::shouldBeStrict(! $this->app->isProduction());
55 | Model::automaticallyEagerLoadRelationships();
56 | }
57 |
58 | /**
59 | * Configure the application's Vite
60 | */
61 | private function configureVite(): void
62 | {
63 | Vite::useAggressivePrefetching();
64 | }
65 |
66 | /**
67 | * Configure the dates.
68 | */
69 | private function configureDates(): void
70 | {
71 | Date::use(CarbonImmutable::class);
72 | }
73 |
74 | /**
75 | * Configure the application's URL
76 | */
77 | private function configureUrl(): void
78 | {
79 | URL::forceHttps($this->app->isProduction());
80 | }
81 |
82 | /**
83 | * Configure Http module
84 | */
85 | private function configureHttp(): void
86 | {
87 | Http::preventStrayRequests();
88 | }
89 |
90 | private function registerTelescope(): void
91 | {
92 | if (app()->isLocal() && class_exists(\Laravel\Telescope\TelescopeServiceProvider::class)) {
93 | $this->app->register(TelescopeServiceProvider::class);
94 | }
95 | }
96 |
97 | private function registerDebugbar(): void
98 | {
99 | if (app()->isLocal()
100 | && app()->hasDebugModeEnabled()
101 | && class_exists(\Barryvdh\Debugbar\ServiceProvider::class)
102 | && config('debugbar.enabled')
103 | ) {
104 | Log::notice('Registering Debugbar Service Provider');
105 | $this->app->register(\Barryvdh\Debugbar\ServiceProvider::class);
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/public/js/filament/tables/components/table.js:
--------------------------------------------------------------------------------
1 | function n(){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){if(this.isLoading=!0,this.areRecordsSelected(this.getRecordsInGroupOnPage(e))){this.deselectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e));return}this.selectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e)),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 o=s.indexOf(this.lastChecked),r=s.indexOf(t),l=[o,r].sort((i,d)=>i-d),c=[];for(let i=l[0];i<=l[1];i++)s[i].checked=t.checked,c.push(s[i].value);t.checked?this.selectRecords(c):this.deselectRecords(c)}this.lastChecked=t}}}export{n as default};
2 |
--------------------------------------------------------------------------------
/config/cache.php:
--------------------------------------------------------------------------------
1 | env('CACHE_STORE', 'database'),
21 |
22 | /*
23 | |--------------------------------------------------------------------------
24 | | Cache Stores
25 | |--------------------------------------------------------------------------
26 | |
27 | | Here you may define all of the cache "stores" for your application as
28 | | well as their drivers. You may even define multiple stores for the
29 | | same cache driver to group types of items stored in your caches.
30 | |
31 | | Supported drivers: "array", "database", "file", "memcached",
32 | | "redis", "dynamodb", "octane", "null"
33 | |
34 | */
35 |
36 | 'stores' => [
37 |
38 | 'array' => [
39 | 'driver' => 'array',
40 | 'serialize' => false,
41 | ],
42 |
43 | 'database' => [
44 | 'driver' => 'database',
45 | 'connection' => env('DB_CACHE_CONNECTION'),
46 | 'table' => env('DB_CACHE_TABLE', 'cache'),
47 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
48 | 'lock_table' => env('DB_CACHE_LOCK_TABLE'),
49 | ],
50 |
51 | 'file' => [
52 | 'driver' => 'file',
53 | 'path' => storage_path('framework/cache/data'),
54 | 'lock_path' => storage_path('framework/cache/data'),
55 | ],
56 |
57 | 'memcached' => [
58 | 'driver' => 'memcached',
59 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
60 | 'sasl' => [
61 | env('MEMCACHED_USERNAME'),
62 | env('MEMCACHED_PASSWORD'),
63 | ],
64 | 'options' => [
65 | // Memcached::OPT_CONNECT_TIMEOUT => 2000,
66 | ],
67 | 'servers' => [
68 | [
69 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'),
70 | 'port' => env('MEMCACHED_PORT', 11211),
71 | 'weight' => 100,
72 | ],
73 | ],
74 | ],
75 |
76 | 'redis' => [
77 | 'driver' => 'redis',
78 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
79 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
80 | ],
81 |
82 | 'dynamodb' => [
83 | 'driver' => 'dynamodb',
84 | 'key' => env('AWS_ACCESS_KEY_ID'),
85 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
86 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
87 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
88 | 'endpoint' => env('DYNAMODB_ENDPOINT'),
89 | ],
90 |
91 | 'octane' => [
92 | 'driver' => 'octane',
93 | ],
94 |
95 | ],
96 |
97 | /*
98 | |--------------------------------------------------------------------------
99 | | Cache Key Prefix
100 | |--------------------------------------------------------------------------
101 | |
102 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache
103 | | stores, there might be other applications using the same cache. For
104 | | that reason, you may prefix every cache key to avoid collisions.
105 | |
106 | */
107 |
108 | 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
109 |
110 | ];
111 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | withPaths([
28 | __DIR__.'/app',
29 | __DIR__.'/config',
30 | __DIR__.'/bootstrap/app.php',
31 | __DIR__.'/database',
32 | __DIR__.'/routes',
33 | __DIR__.'/public',
34 | __DIR__.'/tests',
35 | ])
36 | ->withCache(
37 | cacheDirectory: __DIR__.'/.rector.cache',
38 | cacheClass: FileCacheStorage::class,
39 | )
40 | ->withImportNames(importShortClasses: false, removeUnusedImports: true)
41 | ->withRootFiles()
42 | ->withBootstrapFiles([__DIR__.'/vendor/larastan/larastan/bootstrap.php'])
43 | ->withPHPStanConfigs([__DIR__.'/phpstan.neon'])
44 | ->withPreparedSets(
45 | deadCode: true,
46 | codeQuality: true,
47 | codingStyle: true,
48 | typeDeclarations: true,
49 | privatization: true,
50 | instanceOf: true,
51 | earlyReturn: true,
52 | strictBooleans: true,
53 | carbon: true,
54 | rectorPreset: true,
55 | )
56 | ->withPhpSets(php84: true)
57 | ->withRules([
58 | ValidationRuleArrayStringValueToArrayRector::class,
59 | AnonymousMigrationsRector::class,
60 | AssertStatusToAssertMethodRector::class,
61 | AddExtendsAnnotationToModelFactoriesRector::class,
62 | AddGenericReturnTypeToRelationsRector::class,
63 | ApplyDefaultInsteadOfNullCoalesceRector::class,
64 | DispatchToHelperFunctionsRector::class,
65 | EmptyToBlankAndFilledFuncRector::class,
66 | ModelCastsPropertyToCastsMethodRector::class,
67 | NotFilledBlankFuncCallToBlankFilledFuncCallRector::class,
68 | RefactorBlueprintGeometryColumnsRector::class,
69 | ReplaceExpectsMethodsInTestsRector::class,
70 | ReplaceFakerInstanceWithHelperRector::class,
71 | ])
72 | ->withSets([
73 | LaravelSetList::LARAVEL_ARRAYACCESS_TO_METHOD_CALL,
74 | LaravelSetList::LARAVEL_ARRAY_STR_FUNCTION_TO_STATIC_CALL,
75 | LaravelSetList::LARAVEL_CODE_QUALITY,
76 | LaravelSetList::LARAVEL_COLLECTION,
77 | LaravelSetList::LARAVEL_CONTAINER_STRING_TO_FULLY_QUALIFIED_NAME,
78 | LaravelSetList::LARAVEL_ELOQUENT_MAGIC_METHOD_TO_QUERY_BUILDER,
79 | LaravelSetList::LARAVEL_FACADE_ALIASES_TO_FULL_NAMES,
80 | LaravelSetList::LARAVEL_IF_HELPERS,
81 | LaravelSetList::LARAVEL_LEGACY_FACTORIES_TO_CLASSES,
82 | ])
83 | ->withSkip([
84 | AddOverrideAttributeToOverriddenMethodsRector::class,
85 | ChangeOrIfContinueToMultiContinueRector::class,
86 | PostIncDecToPreIncDecRector::class,
87 | AddArrowFunctionReturnTypeRector::class,
88 | ]);
89 |
--------------------------------------------------------------------------------
/config/mail.php:
--------------------------------------------------------------------------------
1 | env('MAIL_MAILER', 'log'),
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Mailer Configurations
24 | |--------------------------------------------------------------------------
25 | |
26 | | Here you may configure all of the mailers used by your application plus
27 | | their respective settings. Several examples have been configured for
28 | | you and you are free to add your own as your application requires.
29 | |
30 | | Laravel supports a variety of mail "transport" drivers that can be used
31 | | when delivering an email. You may specify which one you're using for
32 | | your mailers below. You may also add additional mailers if needed.
33 | |
34 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
35 | | "postmark", "resend", "log", "array",
36 | | "failover", "roundrobin"
37 | |
38 | */
39 |
40 | 'mailers' => [
41 |
42 | 'smtp' => [
43 | 'transport' => 'smtp',
44 | 'scheme' => env('MAIL_SCHEME'),
45 | 'url' => env('MAIL_URL'),
46 | 'host' => env('MAIL_HOST', '127.0.0.1'),
47 | 'port' => env('MAIL_PORT', 2525),
48 | 'username' => env('MAIL_USERNAME'),
49 | 'password' => env('MAIL_PASSWORD'),
50 | 'timeout' => null,
51 | 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
52 | ],
53 |
54 | 'ses' => [
55 | 'transport' => 'ses',
56 | ],
57 |
58 | 'postmark' => [
59 | 'transport' => 'postmark',
60 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
61 | // 'client' => [
62 | // 'timeout' => 5,
63 | // ],
64 | ],
65 |
66 | 'resend' => [
67 | 'transport' => 'resend',
68 | ],
69 |
70 | 'sendmail' => [
71 | 'transport' => 'sendmail',
72 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
73 | ],
74 |
75 | 'log' => [
76 | 'transport' => 'log',
77 | 'channel' => env('MAIL_LOG_CHANNEL'),
78 | ],
79 |
80 | 'array' => [
81 | 'transport' => 'array',
82 | ],
83 |
84 | 'failover' => [
85 | 'transport' => 'failover',
86 | 'mailers' => [
87 | 'smtp',
88 | 'log',
89 | ],
90 | 'retry_after' => 60,
91 | ],
92 |
93 | 'roundrobin' => [
94 | 'transport' => 'roundrobin',
95 | 'mailers' => [
96 | 'ses',
97 | 'postmark',
98 | ],
99 | 'retry_after' => 60,
100 | ],
101 |
102 | ],
103 |
104 | /*
105 | |--------------------------------------------------------------------------
106 | | Global "From" Address
107 | |--------------------------------------------------------------------------
108 | |
109 | | You may wish for all emails sent by your application to be sent from
110 | | the same address. Here you may specify a name and address that is
111 | | used globally for all emails that are sent by your application.
112 | |
113 | */
114 |
115 | 'from' => [
116 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
117 | 'name' => env('MAIL_FROM_NAME', 'Example'),
118 | ],
119 |
120 | ];
121 |
--------------------------------------------------------------------------------
/config/queue.php:
--------------------------------------------------------------------------------
1 | env('QUEUE_CONNECTION', 'database'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Queue Connections
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may configure the connection options for every queue backend
26 | | used by your application. An example configuration is provided for
27 | | each backend supported by Laravel. You're also free to add more.
28 | |
29 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
30 | |
31 | */
32 |
33 | 'connections' => [
34 |
35 | 'sync' => [
36 | 'driver' => 'sync',
37 | ],
38 |
39 | 'database' => [
40 | 'driver' => 'database',
41 | 'connection' => env('DB_QUEUE_CONNECTION'),
42 | 'table' => env('DB_QUEUE_TABLE', 'jobs'),
43 | 'queue' => env('DB_QUEUE', 'default'),
44 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
45 | 'after_commit' => false,
46 | ],
47 |
48 | 'beanstalkd' => [
49 | 'driver' => 'beanstalkd',
50 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
51 | 'queue' => env('BEANSTALKD_QUEUE', 'default'),
52 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
53 | 'block_for' => 0,
54 | 'after_commit' => false,
55 | ],
56 |
57 | 'sqs' => [
58 | 'driver' => 'sqs',
59 | 'key' => env('AWS_ACCESS_KEY_ID'),
60 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
61 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
62 | 'queue' => env('SQS_QUEUE', 'default'),
63 | 'suffix' => env('SQS_SUFFIX'),
64 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
65 | 'after_commit' => false,
66 | ],
67 |
68 | 'redis' => [
69 | 'driver' => 'redis',
70 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
71 | 'queue' => env('REDIS_QUEUE', 'default'),
72 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
73 | 'block_for' => null,
74 | 'after_commit' => false,
75 | ],
76 |
77 | ],
78 |
79 | /*
80 | |--------------------------------------------------------------------------
81 | | Job Batching
82 | |--------------------------------------------------------------------------
83 | |
84 | | The following options configure the database and table that store job
85 | | batching information. These options can be updated to any database
86 | | connection and table which has been defined by your application.
87 | |
88 | */
89 |
90 | 'batching' => [
91 | 'database' => env('DB_CONNECTION', 'sqlite'),
92 | 'table' => 'job_batches',
93 | ],
94 |
95 | /*
96 | |--------------------------------------------------------------------------
97 | | Failed Queue Jobs
98 | |--------------------------------------------------------------------------
99 | |
100 | | These options configure the behavior of failed queue job logging so you
101 | | can control how and where failed jobs are stored. Laravel ships with
102 | | support for storing failed jobs in a simple file or in a database.
103 | |
104 | | Supported drivers: "database-uuids", "dynamodb", "file", "null"
105 | |
106 | */
107 |
108 | 'failed' => [
109 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
110 | 'database' => env('DB_CONNECTION', 'sqlite'),
111 | 'table' => 'failed_jobs',
112 | ],
113 |
114 | ];
115 |
--------------------------------------------------------------------------------
/config/auth.php:
--------------------------------------------------------------------------------
1 | [
21 | 'guard' => env('AUTH_GUARD', 'web'),
22 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
23 | ],
24 |
25 | /*
26 | |--------------------------------------------------------------------------
27 | | Authentication Guards
28 | |--------------------------------------------------------------------------
29 | |
30 | | Next, you may define every authentication guard for your application.
31 | | Of course, a great default configuration has been defined for you
32 | | which utilizes session storage plus the Eloquent user provider.
33 | |
34 | | All authentication guards have a user provider, which defines how the
35 | | users are actually retrieved out of your database or other storage
36 | | system used by the application. Typically, Eloquent is utilized.
37 | |
38 | | Supported: "session"
39 | |
40 | */
41 |
42 | 'guards' => [
43 | 'web' => [
44 | 'driver' => 'session',
45 | 'provider' => 'users',
46 | ],
47 | ],
48 |
49 | /*
50 | |--------------------------------------------------------------------------
51 | | User Providers
52 | |--------------------------------------------------------------------------
53 | |
54 | | All authentication guards have a user provider, which defines how the
55 | | users are actually retrieved out of your database or other storage
56 | | system used by the application. Typically, Eloquent is utilized.
57 | |
58 | | If you have multiple user tables or models you may configure multiple
59 | | providers to represent the model / table. These providers may then
60 | | be assigned to any extra authentication guards you have defined.
61 | |
62 | | Supported: "database", "eloquent"
63 | |
64 | */
65 |
66 | 'providers' => [
67 | 'users' => [
68 | 'driver' => 'eloquent',
69 | 'model' => env('AUTH_MODEL', User::class),
70 | ],
71 |
72 | // 'users' => [
73 | // 'driver' => 'database',
74 | // 'table' => 'users',
75 | // ],
76 | ],
77 |
78 | /*
79 | |--------------------------------------------------------------------------
80 | | Resetting Passwords
81 | |--------------------------------------------------------------------------
82 | |
83 | | These configuration options specify the behavior of Laravel's password
84 | | reset functionality, including the table utilized for token storage
85 | | and the user provider that is invoked to actually retrieve users.
86 | |
87 | | The expiry time is the number of minutes that each reset token will be
88 | | considered valid. This security feature keeps tokens short-lived so
89 | | they have less time to be guessed. You may change this as needed.
90 | |
91 | | The throttle setting is the number of seconds a user must wait before
92 | | generating more password reset tokens. This prevents the user from
93 | | quickly generating a very large amount of password reset tokens.
94 | |
95 | */
96 |
97 | 'passwords' => [
98 | 'users' => [
99 | 'provider' => 'users',
100 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
101 | 'expire' => 60,
102 | 'throttle' => 60,
103 | ],
104 | ],
105 |
106 | /*
107 | |--------------------------------------------------------------------------
108 | | Password Confirmation Timeout
109 | |--------------------------------------------------------------------------
110 | |
111 | | Here you may define the number of seconds before a password confirmation
112 | | window expires and users are asked to re-enter their password via the
113 | | confirmation screen. By default, the timeout lasts for three hours.
114 | |
115 | */
116 |
117 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
118 |
119 | ];
120 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://getcomposer.org/schema.json",
3 | "name": "gvieira18/sycorax",
4 | "type": "project",
5 | "description": "",
6 | "keywords": [
7 | "laravel"
8 | ],
9 | "license": "MIT",
10 | "require": {
11 | "php": "^8.4",
12 | "filament/filament": "^4.0.20",
13 | "filament/spatie-laravel-media-library-plugin": "^4.0.20",
14 | "laravel/framework": "^12.31.1",
15 | "laravel/tinker": "^2.10.1"
16 | },
17 | "require-dev": {
18 | "barryvdh/laravel-debugbar": "^3.16.0",
19 | "barryvdh/laravel-ide-helper": "^3.6.0",
20 | "driftingly/rector-laravel": "^2.0.7",
21 | "fakerphp/faker": "^1.24.1",
22 | "larastan/larastan": "^3.7.2",
23 | "laravel/pail": "^1.2.3",
24 | "laravel/pint": "^1.25.1",
25 | "laravel/sail": "^1.46.0",
26 | "laravel/telescope": "^5.12.0",
27 | "mockery/mockery": "^1.6.12",
28 | "nunomaduro/collision": "^8.8.2",
29 | "pestphp/pest": "~4.0.4",
30 | "pestphp/pest-plugin-browser": "~4.0.3",
31 | "pestphp/pest-plugin-faker": "^4.0.0",
32 | "pestphp/pest-plugin-laravel": "^4.0.0",
33 | "pestphp/pest-plugin-livewire": "^4.0.1",
34 | "rector/rector": "^2.1.7"
35 | },
36 | "autoload": {
37 | "psr-4": {
38 | "App\\": "app/",
39 | "Database\\Factories\\": "database/factories/",
40 | "Database\\Seeders\\": "database/seeders/"
41 | }
42 | },
43 | "autoload-dev": {
44 | "psr-4": {
45 | "Tests\\": "tests/"
46 | }
47 | },
48 | "scripts": {
49 | "post-autoload-dump": [
50 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
51 | "@php artisan package:discover --ansi",
52 | "@php artisan filament:upgrade"
53 | ],
54 | "post-update-cmd": [
55 | "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
56 | ],
57 | "post-root-package-install": [
58 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
59 | ],
60 | "post-create-project-cmd": [
61 | "@php artisan key:generate --ansi",
62 | "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
63 | "@php artisan migrate --graceful --ansi"
64 | ],
65 | "ide-helper": [
66 | "@php artisan ide-helper:generate -q",
67 | "@php artisan ide-helper:meta -q"
68 | ],
69 | "setup": [
70 | "@php composer run post-root-package-install",
71 | "@php composer run post-create-project-cmd",
72 | "@php artisan storage:link --ansi",
73 | "@php composer run ide-helper"
74 | ],
75 | "dev": [
76 | "Composer\\Config::disableProcessTimeout",
77 | "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"
78 | ],
79 | "pint": "vendor/bin/pint --parallel",
80 | "test:pint": "vendor/bin/pint --test --parallel",
81 | "rector": "vendor/bin/rector",
82 | "test:rector": "vendor/bin/rector --dry-run",
83 | "phpstan": "vendor/bin/phpstan analyse --ansi",
84 | "test:phpstan": "vendor/bin/phpstan analyse --ansi",
85 | "test:unit": "vendor/bin/pest --parallel --compact --group=unit",
86 | "test:feature": "vendor/bin/pest --parallel --compact --group=feature",
87 | "test:browser": "vendor/bin/pest --parallel --compact --group=browser",
88 | "test:cov": "vendor/bin/pest --parallel --compact --coverage",
89 | "check": [
90 | "@test:rector",
91 | "@test:pint",
92 | "@test:phpstan"
93 | ],
94 | "test": [
95 | "@php artisan config:clear --ansi",
96 | "vendor/bin/pest --parallel --compact"
97 | ]
98 | },
99 | "extra": {
100 | "laravel": {
101 | "dont-discover": [
102 | "laravel/telescope",
103 | "barryvdh/laravel-debugbar"
104 | ]
105 | }
106 | },
107 | "config": {
108 | "optimize-autoloader": true,
109 | "classmap-authoritative": true,
110 | "bump-after-update": true,
111 | "preferred-install": "dist",
112 | "sort-packages": true,
113 | "allow-plugins": {
114 | "pestphp/pest-plugin": true,
115 | "php-http/discovery": true
116 | }
117 | },
118 | "minimum-stability": "stable",
119 | "prefer-stable": true
120 | }
121 |
--------------------------------------------------------------------------------
/config/app.php:
--------------------------------------------------------------------------------
1 | env('APP_NAME', 'Laravel'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Application Environment
23 | |--------------------------------------------------------------------------
24 | |
25 | | This value determines the "environment" your application is currently
26 | | running in. This may determine how you prefer to configure various
27 | | services the application utilizes. Set this in your ".env" file.
28 | |
29 | */
30 |
31 | 'env' => env('APP_ENV', 'production'),
32 |
33 | /*
34 | |--------------------------------------------------------------------------
35 | | Application Debug Mode
36 | |--------------------------------------------------------------------------
37 | |
38 | | When your application is in debug mode, detailed error messages with
39 | | stack traces will be shown on every error that occurs within your
40 | | application. If disabled, a simple generic error page is shown.
41 | |
42 | */
43 |
44 | 'debug' => (bool) env('APP_DEBUG', false),
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Application URL
49 | |--------------------------------------------------------------------------
50 | |
51 | | This URL is used by the console to properly generate URLs when using
52 | | the Artisan command line tool. You should set this to the root of
53 | | the application so that it's available within Artisan commands.
54 | |
55 | */
56 |
57 | 'url' => env('APP_URL', 'http://localhost'),
58 |
59 | /*
60 | |--------------------------------------------------------------------------
61 | | Application Timezone
62 | |--------------------------------------------------------------------------
63 | |
64 | | Here you may specify the default timezone for your application, which
65 | | will be used by the PHP date and date-time functions. The timezone
66 | | is set to "UTC" by default as it is suitable for most use cases.
67 | |
68 | */
69 |
70 | 'timezone' => env('APP_TIMEZONE', 'UTC'),
71 |
72 | /*
73 | |--------------------------------------------------------------------------
74 | | Application Locale Configuration
75 | |--------------------------------------------------------------------------
76 | |
77 | | The application locale determines the default locale that will be used
78 | | by Laravel's translation / localization methods. This option can be
79 | | set to any locale for which you plan to have translation strings.
80 | |
81 | */
82 |
83 | 'locale' => env('APP_LOCALE', 'en'),
84 |
85 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
86 |
87 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
88 |
89 | /*
90 | |--------------------------------------------------------------------------
91 | | Encryption Key
92 | |--------------------------------------------------------------------------
93 | |
94 | | This key is utilized by Laravel's encryption services and should be set
95 | | to a random, 32 character string to ensure that all encrypted values
96 | | are secure. You should do this prior to deploying the application.
97 | |
98 | */
99 |
100 | 'cipher' => 'AES-256-CBC',
101 |
102 | 'key' => env('APP_KEY'),
103 |
104 | 'previous_keys' => [
105 | ...array_filter(
106 | explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
107 | ),
108 | ],
109 |
110 | /*
111 | |--------------------------------------------------------------------------
112 | | Maintenance Mode Driver
113 | |--------------------------------------------------------------------------
114 | |
115 | | These configuration options determine the driver used to determine and
116 | | manage Laravel's "maintenance mode" status. The "cache" driver will
117 | | allow maintenance mode to be controlled across multiple machines.
118 | |
119 | | Supported drivers: "file", "cache"
120 | |
121 | */
122 |
123 | 'maintenance' => [
124 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
125 | 'store' => env('APP_MAINTENANCE_STORE', 'database'),
126 | ],
127 |
128 | ];
129 |
--------------------------------------------------------------------------------
/config/logging.php:
--------------------------------------------------------------------------------
1 | env('LOG_CHANNEL', 'stack'),
24 |
25 | /*
26 | |--------------------------------------------------------------------------
27 | | Deprecations Log Channel
28 | |--------------------------------------------------------------------------
29 | |
30 | | This option controls the log channel that should be used to log warnings
31 | | regarding deprecated PHP and library features. This allows you to get
32 | | your application ready for upcoming major versions of dependencies.
33 | |
34 | */
35 |
36 | 'deprecations' => [
37 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
38 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false),
39 | ],
40 |
41 | /*
42 | |--------------------------------------------------------------------------
43 | | Log Channels
44 | |--------------------------------------------------------------------------
45 | |
46 | | Here you may configure the log channels for your application. Laravel
47 | | utilizes the Monolog PHP logging library, which includes a variety
48 | | of powerful log handlers and formatters that you're free to use.
49 | |
50 | | Available drivers: "single", "daily", "slack", "syslog",
51 | | "errorlog", "monolog", "custom", "stack"
52 | |
53 | */
54 |
55 | 'channels' => [
56 |
57 | 'stack' => [
58 | 'driver' => 'stack',
59 | 'channels' => explode(',', (string) env('LOG_STACK', 'single')),
60 | 'ignore_exceptions' => false,
61 | ],
62 |
63 | 'single' => [
64 | 'driver' => 'single',
65 | 'path' => storage_path('logs/laravel.log'),
66 | 'level' => env('LOG_LEVEL', 'debug'),
67 | 'replace_placeholders' => true,
68 | ],
69 |
70 | 'daily' => [
71 | 'driver' => 'daily',
72 | 'path' => storage_path('logs/laravel.log'),
73 | 'level' => env('LOG_LEVEL', 'debug'),
74 | 'days' => env('LOG_DAILY_DAYS', 14),
75 | 'replace_placeholders' => true,
76 | ],
77 |
78 | 'slack' => [
79 | 'driver' => 'slack',
80 | 'url' => env('LOG_SLACK_WEBHOOK_URL'),
81 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
82 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
83 | 'level' => env('LOG_LEVEL', 'critical'),
84 | 'replace_placeholders' => true,
85 | ],
86 |
87 | 'papertrail' => [
88 | 'driver' => 'monolog',
89 | 'level' => env('LOG_LEVEL', 'debug'),
90 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
91 | 'handler_with' => [
92 | 'host' => env('PAPERTRAIL_URL'),
93 | 'port' => env('PAPERTRAIL_PORT'),
94 | 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
95 | ],
96 | 'processors' => [PsrLogMessageProcessor::class],
97 | ],
98 |
99 | 'stderr' => [
100 | 'driver' => 'monolog',
101 | 'level' => env('LOG_LEVEL', 'debug'),
102 | 'handler' => StreamHandler::class,
103 | 'handler_with' => [
104 | 'stream' => 'php://stderr',
105 | ],
106 | 'formatter' => env('LOG_STDERR_FORMATTER'),
107 | 'processors' => [PsrLogMessageProcessor::class],
108 | ],
109 |
110 | 'syslog' => [
111 | 'driver' => 'syslog',
112 | 'level' => env('LOG_LEVEL', 'debug'),
113 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
114 | 'replace_placeholders' => true,
115 | ],
116 |
117 | 'errorlog' => [
118 | 'driver' => 'errorlog',
119 | 'level' => env('LOG_LEVEL', 'debug'),
120 | 'replace_placeholders' => true,
121 | ],
122 |
123 | 'null' => [
124 | 'driver' => 'monolog',
125 | 'handler' => NullHandler::class,
126 | ],
127 |
128 | 'emergency' => [
129 | 'path' => storage_path('logs/laravel.log'),
130 | ],
131 |
132 | ],
133 |
134 | ];
135 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/public/js/filament/tables/tables.js:
--------------------------------------------------------------------------------
1 | (()=>{var g=({canTrackDeselectedRecords:u,currentSelectionLivewireProperty:d,maxSelectableRecords:l,selectsCurrentPageOnly:r,$wire:s})=>({checkboxClickController:null,collapsedGroups:[],isLoading:!1,selectedRecords:new Set,deselectedRecords:new Set,isTrackingDeselectedRecords:!1,shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,entangledSelectedRecords:d?s.$entangle(d):null,init(){this.livewireId=this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value,s.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),s.$on("scrollToTopOfTable",()=>this.$root.scrollIntoView({block:"start",inline:"nearest"})),d&&(l!==1?this.selectedRecords=new Set(this.entangledSelectedRecords):this.selectedRecords=new Set(this.entangledSelectedRecords?[this.entangledSelectedRecords]:[])),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction(...e){s.set("isTrackingDeselectedTableRecords",this.isTrackingDeselectedRecords,!1),s.set("selectedTableRecords",[...this.selectedRecords],!1),s.set("deselectedTableRecords",[...this.deselectedRecords],!1),s.mountAction(...e)},toggleSelectRecordsOnPage(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecords(e){this.areRecordsSelected(e)?this.deselectRecords(e):this.selectRecords(e)},getSelectedRecordsCount(){return this.isTrackingDeselectedRecords?(this.$refs.allSelectableRecordsCount?.value??this.deselectedRecords.size)-this.deselectedRecords.size:this.selectedRecords.size},getRecordsOnPage(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords(e){l===1&&(this.deselectAllRecords(),e=e.slice(0,1));for(let t of e)if(!this.isRecordSelected(t)){if(this.isTrackingDeselectedRecords){this.deselectedRecords.delete(t);continue}this.selectedRecords.add(t)}this.updatedSelectedRecords()},deselectRecords(e){for(let t of e){if(this.isTrackingDeselectedRecords){this.deselectedRecords.add(t);continue}this.selectedRecords.delete(t)}this.updatedSelectedRecords()},updatedSelectedRecords(){if(l!==1){this.entangledSelectedRecords=[...this.selectedRecords];return}this.entangledSelectedRecords=[...this.selectedRecords][0]??null},toggleSelectedRecord(e){if(this.isRecordSelected(e)){this.deselectRecords([e]);return}this.selectRecords([e])},async selectAllRecords(){if(!u||r){this.isLoading=!0,this.selectedRecords=new Set(await s.getAllSelectableTableRecordKeys()),this.updatedSelectedRecords(),this.isLoading=!1;return}this.isTrackingDeselectedRecords=!0,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},canSelectAllRecords(){if(r){let i=this.getRecordsOnPage();return!this.areRecordsSelected(i)&&this.areRecordsToggleable(i)}let e=parseInt(this.$refs.allSelectableRecordsCount?.value);if(!e)return!1;let t=this.getSelectedRecordsCount();return e===t?!1:l===null||e<=l},deselectAllRecords(){this.isTrackingDeselectedRecords=!1,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},isRecordSelected(e){return this.isTrackingDeselectedRecords?!this.deselectedRecords.has(e):this.selectedRecords.has(e)},areRecordsSelected(e){return e.every(t=>this.isRecordSelected(t))},areRecordsToggleable(e){if(l===null||l===1)return!0;let t=e.filter(i=>this.isRecordSelected(i));return t.length===e.length?!0:this.getSelectedRecordsCount()+(e.length-t.length)<=l},toggleCollapseGroup(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups(){this.collapsedGroups=[]},watchForCheckboxClicks(){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(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let i=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!i.includes(this.lastChecked)){this.lastChecked=t;return}let o=i.indexOf(this.lastChecked),n=i.indexOf(t),a=[o,n].sort((c,R)=>c-R),h=[];for(let c=a[0];c<=a[1];c++)h.push(i[c].value);if(t.checked){if(!this.areRecordsToggleable(h)){t.checked=!1,this.deselectRecords([t.value]);return}this.selectRecords(h)}else this.deselectRecords(h)}this.lastChecked=t}});function f({columns:u,isLive:d}){return{error:void 0,isLoading:!1,columns:u,isLive:d,init(){if(!this.columns||this.columns.length===0){this.columns=[];return}},get groupedColumns(){let l={};return this.columns.filter(r=>r.type==="group").forEach(r=>{l[r.name]=this.calculateGroupedColumns(r)}),l},calculateGroupedColumns(l){if((l?.columns?.filter(i=>!i.isHidden)??[]).length===0)return{hidden:!0,checked:!1,disabled:!1,indeterminate:!1};let s=l.columns.filter(i=>!i.isHidden&&i.isToggleable!==!1);if(s.length===0)return{checked:!0,disabled:!0,indeterminate:!1};let e=s.filter(i=>i.isToggled).length,t=l.columns.filter(i=>!i.isHidden&&i.isToggleable===!1);return e===0&&t.length>0?{checked:!0,disabled:!1,indeterminate:!0}:e===0?{checked:!1,disabled:!1,indeterminate:!1}:e===s.length?{checked:!0,disabled:!1,indeterminate:!1}:{checked:!0,disabled:!1,indeterminate:!0}},getColumn(l,r=null){return r?this.columns.find(e=>e.type==="group"&&e.name===r)?.columns?.find(e=>e.name===l):this.columns.find(s=>s.name===l)},toggleGroup(l){let r=this.columns.find(o=>o.type==="group"&&o.name===l);if(!r?.columns)return;let s=this.calculateGroupedColumns(r);if(s.disabled)return;let t=r.columns.filter(o=>o.isToggleable!==!1).some(o=>o.isToggled),i=s.indeterminate?!0:!t;r.columns.filter(o=>o.isToggleable!==!1).forEach(o=>{o.isToggled=i}),this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager()},toggleColumn(l,r=null){let s=this.getColumn(l,r);!s||s.isToggleable===!1||(s.isToggled=!s.isToggled,this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager())},reorderColumns(l){let r=l.map(s=>s.split("::"));this.reorderTopLevel(r),this.isLive&&this.applyTableColumnManager()},reorderGroupColumns(l,r){let s=this.columns.find(i=>i.type==="group"&&i.name===r);if(!s)return;let e=l.map(i=>i.split("::")),t=[];e.forEach(([i,o])=>{let n=s.columns.find(a=>a.name===o);n&&t.push(n)}),s.columns=t,this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager()},reorderTopLevel(l){let r=this.columns,s=[];l.forEach(([e,t])=>{let i=r.find(o=>e==="group"?o.type==="group"&&o.name===t:e==="column"?o.type!=="group"&&o.name===t:!1);i&&s.push(i)}),this.columns=s},async applyTableColumnManager(){this.isLoading=!0;try{await this.$wire.call("applyTableColumnManager",this.columns),this.error=void 0}catch(l){this.error="Failed to update column visibility",console.error("Table toggle columns error:",l)}finally{this.isLoading=!1}}}}document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentTable",g),window.Alpine.data("filamentTableColumnManager",f)});})();
2 |
--------------------------------------------------------------------------------
/config/database.php:
--------------------------------------------------------------------------------
1 | env('DB_CONNECTION', 'sqlite'),
22 |
23 | /*
24 | |--------------------------------------------------------------------------
25 | | Database Connections
26 | |--------------------------------------------------------------------------
27 | |
28 | | Below are all of the database connections defined for your application.
29 | | An example configuration is provided for each database system which
30 | | is supported by Laravel. You're free to add / remove connections.
31 | |
32 | */
33 |
34 | 'connections' => [
35 |
36 | 'sqlite' => [
37 | 'driver' => 'sqlite',
38 | 'url' => env('DB_URL'),
39 | 'database' => env('DB_DATABASE', database_path('database.sqlite')),
40 | 'prefix' => '',
41 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
42 | 'busy_timeout' => null,
43 | 'journal_mode' => null,
44 | 'synchronous' => null,
45 | ],
46 |
47 | 'mysql' => [
48 | 'driver' => 'mysql',
49 | 'url' => env('DB_URL'),
50 | 'host' => env('DB_HOST', '127.0.0.1'),
51 | 'port' => env('DB_PORT', '3306'),
52 | 'database' => env('DB_DATABASE', 'laravel'),
53 | 'username' => env('DB_USERNAME', 'root'),
54 | 'password' => env('DB_PASSWORD', ''),
55 | 'unix_socket' => env('DB_SOCKET', ''),
56 | 'charset' => env('DB_CHARSET', 'utf8mb4'),
57 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
58 | 'prefix' => '',
59 | 'prefix_indexes' => true,
60 | 'strict' => true,
61 | 'engine' => null,
62 | 'options' => extension_loaded('pdo_mysql') ? array_filter([
63 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
64 | ]) : [],
65 | ],
66 |
67 | 'mariadb' => [
68 | 'driver' => 'mariadb',
69 | 'url' => env('DB_URL'),
70 | 'host' => env('DB_HOST', '127.0.0.1'),
71 | 'port' => env('DB_PORT', '3306'),
72 | 'database' => env('DB_DATABASE', 'laravel'),
73 | 'username' => env('DB_USERNAME', 'root'),
74 | 'password' => env('DB_PASSWORD', ''),
75 | 'unix_socket' => env('DB_SOCKET', ''),
76 | 'charset' => env('DB_CHARSET', 'utf8mb4'),
77 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
78 | 'prefix' => '',
79 | 'prefix_indexes' => true,
80 | 'strict' => true,
81 | 'engine' => null,
82 | 'options' => extension_loaded('pdo_mysql') ? array_filter([
83 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
84 | ]) : [],
85 | ],
86 |
87 | 'pgsql' => [
88 | 'driver' => 'pgsql',
89 | 'url' => env('DB_URL'),
90 | 'host' => env('DB_HOST', '127.0.0.1'),
91 | 'port' => env('DB_PORT', '5432'),
92 | 'database' => env('DB_DATABASE', 'laravel'),
93 | 'username' => env('DB_USERNAME', 'root'),
94 | 'password' => env('DB_PASSWORD', ''),
95 | 'charset' => env('DB_CHARSET', 'utf8'),
96 | 'prefix' => '',
97 | 'prefix_indexes' => true,
98 | 'search_path' => 'public',
99 | 'sslmode' => 'prefer',
100 | ],
101 |
102 | 'sqlsrv' => [
103 | 'driver' => 'sqlsrv',
104 | 'url' => env('DB_URL'),
105 | 'host' => env('DB_HOST', 'localhost'),
106 | 'port' => env('DB_PORT', '1433'),
107 | 'database' => env('DB_DATABASE', 'laravel'),
108 | 'username' => env('DB_USERNAME', 'root'),
109 | 'password' => env('DB_PASSWORD', ''),
110 | 'charset' => env('DB_CHARSET', 'utf8'),
111 | 'prefix' => '',
112 | 'prefix_indexes' => true,
113 | // 'encrypt' => env('DB_ENCRYPT', 'yes'),
114 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
115 | ],
116 |
117 | ],
118 |
119 | /*
120 | |--------------------------------------------------------------------------
121 | | Migration Repository Table
122 | |--------------------------------------------------------------------------
123 | |
124 | | This table keeps track of all the migrations that have already run for
125 | | your application. Using this information, we can determine which of
126 | | the migrations on disk haven't actually been run on the database.
127 | |
128 | */
129 |
130 | 'migrations' => [
131 | 'table' => 'migrations',
132 | 'update_date_on_publish' => true,
133 | ],
134 |
135 | /*
136 | |--------------------------------------------------------------------------
137 | | Redis Databases
138 | |--------------------------------------------------------------------------
139 | |
140 | | Redis is an open source, fast, and advanced key-value store that also
141 | | provides a richer body of commands than a typical key-value system
142 | | such as Memcached. You may define your connection settings here.
143 | |
144 | */
145 |
146 | 'redis' => [
147 |
148 | 'client' => env('REDIS_CLIENT', 'phpredis'),
149 |
150 | 'options' => [
151 | 'cluster' => env('REDIS_CLUSTER', 'redis'),
152 | 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
153 | 'persistent' => env('REDIS_PERSISTENT', false),
154 | ],
155 |
156 | 'default' => [
157 | 'url' => env('REDIS_URL'),
158 | 'host' => env('REDIS_HOST', '127.0.0.1'),
159 | 'username' => env('REDIS_USERNAME'),
160 | 'password' => env('REDIS_PASSWORD'),
161 | 'port' => env('REDIS_PORT', '6379'),
162 | 'database' => env('REDIS_DB', '0'),
163 | ],
164 |
165 | 'cache' => [
166 | 'url' => env('REDIS_URL'),
167 | 'host' => env('REDIS_HOST', '127.0.0.1'),
168 | 'username' => env('REDIS_USERNAME'),
169 | 'password' => env('REDIS_PASSWORD'),
170 | 'port' => env('REDIS_PORT', '6379'),
171 | 'database' => env('REDIS_CACHE_DB', '1'),
172 | ],
173 |
174 | ],
175 |
176 | ];
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Desafio Full Stack – 3Pontos Tech
2 |
3 | Bem-vindo(a) ao processo seletivo da 3Pontos Tech. Este desafio avalia o seu domínio das tecnologias listadas, sua
4 | capacidade de criar um código "limpo" e de comunicar decisões de forma clara.
5 |
6 | ## Projeto proposto
7 |
8 | Este projeto é um clone simplificado do Reddit, com foco em funcionalidades essenciais de comunidades, postagens,
9 | comentários e votos.
10 |
11 | A aplicação permite que usuários explorem subreddits (comunidades temáticas), publiquem conteúdo em Markdown, interajam
12 | por meio de comentários e realizem votos positivos ou negativos nos posts.
13 |
14 | O objetivo é replicar, de forma objetiva e enxuta, a estrutura básica de um fórum de discussão como o Reddit, servindo
15 | como base para estudos, testes técnicos ou evolução futura.
16 |
17 | | Item | Descrição |
18 | |---------------|----------------------------|
19 | | **Linguagem** | PHP 8.3^ |
20 | | **Framework** | Laravel 12 + FilamentPHP 4 |
21 | | **Database** | SQLite ou Postgres |
22 | | **Front-end** | Blade e Tailwind v4 |
23 |
24 | > [!NOTE]
25 | > Use **SQLite** para agilizar a configuração. Caso deseje, inclua instruções para trocar facilmente para **PostgreSQL**
26 | > ou **MySQL** com **Docker**.
27 |
28 | ## Critérios gerais de avaliação
29 |
30 | 1. **Modelagem de dados** – consistência, normalização e previsibilidade.
31 | 2. **Laravel / FilamentPHP** – uso adequado de recursos nativos (Eloquent, policies, actions, widgets, etc.).
32 | 3. **Design do Sistema** – Como deixar o sistema robusto.
33 | 4. **TailwindCSS** – domínio dos utilitários essenciais para layout e tipografia.
34 | 5. **Qualidade de código** – legibilidade, organização em camadas, nomenclatura coerente.
35 | 6. **Commits** – adoção de _Conventional Commits_ e histórico incremental.
36 | 7. **Documentação** – arquivo `DEVELOPMENT.md` escrito por você (sem geração automática por IA) contendo:
37 | - Visão geral da solução.
38 | - Justificativa das principais decisões técnicas.
39 | - Anotações do processo
40 | 8. **Capacidade analítica** – explicação dos trade-offs escolhidos.
41 | 9. **Painel Administrativo** - uso do FilamentPHP para gerenciar subreddits e posts.
42 |
43 |
44 | > [!WARNING]
45 | > Não nos importamos com o uso de I.A desde que você saiba justificar suas decisões. O principal é ter certeza da entrega feita e isso será questionado durante a avaliação.
46 |
47 |
48 | > [!WARNING]
49 | > Não será permitido uso de Plugins externos fora o `MediaLibrary` para agilizar a implementação de uma feature.
50 |
51 | ## Níveis e critérios de aceite
52 |
53 | No projeto disponível no [Figma](https://bit.ly/3pontos-fs-challenge-figma), você encontrará **versões da interface**, cada uma representando um **nível de
54 | complexidade** e refinamento visual correspondente a diferentes níveis de senioridade.
55 |
56 | > Senha do figma: 'pontopontoponto'
57 |
58 | Com base nessas telas, você deverá analisar a UI e modelar o sistema seguindo os critérios definidos abaixo:
59 |
60 | | Nível | Foco principal |
61 | |------------------|------------------------------------------------------------------------------------------------------------------------------------------|
62 | | **Básico** | Conseguir entregar as funcionalidades centrais usando recursos básicos de Laravel, Filament e Tailwind. |
63 | | **Intermedário** | Ênfase em modelagem de dados sólida, separação clara de camadas, e testes de unidade básicos. |
64 | | **Avançado** | Arquitetura escalável, uso avançado de Filament (Resources, Panels, Actions), políticas de segurança, testes abrangentes, CI/CD simples. |
65 |
66 | ## Desafio
67 |
68 | Estamos iniciando um novo projeto: um **fórum de perguntas e respostas inspirado no Reddit** baseado em
69 | subreddits/tópicos.
70 |
71 | > A criação desses subreddits deverá ser dinâmica usando o FilamentPHP.
72 |
73 | Todo o desenvolvimento será guiado pelo layout disponível no Figma, que apresenta três páginas principais, repetidas em
74 | diferentes estágios de evolução visual:
75 |
76 | - **Home/Dashboard**: exibe todas as postagens para usuários logados ou não logados.
77 | - **Subreddit**: página inicial de uma comunidade, listando todos os seus posts.
78 | - **Post**: visualização de um post específico pertencente a uma comunidade/produto.
79 |
80 | > Serão dois ambientes: admin (Filament) e front-end (Blade + Tailwind).
81 |
82 | Você deverá utilizar o layout como base para modelar os dados do projeto, utilizando sua análise visual e interpretação.
83 |
84 | > Você também poderá utilizar uma I.A para modelagem, porém caso use, deixe explicito no `DEVELOPMENT.md` quais foram suas decisões de aceite e o que foi feito por você.
85 |
86 | Cada etapa implementada deve ser versionada com commits no seguinte formato:
87 |
88 | ```
89 | git commit -m "feat(panel): sua-mensagem-de-preferencia-em-ingles"
90 | git commit -m "feat(component): sua-mensagem-de-preferencia-em-ingles"
91 | git commit -m "feat(anything): sua-mensagem-de-preferencia-em-ingles"
92 | ```
93 |
94 | > Caso algum ponto do projeto não esteja claro, registre suas suposições no final do `README.md`.
95 |
96 |
97 | ---
98 |
99 | ## Etapas do Processo
100 |
101 | ### 1. Submissão do Projeto
102 |
103 | 1. Crie um novo repositório utilizando este template (no topo da pagina, no botão verde "Use this template");
104 | 2. Crie uma branch `develop` e faça todo o desenvolvimento nela;
105 | 3. Abra um Pull Request para a branch `main` do **seu repositório pessoal**.
106 | 4. Envie um e-mail para `daniel@3pontos.com` com uma breve apresentação sobre você e o link do Pull Request.
107 | 5. Aguarde nossa resposta.
108 |
109 | > Data de entrega: **01/10/2025** às **13:00h**
110 | > Você pode acompanhar outras submissões e discussões sobre no [Discord da 3Pontos](https://bit.ly/3pontos-discord).
111 |
112 | ### 2. Entrevista Técnica
113 |
114 | Caso avance para a próxima fase, você participará de uma conversa com o time técnico. Vamos discutir suas decisões,
115 | entender seu raciocínio e trocar ideias de forma descontraída e técnica ao mesmo tempo.
116 |
117 | > Data das entrevistas: 02/10 ~ 03/10 na parte da tarde.
118 |
119 | ### 3. Conversa com a Liderança
120 |
121 | Neste momento, você terá um bate-papo com as lideranças da empresa para avaliarmos o alinhamento cultural. Por mais que
122 | pareça uma etapa “clichê”, ela é fundamental: buscamos pessoas que compartilhem dos nossos valores e estejam prontas
123 | para crescer com o time.
124 |
125 | ### 4. Carta Proposta
126 |
127 | Se tudo estiver alinhado, você receberá nossa proposta para integrar o time oficialmente e colar com a gente!
128 |
129 | ## Materiais de Suporte
130 |
131 | - [Filament Brasil](https://filament.com.br)
132 | - [Filament Docs](https://filament.com/docs)
133 | - [https://refactoring.guru/](https://refactoring.guru/)
134 | - [http://br.phptherightway.com/](http://br.phptherightway.com/)
135 | - [https://www.php-fig.org/psr/psr-12/](https://www.php-fig.org/psr/psr-12/)
136 |
137 | ## Agradecimentos
138 |
139 | - [@jeffersongoncalves](https://github.com/jeffersongoncalves) pelo setup inicial do Filament V4 Beta.
140 | - [@nexturhe4rt](https://github.com/nexturhe4rt) pelo UI feita.
141 | - [@gvieira18 - vulgo usb777](https://github.com/gvieira18) pelo setup inicial do projeto.
--------------------------------------------------------------------------------
/config/telescope.php:
--------------------------------------------------------------------------------
1 | env('TELESCOPE_ENABLED', false),
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Telescope Domain
43 | |--------------------------------------------------------------------------
44 | |
45 | | This is the subdomain where Telescope will be accessible from. If the
46 | | setting is null, Telescope will reside under the same domain as the
47 | | application. Otherwise, this value will be used as the subdomain.
48 | |
49 | */
50 |
51 | 'domain' => env('TELESCOPE_DOMAIN'),
52 |
53 | /*
54 | |--------------------------------------------------------------------------
55 | | Telescope Path
56 | |--------------------------------------------------------------------------
57 | |
58 | | This is the URI path where Telescope will be accessible from. Feel free
59 | | to change this path to anything you like. Note that the URI will not
60 | | affect the paths of its internal API that aren't exposed to users.
61 | |
62 | */
63 |
64 | 'path' => env('TELESCOPE_PATH', 'telescope'),
65 |
66 | /*
67 | |--------------------------------------------------------------------------
68 | | Telescope Storage Driver
69 | |--------------------------------------------------------------------------
70 | |
71 | | This configuration options determines the storage driver that will
72 | | be used to store Telescope's data. In addition, you may set any
73 | | custom options as needed by the particular driver you choose.
74 | |
75 | */
76 |
77 | 'driver' => env('TELESCOPE_DRIVER', 'database'),
78 |
79 | 'storage' => [
80 | 'database' => [
81 | 'connection' => env('DB_CONNECTION', 'mysql'),
82 | 'chunk' => 1000,
83 | ],
84 | ],
85 |
86 | /*
87 | |--------------------------------------------------------------------------
88 | | Telescope Queue
89 | |--------------------------------------------------------------------------
90 | |
91 | | This configuration options determines the queue connection and queue
92 | | which will be used to process ProcessPendingUpdate jobs. This can
93 | | be changed if you would prefer to use a non-default connection.
94 | |
95 | */
96 |
97 | 'queue' => [
98 | 'connection' => env('TELESCOPE_QUEUE_CONNECTION', null),
99 | 'queue' => env('TELESCOPE_QUEUE', null),
100 | 'delay' => env('TELESCOPE_QUEUE_DELAY', 10),
101 | ],
102 |
103 | /*
104 | |--------------------------------------------------------------------------
105 | | Telescope Route Middleware
106 | |--------------------------------------------------------------------------
107 | |
108 | | These middleware will be assigned to every Telescope route, giving you
109 | | the chance to add your own middleware to this list or change any of
110 | | the existing middleware. Or, you can simply stick with this list.
111 | |
112 | */
113 |
114 | 'middleware' => [
115 | 'web',
116 | Authorize::class,
117 | ],
118 |
119 | /*
120 | |--------------------------------------------------------------------------
121 | | Allowed / Ignored Paths & Commands
122 | |--------------------------------------------------------------------------
123 | |
124 | | The following array lists the URI paths and Artisan commands that will
125 | | not be watched by Telescope. In addition to this list, some Laravel
126 | | commands, like migrations and queue commands, are always ignored.
127 | |
128 | */
129 |
130 | 'only_paths' => [
131 | // 'api/*'
132 | ],
133 |
134 | 'ignore_paths' => [
135 | 'livewire*',
136 | 'nova-api*',
137 | 'pulse*',
138 | ],
139 |
140 | 'ignore_commands' => [
141 | //
142 | ],
143 |
144 | /*
145 | |--------------------------------------------------------------------------
146 | | Telescope Watchers
147 | |--------------------------------------------------------------------------
148 | |
149 | | The following array lists the "watchers" that will be registered with
150 | | Telescope. The watchers gather the application's profile data when
151 | | a request or task is executed. Feel free to customize this list.
152 | |
153 | */
154 |
155 | 'watchers' => [
156 | BatchWatcher::class => env('TELESCOPE_BATCH_WATCHER', true),
157 |
158 | CacheWatcher::class => [
159 | 'enabled' => env('TELESCOPE_CACHE_WATCHER', true),
160 | 'hidden' => [],
161 | 'ignore' => [],
162 | ],
163 |
164 | ClientRequestWatcher::class => env('TELESCOPE_CLIENT_REQUEST_WATCHER', true),
165 |
166 | CommandWatcher::class => [
167 | 'enabled' => env('TELESCOPE_COMMAND_WATCHER', true),
168 | 'ignore' => [],
169 | ],
170 |
171 | DumpWatcher::class => [
172 | 'enabled' => env('TELESCOPE_DUMP_WATCHER', true),
173 | 'always' => env('TELESCOPE_DUMP_WATCHER_ALWAYS', false),
174 | ],
175 |
176 | EventWatcher::class => [
177 | 'enabled' => env('TELESCOPE_EVENT_WATCHER', true),
178 | 'ignore' => [],
179 | ],
180 |
181 | ExceptionWatcher::class => env('TELESCOPE_EXCEPTION_WATCHER', true),
182 |
183 | GateWatcher::class => [
184 | 'enabled' => env('TELESCOPE_GATE_WATCHER', true),
185 | 'ignore_abilities' => [],
186 | 'ignore_packages' => true,
187 | 'ignore_paths' => [],
188 | ],
189 |
190 | JobWatcher::class => env('TELESCOPE_JOB_WATCHER', true),
191 |
192 | LogWatcher::class => [
193 | 'enabled' => env('TELESCOPE_LOG_WATCHER', true),
194 | 'level' => 'error',
195 | ],
196 |
197 | MailWatcher::class => env('TELESCOPE_MAIL_WATCHER', true),
198 |
199 | ModelWatcher::class => [
200 | 'enabled' => env('TELESCOPE_MODEL_WATCHER', true),
201 | 'events' => ['eloquent.*'],
202 | 'hydrations' => true,
203 | ],
204 |
205 | NotificationWatcher::class => env('TELESCOPE_NOTIFICATION_WATCHER', true),
206 |
207 | QueryWatcher::class => [
208 | 'enabled' => env('TELESCOPE_QUERY_WATCHER', true),
209 | 'ignore_packages' => true,
210 | 'ignore_paths' => [],
211 | 'slow' => 100,
212 | ],
213 |
214 | RedisWatcher::class => env('TELESCOPE_REDIS_WATCHER', true),
215 |
216 | RequestWatcher::class => [
217 | 'enabled' => env('TELESCOPE_REQUEST_WATCHER', true),
218 | 'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64),
219 | 'ignore_http_methods' => [],
220 | 'ignore_status_codes' => [],
221 | ],
222 |
223 | ScheduleWatcher::class => env('TELESCOPE_SCHEDULE_WATCHER', true),
224 | ViewWatcher::class => env('TELESCOPE_VIEW_WATCHER', true),
225 | ],
226 | ];
227 |
--------------------------------------------------------------------------------
/config/session.php:
--------------------------------------------------------------------------------
1 | env('SESSION_DRIVER', 'database'),
24 |
25 | /*
26 | |--------------------------------------------------------------------------
27 | | Session Lifetime
28 | |--------------------------------------------------------------------------
29 | |
30 | | Here you may specify the number of minutes that you wish the session
31 | | to be allowed to remain idle before it expires. If you want them
32 | | to expire immediately when the browser is closed then you may
33 | | indicate that via the expire_on_close configuration option.
34 | |
35 | */
36 |
37 | 'lifetime' => (int) env('SESSION_LIFETIME', 120),
38 |
39 | 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
40 |
41 | /*
42 | |--------------------------------------------------------------------------
43 | | Session Encryption
44 | |--------------------------------------------------------------------------
45 | |
46 | | This option allows you to easily specify that all of your session data
47 | | should be encrypted before it's stored. All encryption is performed
48 | | automatically by Laravel and you may use the session like normal.
49 | |
50 | */
51 |
52 | 'encrypt' => env('SESSION_ENCRYPT', false),
53 |
54 | /*
55 | |--------------------------------------------------------------------------
56 | | Session File Location
57 | |--------------------------------------------------------------------------
58 | |
59 | | When utilizing the "file" session driver, the session files are placed
60 | | on disk. The default storage location is defined here; however, you
61 | | are free to provide another location where they should be stored.
62 | |
63 | */
64 |
65 | 'files' => storage_path('framework/sessions'),
66 |
67 | /*
68 | |--------------------------------------------------------------------------
69 | | Session Database Connection
70 | |--------------------------------------------------------------------------
71 | |
72 | | When using the "database" or "redis" session drivers, you may specify a
73 | | connection that should be used to manage these sessions. This should
74 | | correspond to a connection in your database configuration options.
75 | |
76 | */
77 |
78 | 'connection' => env('SESSION_CONNECTION'),
79 |
80 | /*
81 | |--------------------------------------------------------------------------
82 | | Session Database Table
83 | |--------------------------------------------------------------------------
84 | |
85 | | When using the "database" session driver, you may specify the table to
86 | | be used to store sessions. Of course, a sensible default is defined
87 | | for you; however, you're welcome to change this to another table.
88 | |
89 | */
90 |
91 | 'table' => env('SESSION_TABLE', 'sessions'),
92 |
93 | /*
94 | |--------------------------------------------------------------------------
95 | | Session Cache Store
96 | |--------------------------------------------------------------------------
97 | |
98 | | When using one of the framework's cache driven session backends, you may
99 | | define the cache store which should be used to store the session data
100 | | between requests. This must match one of your defined cache stores.
101 | |
102 | | Affects: "dynamodb", "memcached", "redis"
103 | |
104 | */
105 |
106 | 'store' => env('SESSION_STORE'),
107 |
108 | /*
109 | |--------------------------------------------------------------------------
110 | | Session Sweeping Lottery
111 | |--------------------------------------------------------------------------
112 | |
113 | | Some session drivers must manually sweep their storage location to get
114 | | rid of old sessions from storage. Here are the chances that it will
115 | | happen on a given request. By default, the odds are 2 out of 100.
116 | |
117 | */
118 |
119 | 'lottery' => [2, 100],
120 |
121 | /*
122 | |--------------------------------------------------------------------------
123 | | Session Cookie Name
124 | |--------------------------------------------------------------------------
125 | |
126 | | Here you may change the name of the session cookie that is created by
127 | | the framework. Typically, you should not need to change this value
128 | | since doing so does not grant a meaningful security improvement.
129 | |
130 | */
131 |
132 | 'cookie' => env(
133 | 'SESSION_COOKIE',
134 | Str::snake((string) env('APP_NAME', 'laravel')).'_session'
135 | ),
136 |
137 | /*
138 | |--------------------------------------------------------------------------
139 | | Session Cookie Path
140 | |--------------------------------------------------------------------------
141 | |
142 | | The session cookie path determines the path for which the cookie will
143 | | be regarded as available. Typically, this will be the root path of
144 | | your application, but you're free to change this when necessary.
145 | |
146 | */
147 |
148 | 'path' => env('SESSION_PATH', '/'),
149 |
150 | /*
151 | |--------------------------------------------------------------------------
152 | | Session Cookie Domain
153 | |--------------------------------------------------------------------------
154 | |
155 | | This value determines the domain and subdomains the session cookie is
156 | | available to. By default, the cookie will be available to the root
157 | | domain and all subdomains. Typically, this shouldn't be changed.
158 | |
159 | */
160 |
161 | 'domain' => env('SESSION_DOMAIN'),
162 |
163 | /*
164 | |--------------------------------------------------------------------------
165 | | HTTPS Only Cookies
166 | |--------------------------------------------------------------------------
167 | |
168 | | By setting this option to true, session cookies will only be sent back
169 | | to the server if the browser has a HTTPS connection. This will keep
170 | | the cookie from being sent to you when it can't be done securely.
171 | |
172 | */
173 |
174 | 'secure' => env('SESSION_SECURE_COOKIE'),
175 |
176 | /*
177 | |--------------------------------------------------------------------------
178 | | HTTP Access Only
179 | |--------------------------------------------------------------------------
180 | |
181 | | Setting this value to true will prevent JavaScript from accessing the
182 | | value of the cookie and the cookie will only be accessible through
183 | | the HTTP protocol. It's unlikely you should disable this option.
184 | |
185 | */
186 |
187 | 'http_only' => env('SESSION_HTTP_ONLY', true),
188 |
189 | /*
190 | |--------------------------------------------------------------------------
191 | | Same-Site Cookies
192 | |--------------------------------------------------------------------------
193 | |
194 | | This option determines how your cookies behave when cross-site requests
195 | | take place, and can be used to mitigate CSRF attacks. By default, we
196 | | will set this value to "lax" to permit secure cross-site requests.
197 | |
198 | | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
199 | |
200 | | Supported: "lax", "strict", "none", null
201 | |
202 | */
203 |
204 | 'same_site' => env('SESSION_SAME_SITE', 'lax'),
205 |
206 | /*
207 | |--------------------------------------------------------------------------
208 | | Partitioned Cookies
209 | |--------------------------------------------------------------------------
210 | |
211 | | Setting this value to true will tie the cookie to the top-level site for
212 | | a cross-site context. Partitioned cookies are accepted by the browser
213 | | when flagged "secure" and the Same-Site attribute is set to "none".
214 | |
215 | */
216 |
217 | 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
218 |
219 | ];
220 |
--------------------------------------------------------------------------------
/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 ee=Object.create,q=Object.defineProperty,te=Object.getPrototypeOf,re=Object.prototype.hasOwnProperty,ne=Object.getOwnPropertyNames,ie=Object.getOwnPropertyDescriptor,se=r=>q(r,"__esModule",{value:!0}),ae=(r,n)=>()=>(n||(n={exports:{}},r(n.exports,n)),n.exports),oe=(r,n,s)=>{if(n&&typeof n=="object"||typeof n=="function")for(let l of ne(n))!re.call(r,l)&&l!=="default"&&q(r,l,{get:()=>n[l],enumerable:!(s=ie(n,l))||s.enumerable});return r},fe=r=>oe(se(q(r!=null?ee(te(r)):{},"default",r&&r.__esModule&&"default"in r?{get:()=>r.default,enumerable:!0}:{value:r,enumerable:!0})),r),le=ae((r,n)=>{(function(s,l,g){if(!s)return;for(var d={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"},w={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},b={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},x={option:"alt",command:"meta",return:"enter",escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},P,y=1;y<20;++y)d[111+y]="f"+y;for(y=0;y<=9;++y)d[y+96]=y.toString();function S(e,t,a){if(e.addEventListener){e.addEventListener(t,a,!1);return}e.attachEvent("on"+t,a)}function G(e){if(e.type=="keypress"){var t=String.fromCharCode(e.which);return e.shiftKey||(t=t.toLowerCase()),t}return d[e.which]?d[e.which]:w[e.which]?w[e.which]:String.fromCharCode(e.which).toLowerCase()}function N(e,t){return e.sort().join(",")===t.sort().join(",")}function V(e){var t=[];return e.shiftKey&&t.push("shift"),e.altKey&&t.push("alt"),e.ctrlKey&&t.push("ctrl"),e.metaKey&&t.push("meta"),t}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 J(){if(!P){P={};for(var e in d)e>95&&e<112||d.hasOwnProperty(e)&&(P[d[e]]=e)}return P}function B(e,t,a){return a||(a=J()[e]?"keydown":"keypress"),a=="keypress"&&t.length&&(a="keydown"),a}function X(e){return e==="+"?["+"]:(e=e.replace(/\+{2}/g,"+plus"),e.split("+"))}function T(e,t){var a,h,k,A=[];for(a=X(e),k=0;k1){Q(i,m,f,p);return}u=T(i,p),t._callbacks[u.key]=t._callbacks[u.key]||[],I(u.key,u.modifiers,{type:u.action},o,i,c),t._callbacks[u.key][o?"unshift":"push"]({callback:f,modifiers:u.modifiers,action:u.action,seq:o,level:c,combo:i})}t._bindMultiple=function(i,f,p){for(var o=0;o-1||U(t,a.target))return!1;if("composedPath"in e&&typeof e.composedPath=="function"){var h=e.composedPath()[0];h!==e.target&&(t=h)}return t.tagName=="INPUT"||t.tagName=="SELECT"||t.tagName=="TEXTAREA"||t.isContentEditable},v.prototype.handleKey=function(){var e=this;return e._handleKey.apply(e,arguments)},v.addKeycodes=function(e){for(var t in e)e.hasOwnProperty(t)&&(d[t]=e[t]);P=null},v.init=function(){var e=v(l);for(var t in e)t.charAt(0)!=="_"&&(v[t]=(function(a){return function(){return e[a].apply(e,arguments)}})(t))},v.init(),s.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)}),R=fe(le());(function(r){if(r){var n={},s=r.prototype.stopCallback;r.prototype.stopCallback=function(l,g,d,w){var b=this;return b.paused?!0:n[d]||n[w]?!1:s.call(b,l,g,d)},r.prototype.bindGlobal=function(l,g,d){var w=this;if(w.bind(l,g,d),l instanceof Array){for(var b=0;b{r.directive("mousetrap",(n,{modifiers:s,expression:l},{evaluate:g})=>{let d=()=>l?g(l):n.click();s=s.map(w=>w.replace(/--/g," ").replace(/-/g,"+").replace(/\bslash\b/g,"/")),s.includes("global")&&(s=s.filter(w=>w!=="global"),R.default.bindGlobal(s,w=>{w.preventDefault(),d()})),R.default.bind(s,w=>{w.preventDefault(),d()})})},W=ue;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 r=window.innerWidth;this.resizeObserver=new ResizeObserver(()=>{let n=window.innerWidth,s=r>=1024,l=n<1024,g=n>=1024;s&&l?(this.isOpenDesktop=this.isOpen,this.isOpen&&this.close()):!s&&g&&(this.isOpen=this.isOpenDesktop),r=n}),this.resizeObserver.observe(document.body),window.innerWidth<1024?this.isOpen&&(this.isOpenDesktop=!0,this.close()):this.isOpenDesktop=this.isOpen},groupIsCollapsed(r){return this.collapsedGroups.includes(r)},collapseGroup(r){this.collapsedGroups.includes(r)||(this.collapsedGroups=this.collapsedGroups.concat(r))},toggleCollapsedGroup(r){this.collapsedGroups=this.collapsedGroups.includes(r)?this.collapsedGroups.filter(n=>n!==r):this.collapsedGroups.concat(r)},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 r=localStorage.getItem("theme")??getComputedStyle(document.documentElement).getPropertyValue("--default-theme-mode");window.Alpine.store("theme",r==="dark"||r==="system"&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"),window.addEventListener("theme-changed",n=>{let s=n.detail;localStorage.setItem("theme",s),s==="system"&&(s=window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"),window.Alpine.store("theme",s)}),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")})});document.addEventListener("DOMContentLoaded",()=>{setTimeout(()=>{let r=document.querySelector(".fi-main-sidebar .fi-sidebar-item.fi-active");if((!r||r.offsetParent===null)&&(r=document.querySelector(".fi-main-sidebar .fi-sidebar-group.fi-active")),!r||r.offsetParent===null)return;let n=document.querySelector(".fi-main-sidebar .fi-sidebar-nav");n&&n.scrollTo(0,r.offsetTop-window.innerHeight/2)},10)});window.setUpUnsavedDataChangesAlert=({body:r,livewireComponent:n,$wire:s})=>{window.addEventListener("beforeunload",l=>{window.jsMd5(JSON.stringify(s.data).replace(/\\/g,""))===s.savedDataHash||s?.__instance?.effects?.redirect||(l.preventDefault(),l.returnValue=!0)})};window.setUpSpaModeUnsavedDataChangesAlert=({body:r,resolveLivewireComponentUsing:n,$wire:s})=>{let l=()=>s?.__instance?.effects?.redirect?!1:window.jsMd5(JSON.stringify(s.data).replace(/\\/g,""))!==s.savedDataHash,g=()=>confirm(r);document.addEventListener("livewire:navigate",d=>{if(typeof n()<"u"){if(!l()||g())return;d.preventDefault()}}),window.addEventListener("beforeunload",d=>{l()&&(d.preventDefault(),d.returnValue=!0)})};window.setUpUnsavedActionChangesAlert=({resolveLivewireComponentUsing:r,$wire:n})=>{window.addEventListener("beforeunload",s=>{if(!(typeof r()>"u")&&(n.mountedActions?.length??0)&&!n?.__instance?.effects?.redirect){s.preventDefault(),s.returnValue=!0;return}})};document.addEventListener("alpine:init",()=>{window.Alpine.plugin(W),window.Alpine.store("sidebar",j())});})();
2 |
--------------------------------------------------------------------------------
/config/debugbar.php:
--------------------------------------------------------------------------------
1 | env('DEBUGBAR_ENABLED', false),
20 | 'hide_empty_tabs' => true, // Hide tabs until they have content
21 | 'except' => [
22 | 'telescope*',
23 | 'horizon*',
24 | ],
25 |
26 | /*
27 | |--------------------------------------------------------------------------
28 | | Storage settings
29 | |--------------------------------------------------------------------------
30 | |
31 | | Debugbar stores data for session/ajax requests.
32 | | You can disable this, so the debugbar stores data in headers/session,
33 | | but this can cause problems with large data collectors.
34 | | By default, file storage (in the storage folder) is used. Redis and PDO
35 | | can also be used. For PDO, run the package migrations first.
36 | |
37 | | Warning: Enabling storage.open will allow everyone to access previous
38 | | request, do not enable open storage in publicly available environments!
39 | | Specify a callback if you want to limit based on IP or authentication.
40 | | Leaving it to null will allow localhost only.
41 | */
42 | 'storage' => [
43 | 'enabled' => true,
44 | 'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
45 | 'driver' => 'file', // redis, file, pdo, socket, custom
46 | 'path' => storage_path('debugbar'), // For file driver
47 | 'connection' => null, // Leave null for default connection (Redis/PDO)
48 | 'provider' => '', // Instance of StorageInterface for custom driver
49 | 'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver
50 | 'port' => 2304, // Port to use with the "socket" driver
51 | ],
52 |
53 | /*
54 | |--------------------------------------------------------------------------
55 | | Editor
56 | |--------------------------------------------------------------------------
57 | |
58 | | Choose your preferred editor to use when clicking file name.
59 | |
60 | | Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote",
61 | | "vscode-insiders-remote", "vscodium", "textmate", "emacs",
62 | | "sublime", "atom", "nova", "macvim", "idea", "netbeans",
63 | | "xdebug", "espresso"
64 | |
65 | */
66 |
67 | 'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
68 |
69 | /*
70 | |--------------------------------------------------------------------------
71 | | Remote Path Mapping
72 | |--------------------------------------------------------------------------
73 | |
74 | | If you are using a remote dev server, like Laravel Homestead, Docker, or
75 | | even a remote VPS, it will be necessary to specify your path mapping.
76 | |
77 | | Leaving one, or both of these, empty or null will not trigger the remote
78 | | URL changes and Debugbar will treat your editor links as local files.
79 | |
80 | | "remote_sites_path" is an absolute base path for your sites or projects
81 | | in Homestead, Vagrant, Docker, or another remote development server.
82 | |
83 | | Example value: "/home/vagrant/Code"
84 | |
85 | | "local_sites_path" is an absolute base path for your sites or projects
86 | | on your local computer where your IDE or code editor is running on.
87 | |
88 | | Example values: "/Users//Code", "C:\Users\\Documents\Code"
89 | |
90 | */
91 |
92 | 'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
93 | 'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
94 |
95 | /*
96 | |--------------------------------------------------------------------------
97 | | Vendors
98 | |--------------------------------------------------------------------------
99 | |
100 | | Vendor files are included by default, but can be set to false.
101 | | This can also be set to 'js' or 'css', to only include javascript or css vendor files.
102 | | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
103 | | and for js: jquery and highlight.js
104 | | So if you want syntax highlighting, set it to true.
105 | | jQuery is set to not conflict with existing jQuery scripts.
106 | |
107 | */
108 |
109 | 'include_vendors' => true,
110 |
111 | /*
112 | |--------------------------------------------------------------------------
113 | | Capture Ajax Requests
114 | |--------------------------------------------------------------------------
115 | |
116 | | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
117 | | you can use this option to disable sending the data through the headers.
118 | |
119 | | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
120 | |
121 | | Note for your request to be identified as ajax requests they must either send the header
122 | | X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
123 | |
124 | | By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
125 | | Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
126 | |
127 | | You can defer loading the dataset, so it will be loaded with ajax after the request is done. (Experimental)
128 | */
129 |
130 | 'capture_ajax' => true,
131 | 'add_ajax_timing' => false,
132 | 'ajax_handler_auto_show' => true,
133 | 'ajax_handler_enable_tab' => true,
134 | 'defer_datasets' => false,
135 | /*
136 | |--------------------------------------------------------------------------
137 | | Custom Error Handler for Deprecated warnings
138 | |--------------------------------------------------------------------------
139 | |
140 | | When enabled, the Debugbar shows deprecated warnings for Symfony components
141 | | in the Messages tab.
142 | |
143 | */
144 | 'error_handler' => false,
145 |
146 | /*
147 | |--------------------------------------------------------------------------
148 | | Clockwork integration
149 | |--------------------------------------------------------------------------
150 | |
151 | | The Debugbar can emulate the Clockwork headers, so you can use the Chrome
152 | | Extension, without the server-side code. It uses Debugbar collectors instead.
153 | |
154 | */
155 | 'clockwork' => false,
156 |
157 | /*
158 | |--------------------------------------------------------------------------
159 | | DataCollectors
160 | |--------------------------------------------------------------------------
161 | |
162 | | Enable/disable DataCollectors
163 | |
164 | */
165 |
166 | 'collectors' => [
167 | 'phpinfo' => false, // Php version
168 | 'messages' => true, // Messages
169 | 'time' => true, // Time Datalogger
170 | 'memory' => true, // Memory usage
171 | 'exceptions' => true, // Exception displayer
172 | 'log' => true, // Logs from Monolog (merged in messages if enabled)
173 | 'db' => true, // Show database (PDO) queries and bindings
174 | 'views' => true, // Views with their data
175 | 'route' => false, // Current route information
176 | 'auth' => false, // Display Laravel authentication status
177 | 'gate' => true, // Display Laravel Gate checks
178 | 'session' => false, // Display session data
179 | 'symfony_request' => true, // Only one can be enabled..
180 | 'mail' => true, // Catch mail messages
181 | 'laravel' => true, // Laravel version and environment
182 | 'events' => false, // All events fired
183 | 'default_request' => false, // Regular or special Symfony request logger
184 | 'logs' => false, // Add the latest log messages
185 | 'files' => false, // Show the included files
186 | 'config' => false, // Display config settings
187 | 'cache' => false, // Display cache events
188 | 'models' => true, // Display models
189 | 'livewire' => true, // Display Livewire (when available)
190 | 'jobs' => false, // Display dispatched jobs
191 | 'pennant' => false, // Display Pennant feature flags
192 | ],
193 |
194 | /*
195 | |--------------------------------------------------------------------------
196 | | Extra options
197 | |--------------------------------------------------------------------------
198 | |
199 | | Configure some DataCollectors
200 | |
201 | */
202 |
203 | 'options' => [
204 | 'time' => [
205 | 'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate
206 | ],
207 | 'messages' => [
208 | 'trace' => true, // Trace the origin of the debug message
209 | 'capture_dumps' => true, // Capture laravel `dump();` as message
210 | ],
211 | 'memory' => [
212 | 'reset_peak' => false, // run memory_reset_peak_usage before collecting
213 | 'with_baseline' => false, // Set boot memory usage as memory peak baseline
214 | 'precision' => 0, // Memory rounding precision
215 | ],
216 | 'auth' => [
217 | 'show_name' => true, // Also show the users name/email in the debugbar
218 | 'show_guards' => true, // Show the guards that are used
219 | ],
220 | 'db' => [
221 | 'with_params' => true, // Render SQL with the parameters substituted
222 | 'exclude_paths' => [ // Paths to exclude entirely from the collector
223 | // 'vendor/laravel/framework/src/Illuminate/Session', // Exclude sessions queries
224 | ],
225 | 'backtrace' => true, // Use a backtrace to find the origin of the query in your files.
226 | 'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
227 | 'timeline' => false, // Add the queries to the timeline
228 | 'duration_background' => true, // Show shaded background on each query relative to how long it took to execute.
229 | 'explain' => [ // Show EXPLAIN output on queries
230 | 'enabled' => false,
231 | ],
232 | 'hints' => false, // Show hints for common mistakes
233 | 'show_copy' => true, // Show copy button next to the query,
234 | 'slow_threshold' => false, // Only track queries that last longer than this time in ms
235 | 'memory_usage' => false, // Show queries memory usage
236 | 'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured
237 | 'hard_limit' => 500, // After the hard limit, queries are ignored
238 | ],
239 | 'mail' => [
240 | 'timeline' => true, // Add mails to the timeline
241 | 'show_body' => true,
242 | ],
243 | 'views' => [
244 | 'timeline' => true, // Add the views to the timeline
245 | 'data' => false, // True for all data, 'keys' for only names, false for no parameters.
246 | 'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
247 | 'exclude_paths' => [ // Add the paths which you don't want to appear in the views
248 | 'vendor/filament', // Exclude Filament components by default
249 | ],
250 | ],
251 | 'route' => [
252 | 'label' => true, // Show complete route on bar
253 | ],
254 | 'session' => [
255 | 'hiddens' => [], // Hides sensitive values using array paths
256 | ],
257 | 'symfony_request' => [
258 | 'label' => true, // Show route on bar
259 | 'hiddens' => [], // Hides sensitive values using array paths, example: request_request.password
260 | ],
261 | 'events' => [
262 | 'data' => false, // Collect events data, listeners
263 | ],
264 | 'logs' => [
265 | 'file' => null,
266 | ],
267 | 'cache' => [
268 | 'values' => true, // Collect cache values
269 | ],
270 | ],
271 |
272 | /*
273 | |--------------------------------------------------------------------------
274 | | Inject Debugbar in Response
275 | |--------------------------------------------------------------------------
276 | |
277 | | Usually, the debugbar is added just before