├── .editorconfig ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug.yml │ └── feature_request.md ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── dependabot-auto-merge.yml │ ├── fix-php-code-style-issues.yml │ ├── phpstan.yml │ ├── run-tests.yml │ └── update-changelog.yml ├── .gitignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── config └── zeus-bolt.php ├── database ├── factories │ ├── CategoryFactory.php │ ├── CollectionFactory.php │ ├── FieldFactory.php │ ├── FieldResponseFactory.php │ ├── FormFactory.php │ ├── ResponseFactory.php │ ├── SectionFactory.php │ └── UserFactory.php ├── migrations │ ├── add_borderless_to_section.php.stub │ ├── add_compact_to_section.php.stub │ ├── add_extension_item_responses.php.stub │ ├── add_extensions_to_forms.php.stub │ ├── add_grade_to_field_response.php.stub │ ├── add_grade_to_response.php.stub │ ├── add_options_to_section.php.stub │ ├── alter_tables_constraints.php.stub │ ├── create_categories_table.php.stub │ ├── create_collections_table.php.stub │ ├── create_field_responses_table.php.stub │ ├── create_fields_table.php.stub │ ├── create_forms_table.php.stub │ ├── create_responses_table.php.stub │ └── create_sections_table.php.stub └── seeders │ └── BoltSeeder.php ├── docs ├── _index.md ├── advanced │ ├── _index.md │ ├── add-datasource.md │ ├── add-fields.md │ ├── custom-designer.md │ ├── custom-schemata.md │ ├── events.md │ ├── extension.md │ └── render-hooks.md ├── bolt-pro │ ├── _index.md │ ├── advanced-tables.md │ ├── assets.md │ ├── fields.md │ ├── introduction.md │ ├── presets.md │ └── share-form.md ├── filament-bolt-pro.md ├── filament.md ├── getting-started │ ├── _index.md │ ├── assets.md │ ├── changelog.md │ ├── configuration.md │ ├── installation.md │ ├── loading.md │ └── upgrade.md ├── introduction.md └── usage │ ├── _index.md │ ├── embed.md │ └── overview.md ├── phpstan.neon ├── phpunit.xml.dist ├── pint.json ├── resources ├── lang │ ├── ar.json │ ├── ckb.json │ ├── de.json │ ├── en.json │ ├── es.json │ └── fr.json └── views │ ├── emails │ └── form-submission.blade.php │ ├── errors │ ├── date-not-available.blade.php │ ├── login-required.blade.php │ └── one-entry-per-user.blade.php │ ├── filament │ ├── components │ │ └── color-picker.blade.php │ ├── fields │ │ ├── file-upload.blade.php │ │ └── types.blade.php │ ├── pages │ │ └── reports │ │ │ └── entries.blade.php │ └── resources │ │ ├── form-resource │ │ └── widgets │ │ │ └── edit-collection-warning.blade.php │ │ └── response-resource │ │ ├── components │ │ └── view-responses.blade.php │ │ └── pages │ │ ├── browse-entry.blade.php │ │ ├── browse-responses.blade.php │ │ └── show-entry.blade.php │ └── themes │ └── zeus │ └── bolt │ ├── fill-forms.blade.php │ ├── list-entries.blade.php │ ├── list-forms.blade.php │ ├── loading.blade.php │ ├── show-entry.blade.php │ └── submitted.blade.php ├── routes └── web.php ├── src ├── BoltPlugin.php ├── BoltServiceProvider.php ├── Commands │ ├── InstallCommand.php │ ├── PublishCommand.php │ ├── ZeusDatasourceCommand.php │ └── ZeusFieldCommand.php ├── Concerns │ ├── CanManipulateFiles.php │ ├── HasActive.php │ ├── HasHiddenOptions.php │ ├── HasOptions.php │ ├── HasUpdates.php │ └── Schemata.php ├── Configuration.php ├── Contracts │ ├── CustomFormSchema.php │ ├── CustomSchema.php │ ├── DataSource.php │ ├── Extension.php │ └── Fields.php ├── DataSources │ └── DataSourceContract.php ├── Events │ ├── FormMounted.php │ └── FormSent.php ├── Facades │ ├── Bolt.php │ ├── Collectors.php │ ├── Designer.php │ └── Extensions.php ├── Fields │ ├── Classes │ │ ├── CheckboxList.php │ │ ├── ColorPicker.php │ │ ├── DatePicker.php │ │ ├── DateTimePicker.php │ │ ├── FileUpload.php │ │ ├── Paragraph.php │ │ ├── Radio.php │ │ ├── RichEditor.php │ │ ├── Select.php │ │ ├── TextInput.php │ │ ├── Textarea.php │ │ ├── TimePicker.php │ │ └── Toggle.php │ └── FieldsContract.php ├── Filament │ ├── Actions │ │ ├── ReplicateFormAction.php │ │ └── SetResponseStatus.php │ ├── Exports │ │ └── ResponseExporter.php │ └── Resources │ │ ├── BoltResource.php │ │ ├── CategoryResource.php │ │ ├── CategoryResource │ │ └── Pages │ │ │ ├── CreateCategory.php │ │ │ ├── EditCategory.php │ │ │ └── ListCategories.php │ │ ├── CollectionResource.php │ │ ├── CollectionResource │ │ ├── Pages │ │ │ ├── CreateCollection.php │ │ │ ├── EditCollection.php │ │ │ └── ListCollections.php │ │ └── Widgets │ │ │ └── EditCollectionWarning.php │ │ ├── FormResource.php │ │ └── FormResource │ │ ├── Pages │ │ ├── BrowseResponses.php │ │ ├── CreateForm.php │ │ ├── EditForm.php │ │ ├── ListForms.php │ │ ├── ManageResponses.php │ │ ├── ViewForm.php │ │ └── ViewResponse.php │ │ └── Widgets │ │ ├── FormOverview.php │ │ ├── ResponsesPerFields.php │ │ ├── ResponsesPerMonth.php │ │ └── ResponsesPerStatus.php ├── Livewire │ ├── FillForms.php │ ├── ListEntries.php │ ├── ListForms.php │ └── ShowEntry.php ├── Mail │ └── FormSubmission.php └── Models │ ├── Category.php │ ├── Collection.php │ ├── Concerns │ └── BelongToBolt.php │ ├── Field.php │ ├── FieldResponse.php │ ├── Form.php │ ├── FormsStatus.php │ ├── Response.php │ └── Section.php ├── stubs ├── ZeusDataSources.stub └── ZeusField.stub └── tests ├── AdminPanelProvider.php ├── ArchTest.php ├── FormsTest.php ├── Models └── User.php ├── Pest.php ├── ResourcesTest.php ├── TestCase.php └── migrations ├── 001_create_categories_table.php ├── 002_create_collections_table.php ├── 003_create_forms_table.php ├── 004_create_sections_table.php ├── 005_create_fields_table.php ├── 006_create_responses_table.php └── 007_create_field_responses_table.php /.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 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code 10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be 11 | extremely unfair for them to suffer abuse or anger for their hard work. 12 | 13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the 14 | world that developers are civilized and selfless people. 15 | 16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient 17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 18 | 19 | ## Viability 20 | 21 | When requesting or submitting new features, first consider whether it might be useful to others. Open 22 | source projects are used by many developers, who may have entirely different needs to your own. Think about 23 | whether or not your feature is likely to be used by other users of the project. 24 | 25 | ## Procedure 26 | 27 | Before filing an issue: 28 | 29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 30 | - Check to make sure your feature suggestion isn't already present within the project. 31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 32 | - Check the pull requests tab to ensure that the feature isn't already in progress. 33 | 34 | Before submitting a pull request: 35 | 36 | - Check the codebase to ensure that your feature doesn't already exist. 37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 38 | 39 | ## Requirements 40 | 41 | If the project maintainer has any additional requirements, you will find them listed here. 42 | 43 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). 44 | 45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 46 | 47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 48 | 49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 50 | 51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 52 | 53 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: atmonshi 2 | custom: "https://www.buymeacoffee.com/larazeus" 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report an Issue or Bug with the Package 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | We're sorry to hear you have a problem. Can you help us solve it by providing the following details. 10 | - type: textarea 11 | id: what-happened 12 | attributes: 13 | label: What happened? 14 | description: What did you expect to happen? 15 | placeholder: I cannot currently do X thing because when I do, it breaks X thing. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: how-to-reproduce 20 | attributes: 21 | label: How to reproduce the bug 22 | description: How did this occur, please add any config values used and provide a set of reliable steps if possible. 23 | placeholder: When I do X I see Y. 24 | validations: 25 | required: true 26 | - type: input 27 | id: package-version 28 | attributes: 29 | label: Package Version 30 | description: What version of our Package are you running? Please be as specific as possible 31 | placeholder: 2.0.0 32 | validations: 33 | required: true 34 | - type: input 35 | id: php-version 36 | attributes: 37 | label: PHP Version 38 | description: What version of PHP are you running? Please be as specific as possible 39 | placeholder: 8.2.0 40 | validations: 41 | required: true 42 | - type: input 43 | id: laravel-version 44 | attributes: 45 | label: Laravel Version 46 | description: What version of Laravel are you running? Please be as specific as possible 47 | placeholder: 9.0.0 48 | validations: 49 | required: true 50 | - type: dropdown 51 | id: operating-systems 52 | attributes: 53 | label: Which operating systems does with happen with? 54 | description: You may select more than one. 55 | multiple: true 56 | options: 57 | - macOS 58 | - Windows 59 | - Linux 60 | - type: textarea 61 | id: notes 62 | attributes: 63 | label: Notes 64 | description: Use this field to provide any other notes that you feel might be relevant to the issue. 65 | validations: 66 | required: false 67 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you discover any security related issues, please email info@larazeus.com instead of using the issue tracker. 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | labels: 12 | - "dependencies" -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: dependabot-auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | 14 | - name: Dependabot metadata 15 | id: metadata 16 | uses: dependabot/fetch-metadata@v2.4.0 17 | with: 18 | github-token: "${{ secrets.GITHUB_TOKEN }}" 19 | 20 | - name: Auto-merge Dependabot PRs for semver-minor updates 21 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} 22 | run: gh pr merge --auto --merge "$PR_URL" 23 | env: 24 | PR_URL: ${{github.event.pull_request.html_url}} 25 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 26 | 27 | - name: Auto-merge Dependabot PRs for semver-patch updates 28 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 29 | run: gh pr merge --auto --merge "$PR_URL" 30 | env: 31 | PR_URL: ${{github.event.pull_request.html_url}} 32 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /.github/workflows/fix-php-code-style-issues.yml: -------------------------------------------------------------------------------- 1 | name: code style 2 | 3 | on: [push] 4 | 5 | jobs: 6 | php-code-styling: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | with: 13 | ref: ${{ github.head_ref }} 14 | 15 | - name: Fix PHP code style issues 16 | uses: aglipanci/laravel-pint-action@2.5 17 | 18 | - name: Commit changes 19 | uses: stefanzweifel/git-auto-commit-action@v5 20 | with: 21 | commit_message: Fix styling 22 | -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: PHPStan 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.php' 7 | - 'phpstan.neon.dist' 8 | 9 | jobs: 10 | phpstan: 11 | name: phpstan 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup PHP 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: '8.3' 20 | coverage: none 21 | 22 | - name: Install composer dependencies 23 | uses: ramsey/composer-install@v3 24 | 25 | - name: Run PHPStan 26 | run: ./vendor/bin/phpstan --error-format=github -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | branches: [3.x] 6 | pull_request: 7 | branches: [3.x] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | os: [ubuntu-latest, windows-latest] 16 | php: [8.2] 17 | laravel: [12.*] 18 | stability: [prefer-stable] 19 | include: 20 | - laravel: 12.* 21 | testbench: 10.* 22 | 23 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} 24 | 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v4 28 | 29 | - name: Setup PHP 30 | uses: shivammathur/setup-php@v2 31 | with: 32 | php-version: ${{ matrix.php }} 33 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 34 | coverage: none 35 | 36 | - name: Setup problem matchers 37 | run: | 38 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 39 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 40 | 41 | - name: Install dependencies 42 | run: | 43 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 44 | composer update --${{ matrix.stability }} --prefer-dist --no-interaction 45 | 46 | - name: Execute tests 47 | run: vendor/bin/pest -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | name: "Update Changelog" 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | update: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v4 14 | with: 15 | ref: 3.x 16 | 17 | - name: Update Changelog 18 | uses: stefanzweifel/changelog-updater-action@v1 19 | with: 20 | latest-version: ${{ github.event.release.name }} 21 | release-notes: ${{ github.event.release.body }} 22 | 23 | - name: Commit updated CHANGELOG 24 | uses: stefanzweifel/git-auto-commit-action@v5 25 | with: 26 | branch: 3.x 27 | commit_message: Update CHANGELOG 28 | file_pattern: CHANGELOG.md 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /storage/*.key 5 | /vendor 6 | /vendor/.idea 7 | /bootstrap/cache 8 | .env 9 | .env.backup 10 | .phpunit.result.cache 11 | docker-compose.override.yml 12 | Homestead.json 13 | Homestead.yaml 14 | npm-debug.log 15 | yarn-error.log 16 | /.idea 17 | /.vscode 18 | /pkg 19 | /build 20 | .DS_Store 21 | /.phpunit.cache 22 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Lara Zeus (Ash) 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. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lara-zeus/bolt", 3 | "description": "Zeus Bolt is form builder for your users, with so many use cases", 4 | "keywords": [ 5 | "laravel", 6 | "lara-zeus", 7 | "bolt", 8 | "form", 9 | "forms", 10 | "generator", 11 | "self hosted", 12 | "maker", 13 | "builder", 14 | "filamentphp" 15 | ], 16 | "homepage": "https://larazeus.com/bolt", 17 | "support": { 18 | "issues": "https://github.com/lara-zeus/bolt/issues", 19 | "source": "https://github.com/lara-zeus/bolt" 20 | }, 21 | "license": "MIT", 22 | "type": "library", 23 | "authors": [ 24 | { 25 | "name": "Lara Zeus (Ash)", 26 | "email": "info@larazeus.com" 27 | } 28 | ], 29 | "require": { 30 | "php": "^8.1", 31 | "flowframe/laravel-trend": "^0.4", 32 | "guava/filament-icon-picker": "^2.3", 33 | "lara-zeus/accordion": "^1.1", 34 | "lara-zeus/core": "^3.2", 35 | "lara-zeus/filament-plugin-tools": "^1.0", 36 | "lara-zeus/list-group": "^1.0", 37 | "pxlrbt/filament-excel": "^2.4", 38 | "secondnetwork/blade-tabler-icons": "^3.30" 39 | }, 40 | "require-dev": { 41 | "phpstan/extension-installer": "^1.4", 42 | "larastan/larastan": "^3.0", 43 | "laravel/pint": "^1.0", 44 | "nunomaduro/collision": "^8.6", 45 | "nunomaduro/phpinsights": "^2.8", 46 | "orchestra/testbench": "^10.0", 47 | "pestphp/pest": "^3.0", 48 | "pestphp/pest-plugin-arch": "^3.0", 49 | "pestphp/pest-plugin-laravel": "^3.0", 50 | "pestphp/pest-plugin-livewire": "^3.0" 51 | }, 52 | "autoload": { 53 | "psr-4": { 54 | "LaraZeus\\Bolt\\": "src", 55 | "LaraZeus\\Bolt\\Database\\Factories\\": "database/factories/" 56 | } 57 | }, 58 | "autoload-dev": { 59 | "psr-4": { 60 | "LaraZeus\\Bolt\\Tests\\": "tests" 61 | } 62 | }, 63 | "scripts": { 64 | "analyse": "vendor/bin/phpstan analyse", 65 | "test": "vendor/bin/pest", 66 | "test-coverage": "vendor/bin/pest --coverage", 67 | "format": "vendor/bin/pint", 68 | "pint": "vendor/bin/pint", 69 | "test:pest": "vendor/bin/pest --parallel", 70 | "test:phpstan": "vendor/bin/phpstan analyse" 71 | }, 72 | "config": { 73 | "sort-packages": true, 74 | "allow-plugins": { 75 | "pestphp/pest-plugin": true, 76 | "phpstan/extension-installer": true, 77 | "dealerdirect/phpcodesniffer-composer-installer": true 78 | } 79 | }, 80 | "extra": { 81 | "laravel": { 82 | "providers": [ 83 | "LaraZeus\\Bolt\\BoltServiceProvider" 84 | ], 85 | "aliases": { 86 | "Bolt": "LaraZeus\\Bolt\\Facades\\Bolt" 87 | } 88 | } 89 | }, 90 | "prefer-stable": true 91 | } 92 | -------------------------------------------------------------------------------- /config/zeus-bolt.php: -------------------------------------------------------------------------------- 1 | null, 8 | 9 | /** 10 | * set the default path for the forms homepage. 11 | */ 12 | 'prefix' => 'bolt', 13 | 14 | /* 15 | * set database table prefix 16 | */ 17 | 'table-prefix' => 'bolt_', 18 | 19 | /** 20 | * the middleware you want to apply on all the blog routes 21 | * for example if you want to make your blog for users only, add the middleware 'auth'. 22 | */ 23 | 'middleware' => ['web'], 24 | 25 | /** 26 | * you can overwrite any model and use your own 27 | * you can also configure the model per panel in your panel provider using: 28 | * ->skyModels([ ... ]) 29 | */ 30 | 'models' => [ 31 | 'Category' => \LaraZeus\Bolt\Models\Category::class, 32 | 'Collection' => \LaraZeus\Bolt\Models\Collection::class, 33 | 'Field' => \LaraZeus\Bolt\Models\Field::class, 34 | 'FieldResponse' => \LaraZeus\Bolt\Models\FieldResponse::class, 35 | 'Form' => \LaraZeus\Bolt\Models\Form::class, 36 | 'FormsStatus' => \LaraZeus\Bolt\Models\FormsStatus::class, 37 | 'Response' => \LaraZeus\Bolt\Models\Response::class, 38 | 'Section' => \LaraZeus\Bolt\Models\Section::class, 39 | 'User' => config('auth.providers.users.model'), 40 | ], 41 | 42 | 'collectors' => [ 43 | 'fields' => [ 44 | 'path' => 'app/Zeus/Fields', 45 | 'namespace' => '\\App\\Zeus\\Fields\\', 46 | ], 47 | 48 | 'dataSources' => [ 49 | 'path' => 'app/Zeus/DataSources', 50 | 'namespace' => 'App\\Zeus\\DataSources\\', 51 | ], 52 | ], 53 | 54 | 'defaultMailable' => \LaraZeus\Bolt\Mail\FormSubmission::class, 55 | 56 | 'uploadDisk' => env('BOLT_FILESYSTEM_DISK', 'public'), 57 | 58 | 'uploadDirectory' => env('BOLT_FILESYSTEM_DIRECTORY', 'forms'), 59 | 60 | 'uploadVisibility' => env('BOLT_FILESYSTEM_VISIBILITY', 'public'), 61 | 62 | /* 63 | * if you have installed Bolt Pro, you can enable the presets here 64 | */ 65 | 'show_presets' => false, 66 | 67 | /** 68 | * the preset comes with a demo forms: 69 | * a Contact form and Ticket support form. 70 | * if you dont want them, feel free to set this to false 71 | * */ 72 | 'show_core_presets' => true, 73 | 74 | /** 75 | * the preview for the presets is using sushi: 76 | * you can enable/disable the cache here 77 | * */ 78 | 'should_cache_preset' => env('BOLT_CACHE_PRESET', true), 79 | 80 | /* 81 | * if you have installed Bolt Pro, you can enable the form design option here 82 | */ 83 | 'allow_design' => false, 84 | 85 | /** 86 | * since `collections` or 'data sources' have many types, we cannot lazy load them 87 | * but we cache them for a while to get better performance 88 | * the key is: dataSource_*_response_md5 89 | * 90 | * here you can set the duration of the cache 91 | */ 92 | 'cache' => [ 93 | 'collection_values' => 30, // on seconds 94 | ], 95 | ]; 96 | -------------------------------------------------------------------------------- /database/factories/CategoryFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->words(3, true), 16 | 'ordering' => $this->faker->numberBetween(1, 10), 17 | 'is_active' => 1, 18 | 'description' => $this->faker->words(5, true), 19 | 'slug' => $this->faker->slug, 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /database/factories/CollectionFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->words(3, true), 16 | 'user_id' => config('auth.providers.users.model')::factory(), 17 | 'values' => 'abc', 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /database/factories/FieldFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->words(3, true), 17 | 'type' => '\LaraZeus\Bolt\Fields\Classes\TextInput', 18 | 'section_id' => BoltPlugin::getModel('Section')::factory(), 19 | 'ordering' => $this->faker->numberBetween(1, 20), 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /database/factories/FieldResponseFactory.php: -------------------------------------------------------------------------------- 1 | BoltPlugin::getModel('Forms')::factory(), 17 | 'field_id' => BoltPlugin::getModel('Field')::factory(), 18 | 'response_id' => BoltPlugin::getModel('Response')::factory(), 19 | 'response' => $this->faker->words(3, true), 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /database/factories/FormFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->words(3, true), 17 | 'user_id' => 1, 18 | 'ordering' => $this->faker->numberBetween(1, 20), 19 | 'description' => $this->faker->text(), 20 | 'slug' => $this->faker->slug(), 21 | 'is_active' => 1, 22 | 'category_id' => BoltPlugin::getModel('Category')::factory(), 23 | 'start_date' => $this->faker->dateTime(), 24 | 'end_date' => $this->faker->dateTime(), 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/factories/ResponseFactory.php: -------------------------------------------------------------------------------- 1 | BoltPlugin::getModel('Form')::factory(), 17 | 'status' => 'NEW', 18 | 'user_id' => 1, 19 | 'notes' => $this->faker->text(), 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /database/factories/SectionFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->words(3, true), 17 | 'form_id' => BoltPlugin::getModel('Form')::factory(), 18 | 'ordering' => $this->faker->numberBetween(1, 10), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | fake()->name(), 17 | 'email' => fake()->unique()->safeEmail(), 18 | 'email_verified_at' => now(), 19 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 20 | 'remember_token' => Str::random(10), 21 | ]; 22 | } 23 | 24 | /** 25 | * Indicate that the model's email address should be unverified. 26 | */ 27 | public function unverified(): static 28 | { 29 | return $this->state(fn (array $attributes) => [ 30 | 'email_verified_at' => null, 31 | ]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/add_borderless_to_section.php.stub: -------------------------------------------------------------------------------- 1 | boolean('borderless')->default(0); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table(config('zeus-bolt.table-prefix').'sections', function (Blueprint $table) { 29 | $table->dropColumn('borderless'); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/add_compact_to_section.php.stub: -------------------------------------------------------------------------------- 1 | boolean('compact')->default(0); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table(config('zeus-bolt.table-prefix').'sections', function (Blueprint $table) { 29 | $table->dropColumn('compact'); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/add_extension_item_responses.php.stub: -------------------------------------------------------------------------------- 1 | integer('extension_item_id')->nullable(); 15 | }); 16 | } 17 | 18 | /** 19 | * Reverse the migrations. 20 | */ 21 | public function down(): void 22 | { 23 | Schema::table(config('zeus-bolt.table-prefix').'responses', function (Blueprint $table) { 24 | $table->dropColumn('extension_item_id'); 25 | }); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /database/migrations/add_extensions_to_forms.php.stub: -------------------------------------------------------------------------------- 1 | text('extensions')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table(config('zeus-bolt.table-prefix').'forms', function (Blueprint $table) { 29 | $table->dropColumn('extensions'); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/add_grade_to_field_response.php.stub: -------------------------------------------------------------------------------- 1 | integer('grade')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table(config('zeus-bolt.table-prefix').'field_responses', function (Blueprint $table) { 29 | $table->dropColumn('grade'); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/add_grade_to_response.php.stub: -------------------------------------------------------------------------------- 1 | integer('grades')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table(config('zeus-bolt.table-prefix').'responses', function (Blueprint $table) { 29 | $table->dropColumn('grades'); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/add_options_to_section.php.stub: -------------------------------------------------------------------------------- 1 | text('options')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table(config('zeus-bolt.table-prefix').'sections', function (Blueprint $table) { 29 | $table->dropColumn('options'); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/alter_tables_constraints.php.stub: -------------------------------------------------------------------------------- 1 | dropForeign(['user_id']); 18 | $table->dropForeign(['category_id']); 19 | 20 | $table->unsignedBigInteger('user_id')->nullable()->change(); 21 | Schema::enableForeignKeyConstraints(); 22 | 23 | $table->foreign('user_id') 24 | ->onUpdate('cascade') 25 | ->nullOnDelete() 26 | ->references('id') 27 | ->on('users'); 28 | 29 | $table->foreign('category_id') 30 | ->onUpdate('cascade') 31 | ->nullOnDelete() 32 | ->references('id') 33 | ->on(config('zeus-bolt.table-prefix').'categories'); 34 | }); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function down() 43 | { 44 | 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /database/migrations/create_categories_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->text('name'); 19 | $table->integer('ordering')->default(1); 20 | $table->boolean('is_active')->default(1); 21 | $table->text('description')->nullable(); 22 | $table->string('slug'); 23 | $table->string('logo')->nullable(); 24 | $table->timestamps(); 25 | $table->softDeletes(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists(config('zeus-bolt.table-prefix').'categories'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /database/migrations/create_collections_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->text('name'); 19 | $table->longText('values')->nullable(); 20 | $table->timestamps(); 21 | $table->softDeletes(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists(config('zeus-bolt.table-prefix').'collections'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/create_field_responses_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('form_id')->constrained(config('zeus-bolt.table-prefix').'forms'); 19 | $table->foreignId('field_id')->constrained(config('zeus-bolt.table-prefix').'fields'); 20 | $table->foreignId('response_id')->constrained(config('zeus-bolt.table-prefix').'responses'); 21 | $table->longText('response'); 22 | $table->timestamps(); 23 | $table->softDeletes(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists(config('zeus-bolt.table-prefix').'field_responses'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/create_fields_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('section_id')->constrained(config('zeus-bolt.table-prefix').'sections'); 19 | $table->text('name'); 20 | $table->text('description')->nullable(); 21 | $table->string('type'); 22 | $table->integer('ordering')->default(1); 23 | $table->text('options')->nullable(); 24 | $table->timestamps(); 25 | $table->softDeletes(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists(config('zeus-bolt.table-prefix').'fields'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /database/migrations/create_forms_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained('users'); 19 | $table->foreignId('category_id')->nullable()->constrained(config('zeus-bolt.table-prefix').'categories'); 20 | $table->text('name'); 21 | $table->text('description')->nullable(); 22 | $table->string('slug'); 23 | $table->integer('ordering')->default(1); 24 | $table->boolean('is_active'); 25 | $table->longText('details')->nullable(); 26 | $table->longText('options')->nullable(); 27 | $table->dateTime('start_date')->nullable(); 28 | $table->dateTime('end_date')->nullable(); 29 | $table->timestamps(); 30 | $table->softDeletes(); 31 | }); 32 | } 33 | 34 | /** 35 | * Reverse the migrations. 36 | * 37 | * @return void 38 | */ 39 | public function down() 40 | { 41 | Schema::dropIfExists(config('zeus-bolt.table-prefix').'forms'); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /database/migrations/create_responses_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('form_id')->constrained(config('zeus-bolt.table-prefix').'forms'); 19 | $table->foreignId('user_id')->nullable()->constrained('users'); 20 | $table->string('status')->default('NEW'); 21 | $table->text('notes')->nullable(); 22 | $table->timestamps(); 23 | $table->softDeletes(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists(config('zeus-bolt.table-prefix').'responses'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/create_sections_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('form_id')->constrained(config('zeus-bolt.table-prefix').'forms'); 19 | $table->text('name')->nullable(); 20 | $table->integer('ordering')->default(1); 21 | $table->integer('columns')->default(1); 22 | $table->text('description')->nullable(); 23 | $table->string('icon')->nullable(); 24 | $table->boolean('aside')->default(0); 25 | $table->timestamps(); 26 | $table->softDeletes(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists(config('zeus-bolt.table-prefix').'sections'); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /docs/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: v3 3 | slogan: form builder for your users, with so many use cases. 4 | githubUrl: https://github.com/lara-zeus/bolt 5 | branch: main 6 | icon: akar-thunder 7 | --- 8 | -------------------------------------------------------------------------------- /docs/advanced/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced 3 | weight: 3 4 | --- 5 | -------------------------------------------------------------------------------- /docs/advanced/add-datasource.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Datasource 3 | weight: 5 4 | --- 5 | 6 | ## Create Custom Datasource 7 | 8 | Create a custom data source for your forms and make it available as a collection. 9 | Datasources are helpful for creating custom collections from different models or an external API. 10 | 11 | To create datasource, use the following command, passing the name of the datasource: 12 | 13 | ```bash 14 | php artisan make:zeus-datasource Car 15 | ``` 16 | 17 | ## Caching 18 | 19 | Bolt will automatically list the data sources from your app in the form builder as a collection. 20 | There is a cache for all collections, so remember to flush the key `bolt.datasources` 21 | 22 | ## Customization 23 | Check out the contract `LaraZeus\Bolt\DataSources\DataSourceContract` and see all the available methods. 24 | 25 | ### Disabling 26 | 27 | You can turn off any field temporally by adding: 28 | ```php 29 | public bool $disabled = true; 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/advanced/add-fields.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Fields 3 | weight: 4 4 | --- 5 | 6 | ## Create Custom Fields 7 | 8 | Add any custom fields you want that is available on the [filament core](https://filamentphp.com/docs/3.x/forms/fields/getting-started) or [filament plugins](https://filamentphp.com/plugins). 9 | 10 | For example, we want to allow our users to use icon picker in the forms. 11 | 12 | ### First, install the package: 13 | 14 | ```bash 15 | composer require guava/filament-icon-picker 16 | ``` 17 | 18 | ### Create bolt field 19 | 20 | Create the field using the following command, passing the fully qualified name of the form component: 21 | 22 | ```bash 23 | php artisan make:zeus-field \\Guava\\FilamentIconPicker\\Forms\\IconPicker 24 | ``` 25 | 26 | ## Caching 27 | 28 | Bolt will automatically list the field in the form builder. 29 | There is a cache for all fields, so remember to flush the key `bolt.fields` 30 | 31 | ## Customization 32 | 33 | Check out the contract `LaraZeus\Bolt\Fields\FieldsContract` and see all the available methods. 34 | 35 | ### Disabling 36 | 37 | You can turn off any field temporally by adding: 38 | ```php 39 | public bool $disabled = true; 40 | ``` 41 | 42 | ### Field Title 43 | 44 | ```php 45 | public function title(): string 46 | { 47 | return __(class_basename($this)); 48 | } 49 | ``` 50 | 51 | ### Field Options 52 | 53 | You can add any options to be shown on the admin page when creating the form 54 | 55 | ```php 56 | public static function getOptions(): array 57 | { 58 | return [ 59 | Toggle::make('options.is_required')->label(__('Is Required')), 60 | ]; 61 | } 62 | ``` 63 | 64 | And to apply these options to the field in the frontend: 65 | 66 | ```php 67 | public function appendFilamentComponentsOptions($component, $zeusField) 68 | { 69 | parent::appendFilamentComponentsOptions($component, $zeusField); 70 | 71 | if (isset($zeusField->options['is_required']) && $zeusField->options['is_required']) { 72 | $component = $component->required(); 73 | } 74 | } 75 | ``` 76 | 77 | ### Disable the options tab 78 | 79 | If your field doesn't have any options, you can turn off the options tab by removing the method `getOptions` or returning false: 80 | 81 | ```php 82 | public function hasOptions(): bool 83 | { 84 | return false; 85 | } 86 | ``` 87 | 88 | ### View the Response 89 | 90 | You can control how to view the response on the entries pages 91 | 92 | ```php 93 | public function getResponse($field, $resp): string 94 | { 95 | return $resp->response; 96 | } 97 | ``` 98 | -------------------------------------------------------------------------------- /docs/advanced/custom-designer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Designer 3 | weight: 7 4 | --- 5 | 6 | ## Use Custom Designer 7 | 8 | The class `Designer` is the one responsible for presenting the form in the frontend, and now you can customize it to your liking. 9 | 10 | > **Note**\ 11 | > This is an advanced feature; please use it only when necessary since you have to mainline it manually with every update for Bolt. 12 | 13 | ### First, copy the class to your app 14 | 15 | Copy the class from `\LaraZeus\Bolt\Facades` to your app, lets say: `\App\Zeus\Bolt\Classes` 16 | 17 | ### Call the class in a service provider 18 | 19 | In your register method of your `AppServiceProvider` add the following: 20 | 21 | ```php 22 | \LaraZeus\Bolt\Livewire\FillForms::getBoltFormDesignerUsing(\App\Zeus\Bolt\Concerns\Designer::class); 23 | ``` 24 | 25 | You're done. Customize the form builder to fit your needs. Remember to keep an eye on any changes in future updates so that you avoid breaking changes. 26 | -------------------------------------------------------------------------------- /docs/advanced/custom-schemata.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Schemata 3 | weight: 6 4 | --- 5 | 6 | ## Add Custom Schema for fields 7 | 8 | Bolt allows you to add custom components (additional metadata) to the form, sections, and fields. 9 | 10 | ### First: create the class 11 | 12 | Create a class wherever you want in your app for example in `App\Zeus\CustomSchema` with the content: 13 | 14 | ```php 15 | schema([ 31 | TextInput::make('options.meta.data_binding') 32 | ->label('Data Binding'), 33 | ]); 34 | } 35 | 36 | public function hidden(?FieldsContract $field = null): array 37 | { 38 | return [ 39 | Hidden::make('options.meta.data_binding'), 40 | ]; 41 | } 42 | } 43 | ``` 44 | 45 | - Make sure to return the hidden fields the same as the fields you have defined in the `make` method 46 | - Make sure to set the `state` correctly, if you want to store this info in the `options` then use `options. more data, or in a separate column then make sure to create the migration for it 47 | 48 | ### Second: add it to your panel config 49 | 50 | ```php 51 | BoltPlugin::make() 52 | ->customSchema([ 53 | 'form' => null, 54 | 'section' => null, 55 | 'field' => \App\Zeus\CustomSchema\Field::class, 56 | ]) 57 | ``` 58 | 59 | ### Third: catch it on the `FormSent` event, for example 60 | 61 | ```php 62 | $event->response->fieldsResponses->each(function($fieldResponse){ 63 | CustomeModel::create([ 64 | 'column' => $fieldResponse->field->options['meta']['data_binding'] 65 | ]); 66 | }); 67 | ``` 68 | 69 | >**Warning: This is a simplified example, so don't trust your user input explicitly. That could open some pretty serious security holes.** 70 | 71 | ## Replace the Schemata with Custom one 72 | 73 | The trait `Schemata` is the heart of the form builder, and now you can customize it to your liking. 74 | 75 | > **Note**\ 76 | > This is an advanced feature; please use it only when necessary since you have to mainline it manually with every update for Bolt. 77 | 78 | ### First, copy the trait to your app 79 | 80 | Copy the trait from `\LaraZeus\Bolt\Concerns` to your app, lets say: `\App\Zeus\Bolt\Concerns` 81 | 82 | ### Call the trait in a service provider 83 | 84 | In your register method of your `AppServiceProvider` add the following: 85 | 86 | ```php 87 | \LaraZeus\Bolt\Filament\Resources\FormResource::getBoltFormSchemaUsing(fn(): array => \App\Zeus\Bolt\Facades\Designer::getMainFormSchema()); 88 | ``` 89 | 90 | You're done. Customize the form builder to fit your needs. Remember to keep an eye on any changes in future updates so that you avoid breaking changes. -------------------------------------------------------------------------------- /docs/advanced/events.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Events 3 | weight: 1 4 | --- 5 | 6 | ## Available Events 7 | 8 | Bolt will fire these events: 9 | - `LaraZeus\Bolt\Events\FormMounted` 10 | - `LaraZeus\Bolt\Events\FormSent` 11 | 12 | ## Register a Listener: 13 | * First, create your listener: 14 | ```bash 15 | php artisan make:listener SendNotification --event=FormMounted 16 | ``` 17 | 18 | * Second, register the listener in your `EventServiceProvider` 19 | 20 | ```php 21 | protected $listen = [ 22 | //... 23 | LetterSent::class => [ 24 | SendNotification::class, 25 | ], 26 | ]; 27 | ``` 28 | 29 | * Finally, you can receive the form object in the `handle` method and do whatever you want. 30 | For example: 31 | 32 | ```php 33 | Mail::to(User::first())->send(new \App\Mail\Contact( 34 | $event->form->name, $event->letter->email, $event->letter->message 35 | )); 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/advanced/extension.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Extensions 3 | weight: 3 4 | --- 5 | 6 | ## Extensions 7 | 8 | Bolt offer a simple way to build your own application around the forms, with a simple interface, called `extensions` : 9 | 10 | Extensions are hooks based classes that let you perform your logic around the forms or catch the submission and do more integrations with other apps or external APIs. 11 | for example before showing the form, or after storing the data etc... 12 | 13 | ## Available Hooks: 14 | 15 | - `canView` 16 | before displaying the form, you can do some checks 17 | 18 | - `render` 19 | what to show at the beginning of the form, you can return a view to show more info or instructors while filling out the form 20 | 21 | -`formComponents` 22 | return an array of filament components to add them to the form in the frontend 23 | 24 | - `store` 25 | the store logic for the extension, insert to any DB or external API 26 | 27 | - `postStore` 28 | this is typically used for sending only. It will be executed after saving the form 29 | 30 | - `submittedRender` 31 | this will show any info after saving the form, like a request number or more buttons and links 32 | 33 | 34 | ## Creating an Extension 35 | 36 | Create a class in your app with the following content: 37 | 38 | >I will create a command later :) 39 | 40 | ```php 41 | 42 | $data['order_number'], 83 | // ... 84 | ]);*/ 85 | 86 | // return these data to recive them after the form submitted 87 | // $data['model'] = $model; 88 | 89 | return $data; 90 | } 91 | 92 | public function postStore(Form $form, array $data): void 93 | { 94 | // send emails 95 | // fire some events 96 | } 97 | 98 | public function SubmittedRender(Form $form, array $data): string|null 99 | { 100 | // return view()->with('data', $data); 101 | } 102 | } 103 | 104 | ``` 105 | 106 | ## Enabling The Extension 107 | 108 | In your `zeus-bolt` config file, add your extension to the array: 109 | 110 | ```php 111 | 'extensions' => [ 112 | \App\Zeus\Extensions\Items::class, 113 | ], 114 | ``` 115 | 116 | Now when creating or editing a form, you will see the tab Extensions, and you can select any extension per form. 117 | -------------------------------------------------------------------------------- /docs/advanced/render-hooks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Render hooks 3 | weight: 2 4 | --- 5 | 6 | ## Render Hooks 7 | 8 | Filament provides an elegant way to allow you to customize the UI without having to publish any views by using the `renderHook.` 9 | 10 | Bolt also utilizes these hooks to make it easier to customize any views. 11 | 12 | ### available hooks 13 | 14 | - `Zeus-forms.before` 15 | - will be rendered on the `bolt/` page before listing all categories and forms 16 | 17 | - `Zeus-forms.after` 18 | - will be rendered on the `bolt/` page after listing all categories and forms 19 | 20 | - `Zeus-form.before` 21 | - will be rendered above all forms before any other content like the `details.` 22 | 23 | - `Zeus-form.after` 24 | - this hook will be rendered after all forms 25 | 26 | - `Zeus-form-section.before` 27 | - before rendering any section in all forms 28 | 29 | - `Zeus-form-section.after` 30 | - after rendering any section in all forms 31 | 32 | - `Zeus-form-field.before` 33 | - before rendering any field in all forms 34 | 35 | - `Zeus-form-field.after` 36 | - after rendering any field in all forms 37 | 38 | 39 | ### Usage 40 | 41 | You can define your hooks in your service provider: 42 | 43 | ```php 44 | Filament::registerRenderHook( 45 | 'Zeus-form.before', 46 | fn (): View => view('filament.hooks.zeus.form-before'), 47 | ); 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/bolt-pro/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bolt Pro 3 | weight: 4 4 | --- 5 | -------------------------------------------------------------------------------- /docs/bolt-pro/advanced-tables.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Integrated with Advanced Tables 3 | weight: 6 4 | --- 5 | 6 | The Advanced Tables (formerly Filter Sets) plugin from [Kenneth Sese](https://filamentphp.com/plugins/kenneth-sese-advanced-tables), to Supercharge your tables with powerful features like user customizable views, enhanced filter tabs, reorderable columns, convenient view management, and more. Now fully integrated to create powerful reports from your dynamic forms. 7 | 8 | You will need a separate license for the Advanced Tables plugin to activate these features. 9 | 10 | ## Enable the Advanced Tables Plugin 11 | 12 | After installing Advanced Tables, the the filters will be available in forms immediately but you need to enable it in the Entries Report page: 13 | 14 | Create the file 15 | `resources/views/vendor/zeus/filament/pages/reports/entries-pro.blade.php` 16 | or copy it from the vendor folder 17 | and add the content: 18 | 19 | ```html 20 | 21 | 22 | {{ $this->table }} 23 | 24 | ``` -------------------------------------------------------------------------------- /docs/bolt-pro/assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Themes and Assets 3 | weight: 2 4 | --- 5 | 6 | ## Compiling assets 7 | 8 | We use [tailwind Css](https://tailwindcss.com/) and custom themes by filament, make sure you are familiar with [tailwindcss configuration](https://tailwindcss.com/docs/configuration), and how to make a custom [filament theme](https://filamentphp.com/docs/2.x/admin/appearance#building-themes). 9 | 10 | ### Custom Classes 11 | 12 | You need to add these files to your `tailwind.config.js` file in the `content` section. 13 | 14 | * frontend: 15 | 16 | ```js 17 | content: [ 18 | //... 19 | './vendor/lara-zeus/bolt-pro/resources/views/themes/**/*.blade.php', 20 | ] 21 | ``` 22 | 23 | * filament: 24 | 25 | ```js 26 | content: [ 27 | //... 28 | './vendor/lara-zeus/bolt-pro/resources/views/filament/**/*.blade.php', 29 | ] 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/bolt-pro/fields.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Available Fields 3 | weight: 3 4 | --- 5 | 6 | ## Available Fields 7 | 8 | - 🔥 Rating - Show Star Rating as field 9 | - 🔥 Matrix Grid - Multiple choice grid field as radio or checkboxes 10 | - 🔥 Slider - A range selector, set the min and max value 11 | - 🔥 Advance Date - Set the date picker to range, month, week, and multiple dates 12 | - 🔥 Alert - Show a note with customized styling 13 | - 🔥 Icon Picker - Allows your users to pick an icon 14 | - 🔥 Image Picker - Allows users to pick an image from uploaded set 15 | - 🔥 Dynamic Textbox - Add multiple text values 16 | - 🔥 Signature - Collect users signature 17 | - 🔥 Terms and conditions - Let the users agree to your terms and conditions 18 | -------------------------------------------------------------------------------- /docs/bolt-pro/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | weight: 1 4 | --- 5 | 6 | ## Zeus Bolt Pro 7 | 8 | Advanced Forms management, More Filtering, Presets, and New Fields for Bolt the form builder 9 | 10 | ## Features 11 | 12 | - New Fields Types 13 | - Advanced widgets and stats 14 | - Forms API 15 | - Custom colors and branding per form 16 | - Presets: create Forms with pre defined templates, or from existing forms 17 | - Prefilled Forms URLs 18 | - Advanced Widgets 19 | - Sharing and embedding the form 20 | 21 | ### Get Bolt Pro from [here](https://larazeus.com/bolt-pro) 22 | 23 | ## Installation 24 | 25 | To install bolt, you only need to require it in your composer by running the command: 26 | 27 | ```bash 28 | composer require lara-zeus/bolt-pro 29 | ``` 30 | 31 | Make sure to clear the cache after the installation completed. 32 | 33 | And that is all :). 34 | 35 | You will get more details after you purchasing the package. 36 | 37 | ## Configuration 38 | 39 | Add these configuration keys to `zeus-bolt` config file: 40 | 41 | ```php 42 | // if you want to disable the preset button 43 | 'allow_design' => false, 44 | 45 | // to disable the theming tab 46 | 'show_presets' => false, 47 | 48 | // to disable the core presets 49 | 'show_core_presets' => false, 50 | 51 | /** 52 | * the preview for the presets is using sushi: 53 | * you can enable/disable the cache here 54 | */ 55 | 'should_cache_preset' => env('BOLT_CACHE_PRESET', true), 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/bolt-pro/presets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Presets Forms 3 | weight: 4 4 | --- 5 | 6 | # Presets Forms: 7 | 8 | Create Forms with pre defined templates, build your form manually or use an existing form. 9 | Available Preset: Support Ticket, Contact Form. 10 | 11 | ## Create a new preset 12 | 13 | Use the artisan command to create new preset: 14 | 15 | `make:zeus-preset` 16 | 17 | This will prompts you to enter the preset class name without any namespace 18 | or the form ID from you existing forms. 19 | 20 | All new generated presets will be saved in the folder: `App\Zeus\Presets` 21 | 22 | -------------------------------------------------------------------------------- /docs/bolt-pro/share-form.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sharing & Embedding 3 | weight: 5 4 | --- 5 | 6 | # Sharing a Form: 7 | 8 | Bolt Pro will provide your users with a new tab, that let them share the form to social media and other webpages very easily. 9 | 10 | To customize the social media sharing icons or use any other widget, create the file: 11 | `resources/views/vendor/zeus/filament/social-share.blade.php`. 12 | 13 | And add any social platform you want. 14 | 15 | ## Embedding a Form 16 | 17 | The embed forms will be accessible from the route name `bolt.form.embed` and 18 | the url is; `/bolt/embed/$form->slug` 19 | 20 | To customize the layout you can create the file: 21 | `resources/views/vendor/zeus/filament/embed-layout.blade.php` 22 | 23 | The default content is: 24 | 25 | ```html 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | @livewireStyles 43 | @filamentStyles 44 | @stack('styles') 45 | 46 | 47 | 48 | 52 | 53 | 54 | 55 |
56 | {{ $slot }} 57 |
58 | 59 | @stack('scripts') 60 | @livewireScripts 61 | @filamentScripts 62 | @livewire('notifications') 63 | 64 | 71 | 72 | 73 | 74 | ``` -------------------------------------------------------------------------------- /docs/filament-bolt-pro.md: -------------------------------------------------------------------------------- 1 | ## Zeus Bolt Pro 2 | 3 | Advanced Forms management, More Filtering, Presets, and New Fields for [Bolt the form builder](https://filamentphp.com/plugins/lara-zeus-bolt) 4 | 5 | ## Available Fields 6 | 7 | - 🔥 Rating. Show Star Rating as field 8 | - 🔥 Matrix Grid. multiple choice grid field as radio or checkboxes 9 | - 🔥 Slider. a range selector, set the min and max value 10 | - 🔥 Advance Date. set the date picker to range, month, week, and multiple dates 11 | - 🔥 Alert. show a note with customized styling 12 | - 🔥 Icon Picker. allows your users to pick an icon 13 | - 🔥 Image Picker. allows users to pick an image from uploaded set 14 | - 🔥 Dynamic Textbox. add multiple text values 15 | - 🔥 Signature. collect users signature 16 | - 🔥 terms and conditions. let the users agree to your terms and conditions 17 | 18 | ## And Even More Features 19 | 20 | * Advanced widgets and stats 21 | * Forms API 22 | * Presets: create Forms with pre defined templates 23 | * Custom colors and branding per form 24 | * Prefilled Forms URLs 25 | * Advanced Widgets 26 | * Sharing and embedding the form 27 | 28 | ## Fully Integrated with Advanced Tables. 29 | 30 | [The Advanced Tables (formerly Filter Sets) plugin](https://filamentphp.com/plugins/kenneth-sese-advanced-tables) from Kenneth Sese, to Supercharge your tables with powerful features like user customizable views, enhanced filter tabs, reorderable columns, convenient view management, and more. now, fully integrated to create powerful reports from your dynamic forms. 31 | 32 | You will need a separate license for the Advanced Tables plugin to activate these features. 33 | 34 | ## Screenshots 35 | 36 | ![](https://larazeus.com/images/screenshots/bolt-pro/screen-3.webp) 37 | ![](https://larazeus.com/images/screenshots/bolt-pro/screen-1.webp) 38 | ![](https://larazeus.com/images/screenshots/bolt-pro/screen-2.webp) 39 | ![](https://larazeus.com/images/screenshots/bolt-pro/pre-01.webp) 40 | ![](https://larazeus.com/images/screenshots/bolt-pro/pre-02.webp) 41 | ![](https://larazeus.com/images/screenshots/bolt-pro/pro-01.webp) 42 | 43 | ## More Details 44 | 45 | **✨ to learn more about Bolt Pro, please visit:** 46 | 47 | - [Discord](https://discord.com/channels/883083792112300104/1282748203376050309) 48 | - [Docs](https://larazeus.com/docs/bolt/v2/bolt-pro) 49 | - [Demo](https://demo.larazeus.com/admin/forms/bolt-pro) 50 | 51 | -------------------------------------------------------------------------------- /docs/filament.md: -------------------------------------------------------------------------------- 1 | # Zeus Bolt 2 | 3 | Bolt is a form builder for your users, with so many use cases included a UI for the frontend built with filament 4 | 5 | ## Features 6 | 7 | - 🔥 Collections. 8 | - 🔥 Categories. 9 | - 🔥 Entries report and Filters. 10 | - 🔥 Custom Datasource. 11 | - 🔥 Custom Fields. 12 | - 🔥 Extensions. 13 | - 🔥 Conditional Visibility. 14 | - 🔥 Events. 15 | - 🔥 View the forms as livewire component. 16 | - 🔥 integrated with Sky. 17 | - 🔥 Frontend scaffolding, highly customizable. 18 | 19 | ## Screenshots 20 | 21 | * **UI Form Manager:** beautifully designed form builder, data collections, and Categories 22 | 23 | ![](https://larazeus.com/images/screenshots/bolt/admin-1.webp) 24 | ![](https://larazeus.com/images/screenshots/bolt/admin-2.webp) 25 | ![](https://larazeus.com/images/screenshots/bolt/admin-3.webp) 26 | ![](https://larazeus.com/images/screenshots/bolt/admin-9.webp) 27 | ![](https://larazeus.com/images/screenshots/bolt/admin-10.webp) 28 | 29 | * **Submission Manager:** Manage your forms entries with ease and set status, fully customizable, with reports and advanced filters. 30 | 31 | ![](https://larazeus.com/images/screenshots/bolt/admin-5.webp) 32 | ![](https://larazeus.com/images/screenshots/bolt/admin-6.webp) 33 | ![](https://larazeus.com/images/screenshots/bolt/admin-7.webp) 34 | ![](https://larazeus.com/images/screenshots/bolt/admin-8.webp) 35 | 36 | * **Frontend:** frontend scaffolding built with filament. Including pages to list all user entries for your clients 37 | 38 | ![](https://larazeus.com/images/screenshots/bolt/frontend-1.webp) 39 | ![](https://larazeus.com/images/screenshots/bolt/frontend-2.webp) 40 | ![](https://larazeus.com/images/screenshots/bolt/frontend-3.webp) 41 | ![](https://larazeus.com/images/screenshots/bolt/frontend-4.webp) 42 | ![](https://larazeus.com/images/screenshots/bolt/frontend-5.webp) 43 | 44 | ## Bolt Pro 45 | 46 | [![bolt-pro](https://larazeus.com/images/bolt-pro-ad.webp)](https://filamentphp.com/plugins/lara-zeus-bolt-pro) 47 | 48 | ## More Details 49 | 50 | **✨ to learn more about Bolt the form builder, please visit:** 51 | 52 | - [Discord](https://discord.com/channels/883083792112300104/1282746904303894579) 53 | - [Docs](https://larazeus.com/docs/bolt) 54 | - [Github](https://github.com/lara-zeus/bolt) 55 | - [Demo](https://demo.larazeus.com) 56 | -------------------------------------------------------------------------------- /docs/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | weight: 1 4 | --- 5 | -------------------------------------------------------------------------------- /docs/getting-started/assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Themes and Assets 3 | weight: 6 4 | --- 5 | 6 | ## Compiling assets 7 | 8 | We use [tailwind Css](https://tailwindcss.com/) and custom themes by filament, make sure you are familiar with [tailwindcss configuration](https://tailwindcss.com/docs/configuration), and how to make a custom [filament theme](https://filamentphp.com/docs/2.x/admin/appearance#building-themes). 9 | 10 | ### Custom Classes 11 | 12 | You need to add these files to your `tailwind.config.js` file in the `content` section. 13 | 14 | * frontend: 15 | 16 | ```js 17 | content: [ 18 | //... 19 | './vendor/lara-zeus/core/resources/views/**/*.blade.php', 20 | './vendor/lara-zeus/bolt/resources/views/themes/**/*.blade.php', 21 | ] 22 | ``` 23 | 24 | * filament: 25 | 26 | ```js 27 | content: [ 28 | //... 29 | './vendor/lara-zeus/bolt/resources/views/filament/**/*.blade.php', 30 | './vendor/lara-zeus/accordion/resources/views/**/*.blade.php', 31 | ] 32 | ``` 33 | 34 | ### Customizing the Frontend Views 35 | 36 | First, publish the config file: 37 | 38 | ```php 39 | php artisan vendor:publish --tag=zeus-config 40 | ``` 41 | 42 | Then change the default layout in the file `zeus.php`: 43 | 44 | ```php 45 | 'layout' => 'components.layouts.app', 46 | // this is assuming your layout on the folder `resources/views/components/layouts/app` 47 | ``` 48 | This will give you full control for the assets files and the header and the footer. 49 | 50 | 51 | If needed, you can publish the blade views for all zeus packages: 52 | 53 | ```php 54 | php artisan vendor:publish --tag=zeus-views 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/getting-started/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | weight: 100 4 | --- 5 | 6 | All changes to @zeus Bolt are auto updated documented on GitHub [changelog](https://github.com/lara-zeus/bolt/blob/main/CHANGELOG.md) 7 | -------------------------------------------------------------------------------- /docs/getting-started/configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuration 3 | weight: 5 4 | --- 5 | 6 | ## Configuration 7 | 8 | There are two different sets of configuration, for filament, and for the frontend pages 9 | 10 | ## Filament Configuration 11 | 12 | To configure the plugin Bolt, you can pass the configuration to the plugin in `adminPanelProvider` 13 | 14 | These all the available configuration, and their defaults values 15 | 16 | > **Note**\ 17 | > All these configurations are optional 18 | 19 | ```php 20 | BoltPlugin::make() 21 | // the default models, by default Bolt will read from the config file 'zeus-bolt'. 22 | // but if you want to customize the models per panel, you can do it here 23 | ->models([ 24 | // ... 25 | 'Category' => \App\Models\Bolt\Category::class, 26 | 'Collection' => \App\Models\Bolt\Collection::class, 27 | 'Field' => \App\Models\Bolt\Field::class, 28 | 'FieldResponse' => \App\Models\Bolt\FieldResponse::class, 29 | 'Form' => \App\Models\Bolt\Form::class, 30 | 'FormsStatus' => \App\Models\Bolt\FormsStatus::class, 31 | 'Response' => \App\Models\Bolt\Response::class, 32 | 'Section' => \App\Models\Bolt\Section::class, 33 | 'User' => \App\Models\Staff::class, 34 | ]) 35 | 36 | // make the actions floating in create and edit forms 37 | ->formActionsAreSticky(true) 38 | 39 | ->hideResources([ 40 | FormResource::class 41 | ]) 42 | 43 | ->globallySearchableAttributes([ 44 | // you can return empty array to disable it 45 | FormResource::class => ['name'] 46 | ]) 47 | 48 | ->navigationGroupLabel('Bolt') 49 | 50 | ->hideNavigationBadges(resource: LaraZeus\Bolt\Resources::CollectionResource) 51 | ->showNavigationBadges(resource: LaraZeus\Bolt\Resources::CollectionResource) 52 | 53 | // if you have custom extension or using thunder 54 | ->extensions([ 55 | \LaraZeus\Thunder\Extensions\Thunder::class, 56 | ]) 57 | , 58 | ``` 59 | 60 | ## Customize Filament Resources 61 | 62 | You can customize all Bolt resources icons and sorting by adding the following code to your `AppServiceProvider` boot method 63 | 64 | ```php 65 | FormResource::navigationSort(100); 66 | FormResource::navigationIcon('heroicon-o-home'); 67 | FormResource::navigationGroup('New Name'); 68 | ``` 69 | 70 | ### Show or Hide Badges 71 | 72 | To show all navigation badges (default) 73 | ``` 74 | ->showNavigationBadges() 75 | ``` 76 | 77 | To hide all navigation badges 78 | ``` 79 | ->hideNavigationBadges() 80 | ``` 81 | 82 | This will hide only the CollectionResource navigation badge 83 | ``` 84 | ->hideNavigationBadges(resource: LaraZeus\Bolt\Resources::CollectionResource) 85 | ``` 86 | 87 | This will show only the FormResource navigation badge 88 | ``` 89 | ->hideNavigationBadges() 90 | ->showNavigationBadges(resource: LaraZeus\Bolt\Resources::CollectionResource) 91 | ``` 92 | 93 | available resources: 94 | 95 | - CategoryResource, 96 | - CollectionResource, 97 | - FormResource, 98 | 99 | ## Frontend Configuration 100 | 101 | Use the file `zeus-bolt.php`, to customize the frontend, like the prefix, domain, and middleware for each content type. 102 | 103 | To publish the configuration: 104 | 105 | ```bash 106 | php artisan vendor:publish --tag=zeus-bolt-config 107 | ``` 108 | 109 | ### Custom User Model 110 | 111 | By default Bolt will use the default Laravel user model to get the user info: 112 | 113 | `config('auth.providers.users.model')` 114 | 115 | If you need to change this to use another model, add the following in your config file: `zeus-bolt.php`: 116 | 117 | ```php 118 | 'models' => [ 119 | //... 120 | 'User' => AnotherUserModel::class, 121 | ], 122 | ``` -------------------------------------------------------------------------------- /docs/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | weight: 3 4 | --- 5 | 6 | ## Prerequisites 7 | 8 | Bolt is built on top of [laravel](https://laravel.com/docs/master) and uses [filament](https://filamentphp.com/docs/3.x/panels/installation) as an admin panel to manage everything. 9 | 10 | And for the frontend, it uses [Tall stack](https://tallstack.dev/). 11 | 12 | So, ensure you are familiar with these tools before diving into @zeus Bolt. 13 | 14 | > **Note**\ 15 | > You can get up and running with our [starter kit Zeus](https://github.com/lara-zeus/zeus). 16 | 17 | ## Installation 18 | 19 | > **Important**\ 20 | > Before starting, make sure you have the following PHP extensions enabled: 21 | `sqlite` 22 | 23 | Install @zeus Bolt by running the following commands in your Laravel project directory. 24 | 25 | ```bash 26 | composer require lara-zeus/bolt 27 | php artisan bolt:install 28 | ``` 29 | 30 | The install command will publish the migrations and the necessary assets for the frontend. 31 | 32 | ## Register Bolt with Filament: 33 | 34 | To set up the plugin with filament, you need to add it to your panel provider; The default one is `adminPanelProvider` 35 | 36 | ```php 37 | ->plugins([ 38 | SpatieLaravelTranslatablePlugin::make()->defaultLocales([config('app.locale')]), 39 | BoltPlugin::make() 40 | ]) 41 | ``` 42 | 43 | ## Add Bolt Trait to User Model 44 | 45 | add this to your user model: 46 | 47 | `use \LaraZeus\Bolt\Models\Concerns\BelongToBolt;` 48 | 49 | This will allow you to get the user name by another attribute like `full_name` 50 | 51 | ## Usage 52 | 53 | To access the forms, visit the URL `/admin` , and `/bolt`. 54 | 55 | ## Deploying to Production 56 | 57 | to improve performance in the production environment, make sure to run these commands with your deployment workflow: 58 | 59 | ```bash 60 | php artisan icons:cache 61 | ``` 62 | 63 | To learn more about performance in the production, check out [filament docs](https://filamentphp.com/docs/3.x/panels/installation#improving-filament-panel-performance). 64 | -------------------------------------------------------------------------------- /docs/getting-started/loading.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Loading indicator 3 | weight: 7 4 | --- 5 | 6 | ## Frontend loading indicator 7 | 8 | By default there is a loading indicator on the top left next to the breadcrumbs, but you can customize it as you want. 9 | 10 | ### the loading blade 11 | 12 | Create the file `resources/views/vendor/zeus/themes/zeus/bolt/loading.blade.php` 13 | with the default content: 14 | 15 | ```html 16 |
17 | @teleport('.bolt-loading') 18 |
19 | @svg('heroicon-o-arrow-path', 'text-primary-600 w-8 h-8 animate-spin') 20 |
21 | @endteleport 22 |
23 | ``` 24 | 25 | In your app layout add the following where you want the loader to show 26 | 27 | ```html 28 |
29 | ``` 30 | -------------------------------------------------------------------------------- /docs/getting-started/upgrade.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Upgrading 3 | weight: 90 4 | --- 5 | 6 | ## upgrade to v3.0.25 7 | 8 | In v3.0.25, I added a new trait for getting the user name 9 | 10 | So you have to add this to your User model: 11 | 12 | `use \LaraZeus\Bolt\Models\Concerns\BelongToBolt;` 13 | 14 | ## upgrade to v2.1 15 | 16 | In v2.1, I refactored the configuration to separate the frontend configuration from filament-related ones. 17 | This causes an issue when having multiple panels. 18 | 19 | 1. First, publish the config file by running the command: 20 | 21 | ```bash 22 | php artisan vendor:publish --tag="zeus-bolt-config" --force 23 | ``` 24 | 25 | 2. Move your configuration from your panel provider to the `zeus-bolt` config file. 26 | 27 | So these are the deprecated configuration methods: 28 | 29 | 30 | ```php 31 | 32 | ->boltPrefix() 33 | ->boltMiddleware() 34 | ->defaultMailable() 35 | ->uploadDisk() 36 | ->uploadDirectory() 37 | ->domain() 38 | 39 | ``` 40 | 41 | ## upgrade from 2 to 3 42 | 43 | To upgrade @zeus Bolt to v2 please check this `Core` [upgrade guide](/docs/core/v3/upgrade) 44 | -------------------------------------------------------------------------------- /docs/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | weight: 1 4 | --- 5 | 6 | ## Introduction 7 | @zeus bolt is a form builder for your users, with so many use cases 8 | included a UI for the frontend built with filament 9 | 10 | For example, you can use it as a job board, appointments, ticketing, survey, and even [more use cases](/docs/bolt/v2/usage/use-cases). 11 | 12 | **[Demo](https://demo.larazeus.com/admin/forms) · [Github](https://github.com/lara-zeus/bolt) · [Discord](https://discord.com/channels/883083792112300104/1282746904303894579)** 13 | 14 | ## Features 15 | - 🔥 zero configuration, just composer it. but highly customizable 16 | - 🔥 Built with [TALL stack](https://tallstack.dev/) 17 | - 🔥 Using [filament](https://filamentadmin.com) as an admin panel and form builder 18 | - 🔥 Frontend scaffolding, highly customizable 19 | - 🔥 Sections to Group your fields with the Option to display them as wizard or tabs 20 | - 🔥 Categories to organize your forms in the frontend 21 | - 🔥 Control the starting and ending dates for submissions 22 | - 🔥 Email notifications per form 23 | - 🔥 Confirmation message after submission per form 24 | - 🔥 Option for requiring login per form 25 | - 🔥 Limit submissions with one entry for a user (if auth is required only) 26 | - 🔥 Conditional Visibility for fields depending on other field's values 27 | - 🔥 Extensions control the submitting and store them or perform actions 28 | - 🔥 Embed the form in [sky](https://github.com/lara-zeus/sky) posts and pages, or as a livewire component 29 | - 🔥 Add any field from filament plugins 30 | - 🔥 Create your own field 31 | - 🔥 Collections for datasets 32 | - 🔥 Create Custom Collections from eloquent model 33 | - 🔥 Validation rules for text input 34 | - 🔥 Multi lang ready 35 | - 🔥 Show responses for each form 36 | - 🔥 Search in all responses filtering on the dynamic fields 37 | - 🔥 Set status for each response 38 | - 🔥 Export or print form with its fields 39 | - 🔥 Replicate forms with their sections and fields 40 | - 🔥 Form reports, statistics, and widgets 41 | - 🔥 Multiple events ready to listen to 42 | - Poll Module: (custom layout to show the form as a poll and display the result as a chart) (soon) 43 | - Exam Module: (set correct answer, the mark for each question, autocorrection for all responses, send the mark to the user, and display the result) (soon) 44 | - Product Module: (show an order form with products and pricing) (soon) 45 | 46 | ## Bolt Pro 47 | 48 | [![bolt-pro](https://larazeus.com/images/bolt-pro-ad.webp)](https://larazeus.com/bolt-pro) 49 | 50 | ## Support 51 | 52 | Available support channels: 53 | 54 | * Join our channel on [Discord](https://discord.com/channels/883083792112300104/1282746904303894579) 55 | * open an issue on [GitHub](https://github.com/lara-zeus/bolt/issues) 56 | * Email us using the [contact center](https://larazeus.com/contact-us) 57 | -------------------------------------------------------------------------------- /docs/usage/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Architecture 3 | weight: 2 4 | --- 5 | -------------------------------------------------------------------------------- /docs/usage/embed.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Embed the Form 3 | weight: 3 4 | --- 5 | 6 | ## Embed the Form 7 | 8 | @zeus Bolt forms are simply a livewire component, you can embed it in any page in your frontend or filament pages. 9 | 10 | To embed the Form in any blade page, simply use: 11 | 12 | ```blade 13 | 14 | ``` 15 | 16 | If you have an extension linked to your form, you can pass in the `extensionSlug` 17 | 18 | ```blade 19 | 20 | ``` 21 | 22 | and [this](https://demo.larazeus.com/embed) is how the form looks in an empty page. 23 | 24 | ## Embed in the Sky 25 | 26 | If you are using our package @zeus [Sky](https://larazeus.com/sky), you can embed the forms by using the code: 27 | ```html 28 | formSlug 29 | ``` 30 | 31 | ## Sharing on the Web 32 | With Bolt Pro, and for your users convenience, we added a new tab in the form to make it easy to copy the code. 33 | 34 | Read more about it in [Bolt Pro share-form](../bolt-pro/share-form) 35 | -------------------------------------------------------------------------------- /docs/usage/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | weight: 2 4 | --- 5 | 6 | ## Overview 7 | Here is the structure of the database tables; it will give you an idea of how Bolt works: 8 | 9 | ![bolt-diagram](https://larazeus.com/images/screenshots/bolt/bolt-diagram.webp) 10 | 11 | ## The concept 12 | 13 | ### Form 14 | The main component of all of this. Including all descriptive texts and the form options. 15 | 16 | ### Sections 17 | To group the fields, it has some options like columns number. 18 | Also, an option to display sections as pages or wizards, or tabs. 19 | 20 | ### Fields 21 | Fields are fields, most of the common types are available, and you can create custom ones too. 22 | 23 | ### Categories 24 | It's a way to organize and display the forms in the front end. 25 | Any form not linked to a category won't be listed in the forms list at the front end. 26 | 27 | ### Collections 28 | Collections are datasets used for lists like checkboxes and select menus. 29 | 30 | ### Responses 31 | Every submission has a response record per user. 32 | You can view the responses per form in three ways: 33 | - list: list the response only as cards 34 | - browse: how the response with the fields one per page 35 | - report: table view for all entries and their fields 36 | 37 | ### Responses Fields 38 | Fields are related to each response and each field of the form. And they contain the user input. 39 | 40 | This structure helps to create any reports you want with ease. 41 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 6 3 | paths: 4 | - src 5 | - database 6 | 7 | checkOctaneCompatibility: true 8 | checkModelProperties: true 9 | 10 | ignoreErrors: 11 | - 12 | identifier: trait.unused 13 | - 14 | identifier: missingType.generics 15 | - 16 | identifier: argument.templateType 17 | - 18 | identifier: missingType.iterableValue 19 | - 20 | identifier: argument.type 21 | - 22 | identifier: property.defaultValue 23 | - 24 | identifier: return.type 25 | - 26 | identifier: class.notFound 27 | - 28 | identifier: notIdentical.alwaysTrue 29 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | ./src 15 | 16 | 17 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "rules": { 4 | "blank_line_before_statement": true, 5 | "concat_space": { 6 | "spacing": "one" 7 | }, 8 | "method_argument_space": true, 9 | "single_trait_insert_per_statement": true, 10 | "types_spaces": { 11 | "space": "single" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /resources/views/emails/form-submission.blade.php: -------------------------------------------------------------------------------- 1 | 2 | # {{ __('New submission on form') }}: {{ $form->name }} 3 | 4 | 5 | {{ __('view the entry') }} 6 | 7 | 8 | {{ __('Thanks') }},
9 | {{ config('app.name') }} 10 |
-------------------------------------------------------------------------------- /resources/views/errors/date-not-available.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

{{ __('Date Not Available') }}

4 |
5 | 6 |
7 | 8 | 9 |
10 | @svg('heroicon-o-exclamation-triangle','w-5 h-5 text-primary-600') 11 | 12 | {{ __('Date Not Available') }} 13 | 14 |
15 |
16 | {{ __('the form is not available for submission') }} 17 | {{ $zeusForm->name ?? '' }}. 18 | 19 | 20 | {{ __('Start date') }}: 21 | {{ $zeusForm->start_date->format(\Filament\Infolists\Infolist::$defaultDateTimeDisplayFormat) }}, 22 | {{ __('End date') }}: 23 | {{ $zeusForm->end_date->format(\Filament\Infolists\Infolist::$defaultDateTimeDisplayFormat) }} 24 | 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /resources/views/errors/login-required.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

{{ __('Login Required') }}

4 |
5 | 6 |
7 | 8 | 9 |
10 | @svg('heroicon-o-exclamation-triangle','w-5 h-5 text-primary-600') 11 | 12 | {{ __('Login Required') }} 13 | 14 |
15 |
16 | {{ __('Login is required to access the form') }} 17 | {{ $zeusForm->name ?? '' }}. 18 | 19 | 20 | {{ __('Login') }} 21 | 22 | 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /resources/views/errors/one-entry-per-user.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

{{ __('one entry') }}

4 |
5 | 6 |
7 | 8 | 9 |
10 | @svg('heroicon-o-exclamation-triangle','w-5 h-5 text-primary-600') 11 | 12 | {{ __('one entry per user') }} 13 | 14 |
15 |
16 | {{ __('the form') }} 17 | {{ $zeusForm->name ?? '' }}. 18 | {{ __('allow only one entry per user') }} 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /resources/views/filament/components/color-picker.blade.php: -------------------------------------------------------------------------------- 1 | 5 |
6 | 7 | 8 | 9 |
10 |

{{ __('Color') }}

11 | 12 |
13 | @php 14 | $colors = collect(\Filament\Support\Colors\Color::all())->forget(['slate','zinc','neutral','stone'])->keys()->toArray(); 15 | @endphp 16 | @foreach($colors as $color) 17 | @php 18 | $setColor = \Illuminate\Support\Arr::toCssStyles([ 19 | \Filament\Support\get_color_css_variables($color, shades: [500]), 20 | ]); 21 | @endphp 22 | 30 | 31 | @endforeach 32 |
33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /resources/views/filament/fields/file-upload.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @if(filled($responseValue)) 3 |
4 | @foreach($responseValue as $file) 5 | 11 | {{ __('view file') .': '. $loop->iteration }} 12 | 13 | @endforeach 14 |
15 | @else 16 | {{ __('no file uploaded') }} 17 | @endif 18 |
19 | -------------------------------------------------------------------------------- /resources/views/filament/fields/types.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @svg($field['icon'],'text-primary-500 w-5 h-5') 3 | 4 | 5 | {{ $field['title'] }} 6 | {{--{{ $field['description'] }}--}} 7 | 8 | 12 | @svg('heroicon-o-information-circle','mx-2 w-4 h-4 text-gray-400') 13 | 14 | 15 |
-------------------------------------------------------------------------------- /resources/views/filament/pages/reports/entries.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $this->table }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/filament/resources/form-resource/widgets/edit-collection-warning.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | {{ __('any changes in the values of these items will affect the responses for the forms in') }} 5 | 6 | {{ \LaraZeus\Bolt\Models\Field::whereJsonContains('options->dataSource', "$record->id")->count() }} 7 | 8 | {{ __('field') }} 9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /resources/views/filament/resources/response-resource/components/view-responses.blade.php: -------------------------------------------------------------------------------- 1 | 5 |
6 | @include('zeus::filament.resources.response-resource.pages.show-entry') 7 |
8 |
-------------------------------------------------------------------------------- /resources/views/filament/resources/response-resource/pages/browse-responses.blade.php: -------------------------------------------------------------------------------- 1 | 2 | {{ $this->table }} 3 | 4 | -------------------------------------------------------------------------------- /resources/views/themes/zeus/bolt/list-entries.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |

{{ __('browse your Entries') }}

4 |
5 | 6 | 7 |
  • 8 | {{ __('browse your Entries') }} 9 |
  • 10 |
    11 | 12 |
    13 | {{ $this->table }} 14 |
    15 |
    16 | -------------------------------------------------------------------------------- /resources/views/themes/zeus/bolt/list-forms.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |

    {{ __('List All Forms') }}

    5 |
    6 | 7 | 8 |
  • 9 | {{ __('Forms') }} 10 |
  • 11 |
    12 | 13 | {{ \LaraZeus\Bolt\Facades\Bolt::renderHookBlade('zeus-forms.before') }} 14 | 15 |
    16 | @foreach($categories as $category) 17 | 18 | @if($category->logo !== null) 19 | {{ $category->name }} {{ __('Logo') }} 20 | @endif 21 | 22 |

    23 | {{ $category->name }} 24 | {{ $category->description }} 25 |

    26 | 27 | @foreach($category->forms as $form) 28 | 29 | 30 | {{ $form->name ?? '' }} 31 | 32 | 33 | {{ $form->description }} 34 | 35 | 36 | @endforeach 37 |
    38 | @endforeach 39 |
    40 | 41 | {{ \LaraZeus\Bolt\Facades\Bolt::renderHookBlade('zeus-forms.before') }} 42 | 43 |
    44 | -------------------------------------------------------------------------------- /resources/views/themes/zeus/bolt/loading.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | @teleport('.bolt-loading') 4 |
    5 | @svg('heroicon-o-arrow-path', 'text-primary-600 w-6 h-6 animate-spin') 6 |
    7 | @endteleport 8 |
    9 | -------------------------------------------------------------------------------- /resources/views/themes/zeus/bolt/submitted.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | @if(!empty($zeusForm->options['confirmation-message'])) 5 | 6 | {!! $zeusForm->options['confirmation-message'] !!} 7 | 8 | @else 9 | 10 | {{ __('the form') }} 11 | {{ $zeusForm->name ?? '' }} 12 | {{ __('submitted successfully') }}. 13 | 14 | @endif 15 | 16 | {!! \LaraZeus\Bolt\Facades\Extensions::init($zeusForm, 'SubmittedRender', [ 17 | 'extensionData' => $extensionData['extInfo']['itemId'] ?? 0, 18 | 'response' => $extensionData['response'], 19 | ]) !!} 20 | 21 | 22 |
    23 |
    24 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | prefix(config('zeus-bolt.prefix')) 11 | ->name('bolt.') 12 | ->middleware(config('zeus-bolt.middleware')) 13 | ->group(function () { 14 | Route::get('/', ListForms::class) 15 | ->name('forms.list'); 16 | 17 | Route::get('/entries', ListEntries::class)->name('entries.list') 18 | ->middleware('auth'); 19 | 20 | Route::get('/entry/{responseID}', ShowEntry::class) 21 | ->name('entry.show') 22 | ->middleware('auth'); 23 | 24 | if (class_exists(\LaraZeus\BoltPro\BoltProServiceProvider::class)) { 25 | Route::get('embed/{slug}', \LaraZeus\BoltPro\Livewire\EmbedForm::class) 26 | ->name('form.embed'); 27 | } 28 | 29 | Route::get('{slug}/{extensionSlug?}', FillForms::class) 30 | ->name('form.show'); 31 | }); 32 | -------------------------------------------------------------------------------- /src/BoltPlugin.php: -------------------------------------------------------------------------------- 1 | ['name', 'slug'], 36 | CollectionResource::class => ['name', 'values'], 37 | FormResource::class => ['name', 'slug'], 38 | ]; 39 | 40 | public function getId(): string 41 | { 42 | return 'zeus-bolt'; 43 | } 44 | 45 | public function register(Panel $panel): void 46 | { 47 | $panel 48 | ->resources([ 49 | CollectionResource::class, 50 | FormResource::class, 51 | CategoryResource::class, 52 | ]); 53 | } 54 | 55 | public static function make(): static 56 | { 57 | return new self; 58 | } 59 | 60 | public static function get(): static 61 | { 62 | // @phpstan-ignore-next-line 63 | return filament('zeus-bolt'); 64 | } 65 | 66 | public function boot(Panel $panel): void 67 | { 68 | // 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/BoltServiceProvider.php: -------------------------------------------------------------------------------- 1 | name(static::$name) 25 | ->hasViews('zeus') 26 | ->hasMigrations($this->getMigrations()) 27 | ->hasTranslations() 28 | ->hasConfigFile() 29 | ->hasCommands($this->getCommands()) 30 | ->hasRoute('web'); 31 | } 32 | 33 | public function packageBooted(): void 34 | { 35 | CoreServiceProvider::setThemePath('bolt'); 36 | 37 | Livewire::component('bolt.fill-form', FillForms::class); 38 | Livewire::component('bolt.list-forms', ListForms::class); 39 | Livewire::component('bolt.list-entries', ListEntries::class); 40 | } 41 | 42 | /** 43 | * @return array 44 | */ 45 | protected function getCommands(): array 46 | { 47 | return [ 48 | PublishCommand::class, 49 | ZeusFieldCommand::class, 50 | ZeusDatasourceCommand::class, 51 | InstallCommand::class, 52 | ]; 53 | } 54 | 55 | /** 56 | * @return array 57 | */ 58 | protected function getMigrations(): array 59 | { 60 | return [ 61 | 'create_categories_table', 62 | 'create_collections_table', 63 | 'create_forms_table', 64 | 'create_sections_table', 65 | 'create_fields_table', 66 | 'create_responses_table', 67 | 'create_field_responses_table', 68 | 'add_extensions_to_forms', 69 | 'add_extension_item_responses', 70 | 'alter_tables_constraints', 71 | 'add_compact_to_section', 72 | 'add_options_to_section', 73 | 'add_grade_to_response', 74 | 'add_grade_to_field_response', 75 | 'add_borderless_to_section', 76 | ]; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Commands/InstallCommand.php: -------------------------------------------------------------------------------- 1 | info('publishing migrations...'); 16 | $this->call('vendor:publish', ['--tag' => 'zeus-bolt-migrations']); 17 | $this->call('vendor:publish', ['--tag' => 'zeus-bolt-config']); 18 | 19 | $this->info('publishing assets...'); 20 | $this->call('vendor:publish', ['--tag' => 'zeus-assets']); 21 | 22 | if ($this->confirm('Do you want to run the migration now?', true)) { 23 | $this->info('running migrations...'); 24 | $this->call('migrate'); 25 | } 26 | 27 | $this->output->success('Zeus Bolt has been Installed successfully, consider ⭐️ the package in filament site :)'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Commands/PublishCommand.php: -------------------------------------------------------------------------------- 1 | callSilent('vendor:publish', ['--tag' => 'zeus-bolt-migrations', '--force' => $this->option('force')]); 29 | $this->callSilent('vendor:publish', ['--tag' => 'zeus-bolt-config', '--force' => $this->option('force')]); 30 | 31 | // publish Zeus files 32 | $this->callSilent('vendor:publish', ['--tag' => 'zeus-config', '--force' => $this->option('force')]); 33 | $this->callSilent('vendor:publish', ['--tag' => 'zeus-views', '--force' => $this->option('force')]); 34 | $this->callSilent('vendor:publish', ['--tag' => 'zeus-assets', '--force' => $this->option('force')]); 35 | $this->callSilent('vendor:publish', ['--tag' => 'zeus-lang', '--force' => $this->option('force')]); 36 | 37 | $this->callSilent('vendor:publish', ['--tag' => 'filament-icon-picker-config', '--force' => $this->option('force')]); 38 | 39 | $this->output->success('Zeus and Bolt has been Published successfully'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Commands/ZeusDatasourceCommand.php: -------------------------------------------------------------------------------- 1 | argument('name'); 32 | 33 | $path = config('zeus-bolt.collectors.dataSources.path'); 34 | $namespace = str_replace('\\\\', '\\', trim(config('zeus-bolt.collectors.dataSources.namespace'), '\\')); 35 | 36 | $this->copyStubToApp('ZeusDataSources', "{$path}/{$filamentPluginFullNamespace}.php", [ 37 | 'namespace' => $namespace, 38 | 'class' => $filamentPluginFullNamespace, 39 | ]); 40 | 41 | $this->info('zeus datasource created successfully!'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Commands/ZeusFieldCommand.php: -------------------------------------------------------------------------------- 1 | argument('plugin'); 32 | $fieldClassName = str($filamentPluginFullNamespace)->explode('\\')->last(); 33 | 34 | $path = config('zeus-bolt.collectors.fields.path'); 35 | $namespace = str_replace('\\\\', '\\', trim(config('zeus-bolt.collectors.fields.namespace'), '\\')); 36 | 37 | $this->copyStubToApp('ZeusField', "{$path}/{$fieldClassName}.php", [ 38 | 'namespace' => $namespace, 39 | 'plugin' => $filamentPluginFullNamespace, 40 | 'class' => $fieldClassName, 41 | ]); 42 | 43 | $this->info('zeus field created successfully!'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Concerns/CanManipulateFiles.php: -------------------------------------------------------------------------------- 1 | fileExists($stubPath = base_path("stubs/zeus-bolt/{$stub}.stub"))) { 16 | $stubPath = $this->getDefaultStubPath() . "/{$stub}.stub"; 17 | } 18 | 19 | $stub = Str::of($filesystem->get($stubPath)); 20 | 21 | foreach ($replacements as $key => $replacement) { 22 | $stub = $stub->replace("{{ {$key} }}", $replacement); 23 | } 24 | 25 | $stub = (string) $stub; 26 | 27 | $this->writeFile($targetPath, $stub); 28 | } 29 | 30 | protected function fileExists(string $path): bool 31 | { 32 | $filesystem = app(Filesystem::class); 33 | 34 | return $filesystem->exists($path); 35 | } 36 | 37 | protected function writeFile(string $path, string $contents): void 38 | { 39 | $filesystem = app(Filesystem::class); 40 | 41 | $filesystem->ensureDirectoryExists( 42 | (string) Str::of($path) 43 | ->beforeLast('/'), 44 | ); 45 | 46 | $filesystem->put($path, $contents); 47 | } 48 | 49 | protected function getDefaultStubPath(): string 50 | { 51 | $reflectionClass = new ReflectionClass($this); 52 | 53 | return (string) Str::of($reflectionClass->getFileName()) 54 | ->beforeLast('Commands') 55 | ->append('../stubs'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Concerns/HasActive.php: -------------------------------------------------------------------------------- 1 | is_active === 0) 10 | ? '' . __('Inactive') . '' 11 | : '' . __('Active') . ''; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Concerns/HasHiddenOptions.php: -------------------------------------------------------------------------------- 1 | default(false), 13 | Hidden::make('options.visibility.fieldID'), 14 | Hidden::make('options.visibility.values'), 15 | ]; 16 | } 17 | 18 | public static function hiddenRequired(): array 19 | { 20 | return [ 21 | Hidden::make('options.is_required')->default(false), 22 | ]; 23 | } 24 | 25 | public static function hiddenHintOptions(): array 26 | { 27 | return [ 28 | Hidden::make('options.hint.text'), 29 | Hidden::make('options.hint.icon'), 30 | Hidden::make('options.hint.color'), 31 | Hidden::make('options.hint.icon-tooltip'), 32 | ]; 33 | } 34 | 35 | public static function hiddenColumnSpanFull(): array 36 | { 37 | return [ 38 | Hidden::make('options.column_span_full')->default(false), 39 | ]; 40 | } 41 | 42 | public static function hiddenHiddenLabel(): array 43 | { 44 | return [ 45 | Hidden::make('options.hidden_label')->default(false), 46 | ]; 47 | } 48 | 49 | public static function hiddenDataSource(): array 50 | { 51 | return [ 52 | Hidden::make('options.dataSource'), 53 | ]; 54 | } 55 | 56 | public static function hiddenHtmlID(): array 57 | { 58 | return [ 59 | Hidden::make('options.htmlId')->default(str()->random(6)), 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Concerns/HasUpdates.php: -------------------------------------------------------------------------------- 1 | ' . Carbon::parse($this->updated_at)->format(\Filament\Infolists\Infolist::$defaultDateTimeDisplayFormat) . ''; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Configuration.php: -------------------------------------------------------------------------------- 1 | null, 9 | 'section' => null, 10 | 'field' => null, 11 | ]; 12 | 13 | /** 14 | * available extensions, leave it null to disable the extensions tab from the forms 15 | */ 16 | protected ?array $extensions = null; 17 | 18 | protected bool $formActionsAreSticky = false; 19 | 20 | public function customSchema(array $schema): static 21 | { 22 | $this->customSchema = $schema; 23 | 24 | return $this; 25 | } 26 | 27 | public function getCustomSchema(): array 28 | { 29 | return $this->customSchema; 30 | } 31 | 32 | public static function getSchema(string $type): ?string 33 | { 34 | return (new static)::get()->getCustomSchema()[$type]; 35 | } 36 | 37 | public function formActionsAreSticky(bool $condition = false): static 38 | { 39 | $this->formActionsAreSticky = $condition; 40 | 41 | return $this; 42 | } 43 | 44 | public function isFormActionsAreSticky(): bool 45 | { 46 | return $this->evaluate($this->formActionsAreSticky); 47 | } 48 | 49 | public function extensions(?array $extensions): static 50 | { 51 | $this->extensions = $extensions; 52 | 53 | return $this; 54 | } 55 | 56 | public function getExtensions(): ?array 57 | { 58 | return $this->extensions; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Contracts/CustomFormSchema.php: -------------------------------------------------------------------------------- 1 | render() 29 | * 30 | * @param Form $form Bolt form 31 | * @param array $data extra data 32 | */ 33 | public function render(Form $form, array $data): ?string; 34 | 35 | /* 36 | * return an array of filament components to add them to the form 37 | * 38 | * @param Form $form Bolt form 39 | */ 40 | public function formComponents(Form $form): ?array; 41 | 42 | /* 43 | * will triggered before saving the form, if you want to perform some validation 44 | * 45 | * you can throw an exception or return true 46 | */ 47 | public function preStore(Form $form, array $data): bool; 48 | 49 | /** 50 | * the store logic for the app, insert ticket or any DB ONLY calls, don't send here anything, 51 | * and you must return the saved app, if you want to depend on it in the postStore 52 | * 53 | * @param Form $form Bolt form 54 | * @param array $data extra data 55 | * 56 | * @throws \Exception 57 | */ 58 | public function store(Form $form, array $data): ?array; 59 | 60 | /** 61 | * this typically used for sending only, it will execute after the DB::transaction 62 | * 63 | * @param Form $form Bolt form 64 | */ 65 | public function postStore(Form $form, array $data): void; 66 | 67 | /** 68 | * this will show any info after saving the form, like ticket num or more buttons and links 69 | * also it's better to use blade file, view()->render() 70 | * 71 | * @param Form $form Bolt form 72 | */ 73 | public function SubmittedRender(Form $form, array $data): ?string; 74 | 75 | /* 76 | * list all items connected to a form 77 | */ 78 | public function getItems(Form $form): array; 79 | 80 | /* 81 | * return the url to the form, when clicking on `open`. append any parameters you need to your Extension 82 | */ 83 | public function getUrl(Form $form, array $extension): string; 84 | 85 | /* 86 | * check if its allowed to delete the form 87 | */ 88 | public function canDelete(Form $form, array $extension): bool; 89 | 90 | /* 91 | * check if its allowed to delete the response 92 | */ 93 | public function canDeleteResponse(Form $form, array $extension): bool; 94 | } 95 | -------------------------------------------------------------------------------- /src/Contracts/Fields.php: -------------------------------------------------------------------------------- 1 | getModel())->query(); 22 | } 23 | 24 | public function toArray(): array 25 | { 26 | return [ 27 | 'getValuesUsing' => $this->getValuesUsing(), 28 | 'getKeysUsing' => $this->getKeysUsing(), 29 | 'getModel' => $this->getModel(), 30 | 'title' => $this->title(), 31 | 'sort' => $this->getSort(), 32 | 'class' => '\\' . get_called_class(), 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Events/FormMounted.php: -------------------------------------------------------------------------------- 1 | form = $form; 23 | } 24 | 25 | /** 26 | * Get the channels the event should broadcast on. 27 | */ 28 | public function broadcastOn(): Channel | PrivateChannel | array 29 | { 30 | return new PrivateChannel('form-mounted'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Events/FormSent.php: -------------------------------------------------------------------------------- 1 | response = $response; 23 | } 24 | 25 | /** 26 | * Get the channels the event should broadcast on. 27 | */ 28 | public function broadcastOn(): Channel | PrivateChannel | array 29 | { 30 | return new PrivateChannel('form-sent'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Facades/Collectors.php: -------------------------------------------------------------------------------- 1 | disabled) { 30 | $allClasses[str($class)->explode('\\')->last()] = $getClass->toArray(); 31 | } 32 | } 33 | 34 | return $allClasses; 35 | } 36 | 37 | public static function loadClasses(string $path, string $namespace): array 38 | { 39 | $classes = []; 40 | $path = array_unique(Arr::wrap($path)); 41 | 42 | foreach ((new Finder)->in($path)->files() as $className) { 43 | $classes[] = $namespace . $className->getFilenameWithoutExtension(); 44 | } 45 | 46 | return $classes; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Facades/Extensions.php: -------------------------------------------------------------------------------- 1 | extensions !== null) { 13 | if (class_exists($form->extensions)) { 14 | return (new $form->extensions)->{$hook}($form, $data, $action); 15 | } 16 | } 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Fields/Classes/CheckboxList.php: -------------------------------------------------------------------------------- 1 | accordions([ 40 | Accordion::make('general-options') 41 | ->label(__('General Options')) 42 | ->icon('tabler-settings') 43 | ->schema([ 44 | self::required(), 45 | self::columnSpanFull(), 46 | self::hiddenLabel(), 47 | self::htmlID(), 48 | ]), 49 | self::hintOptions(), 50 | self::visibility($sections), 51 | // @phpstan-ignore-next-line 52 | ...Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::schema($field) : [], 53 | Bolt::getCustomSchema('field', resolve(static::class)) ?? [], 54 | ]), 55 | ]; 56 | } 57 | 58 | public static function getOptionsHidden(): array 59 | { 60 | return [ 61 | // @phpstan-ignore-next-line 62 | Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::hidden() : [], 63 | ...Bolt::getHiddenCustomSchema('field', resolve(static::class)) ?? [], 64 | self::hiddenDataSource(), 65 | self::hiddenVisibility(), 66 | self::hiddenHtmlID(), 67 | self::hiddenHintOptions(), 68 | self::hiddenRequired(), 69 | self::hiddenColumnSpanFull(), 70 | self::hiddenHiddenLabel(), 71 | ]; 72 | } 73 | 74 | public function getResponse(Field $field, FieldResponse $resp): string 75 | { 76 | return $this->getCollectionsValuesForResponse($field, $resp); 77 | } 78 | 79 | // @phpstan-ignore-next-line 80 | public function appendFilamentComponentsOptions($component, $zeusField, bool $hasVisibility = false) 81 | { 82 | parent::appendFilamentComponentsOptions($component, $zeusField, $hasVisibility); 83 | 84 | $options = FieldsContract::getFieldCollectionItemsList($zeusField); 85 | 86 | $component = $component->options($options); 87 | 88 | if (request()->filled($zeusField->options['htmlId'])) { 89 | $component = $component->default(request($zeusField->options['htmlId'])); 90 | 91 | // todo set default items for datasources 92 | } elseif ($selected = $options->where('itemIsDefault', true)->pluck('itemKey')->isNotEmpty()) { 93 | $component = $component->default($selected); 94 | } 95 | 96 | return $component->live(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Fields/Classes/ColorPicker.php: -------------------------------------------------------------------------------- 1 | accordions([ 38 | Accordion::make('general-options') 39 | ->label(__('General Options')) 40 | ->icon('tabler-settings') 41 | ->schema([ 42 | \Filament\Forms\Components\Select::make('options.colorType') 43 | ->label(__('Color Type')) 44 | ->options([ 45 | 'hsl' => 'hsl', 46 | 'rgb' => 'rgb', 47 | 'rgba' => 'rgba', 48 | ]), 49 | self::required(), 50 | self::columnSpanFull(), 51 | self::hiddenLabel(), 52 | self::htmlID(), 53 | ]), 54 | self::hintOptions(), 55 | self::visibility($sections), 56 | Bolt::getCustomSchema('field', resolve(static::class)) ?? [], 57 | ]), 58 | ]; 59 | } 60 | 61 | public static function getOptionsHidden(): array 62 | { 63 | return [ 64 | ...Bolt::getHiddenCustomSchema('field', resolve(static::class)) ?? [], 65 | Hidden::make('options.colorType'), 66 | self::hiddenHtmlID(), 67 | self::hiddenHintOptions(), 68 | self::hiddenRequired(), 69 | self::hiddenColumnSpanFull(), 70 | self::hiddenHiddenLabel(), 71 | self::hiddenVisibility(), 72 | ]; 73 | } 74 | 75 | // @phpstan-ignore-next-line 76 | public function appendFilamentComponentsOptions($component, $zeusField, bool $hasVisibility = false) 77 | { 78 | parent::appendFilamentComponentsOptions($component, $zeusField, $hasVisibility); 79 | 80 | if (! empty($zeusField['options']['colorType'])) { 81 | call_user_func([$component, $zeusField['options']['colorType']]); 82 | } 83 | 84 | return $component; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Fields/Classes/DatePicker.php: -------------------------------------------------------------------------------- 1 | accordions([ 36 | Accordion::make('general-options') 37 | ->label(__('General Options')) 38 | ->icon('tabler-settings') 39 | ->schema([ 40 | self::required(), 41 | self::columnSpanFull(), 42 | self::hiddenLabel(), 43 | self::htmlID(), 44 | ]), 45 | self::hintOptions(), 46 | self::visibility($sections), 47 | // @phpstan-ignore-next-line 48 | ...Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::schema($field) : [], 49 | Bolt::getCustomSchema('field', resolve(static::class)) ?? [], 50 | ]), 51 | ]; 52 | } 53 | 54 | public static function getOptionsHidden(): array 55 | { 56 | return [ 57 | // @phpstan-ignore-next-line 58 | Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::hidden() : [], 59 | ...Bolt::getHiddenCustomSchema('field', resolve(static::class)) ?? [], 60 | self::hiddenHtmlID(), 61 | self::hiddenHintOptions(), 62 | self::hiddenRequired(), 63 | self::hiddenColumnSpanFull(), 64 | self::hiddenHiddenLabel(), 65 | self::hiddenVisibility(), 66 | ]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Fields/Classes/DateTimePicker.php: -------------------------------------------------------------------------------- 1 | accordions([ 36 | Accordion::make('general-options') 37 | ->label(__('General Options')) 38 | ->icon('tabler-settings') 39 | ->schema([ 40 | self::required(), 41 | self::columnSpanFull(), 42 | self::hiddenLabel(), 43 | self::htmlID(), 44 | ]), 45 | self::hintOptions(), 46 | self::visibility($sections), 47 | // @phpstan-ignore-next-line 48 | ...Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::schema($field) : [], 49 | Bolt::getCustomSchema('field', resolve(static::class)) ?? [], 50 | ]), 51 | ]; 52 | } 53 | 54 | public static function getOptionsHidden(): array 55 | { 56 | return [ 57 | // @phpstan-ignore-next-line 58 | Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::hidden() : [], 59 | ...Bolt::getHiddenCustomSchema('field', resolve(static::class)) ?? [], 60 | self::hiddenHtmlID(), 61 | self::hiddenHintOptions(), 62 | self::hiddenRequired(), 63 | self::hiddenColumnSpanFull(), 64 | self::hiddenHiddenLabel(), 65 | self::hiddenVisibility(), 66 | ]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Fields/Classes/Paragraph.php: -------------------------------------------------------------------------------- 1 | accordions([ 37 | Accordion::make('general-options') 38 | ->label(__('General Options')) 39 | ->icon('tabler-settings') 40 | ->schema([ 41 | self::columnSpanFull(), 42 | self::hiddenLabel(), 43 | self::hintOptions(), 44 | ]), 45 | self::visibility($sections), 46 | ]), 47 | ]; 48 | } 49 | 50 | public static function getOptionsHidden(): array 51 | { 52 | return [ 53 | self::hiddenHintOptions(), 54 | self::hiddenColumnSpanFull(), 55 | self::hiddenHiddenLabel(), 56 | self::hiddenVisibility(), 57 | ]; 58 | } 59 | 60 | // @phpstan-ignore-next-line 61 | public function appendFilamentComponentsOptions($component, $zeusField, bool $hasVisibility = false) 62 | { 63 | parent::appendFilamentComponentsOptions($component, $zeusField, $hasVisibility); 64 | 65 | return $component 66 | ->helperText('') 67 | ->content(new HtmlString($zeusField->description)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Fields/Classes/RichEditor.php: -------------------------------------------------------------------------------- 1 | accordions([ 36 | Accordion::make('general-options') 37 | ->label(__('General Options')) 38 | ->icon('tabler-settings') 39 | ->schema([ 40 | self::required(), 41 | self::columnSpanFull(), 42 | self::hiddenLabel(), 43 | self::htmlID(), 44 | ]), 45 | self::hintOptions(), 46 | self::visibility($sections), 47 | // @phpstan-ignore-next-line 48 | ...Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::schema($field) : [], 49 | Bolt::getCustomSchema('field', resolve(static::class)) ?? [], 50 | ]), 51 | ]; 52 | } 53 | 54 | public static function getOptionsHidden(): array 55 | { 56 | return [ 57 | // @phpstan-ignore-next-line 58 | Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::hidden() : [], 59 | ...Bolt::getHiddenCustomSchema('field', resolve(static::class)) ?? [], 60 | self::hiddenVisibility(), 61 | self::hiddenHtmlID(), 62 | self::hiddenHintOptions(), 63 | self::hiddenRequired(), 64 | self::hiddenColumnSpanFull(), 65 | self::hiddenHiddenLabel(), 66 | ]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Fields/Classes/TimePicker.php: -------------------------------------------------------------------------------- 1 | accordions([ 36 | Accordion::make('general-options') 37 | ->label(__('General Options')) 38 | ->icon('tabler-settings') 39 | ->schema([ 40 | self::required(), 41 | self::columnSpanFull(), 42 | self::hiddenLabel(), 43 | self::htmlID(), 44 | ]), 45 | self::hintOptions(), 46 | self::visibility($sections), 47 | // @phpstan-ignore-next-line 48 | ...Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::schema($field) : [], 49 | Bolt::getCustomSchema('field', resolve(static::class)) ?? [], 50 | ]), 51 | ]; 52 | } 53 | 54 | public static function getOptionsHidden(): array 55 | { 56 | return [ 57 | // @phpstan-ignore-next-line 58 | Bolt::hasPro() ? \LaraZeus\BoltPro\Facades\GradeOptions::hidden() : [], 59 | ...Bolt::getHiddenCustomSchema('field', resolve(static::class)) ?? [], 60 | self::hiddenVisibility(), 61 | self::hiddenHtmlID(), 62 | self::hiddenHintOptions(), 63 | self::hiddenRequired(), 64 | self::hiddenColumnSpanFull(), 65 | self::hiddenHiddenLabel(), 66 | ]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Filament/Actions/ReplicateFormAction.php: -------------------------------------------------------------------------------- 1 | baseSetUp(); 23 | 24 | $this->icon(FilamentIcon::resolve('actions::replicate-action') ?? 'heroicon-m-square-2-stack') 25 | ->label(__('Replicate')) 26 | ->excludeAttributes(['name', 'slug', 'responses_exists', 'responses_count']) 27 | ->form([ 28 | TextInput::make('name.' . app()->getLocale()) 29 | ->required() 30 | ->maxLength(255) 31 | ->live(onBlur: true) 32 | ->label(__('Form Name')) 33 | ->afterStateUpdated(function (Set $set, $state) { 34 | $set('slug', Str::slug($state)); 35 | }), 36 | TextInput::make('slug') 37 | ->formatStateUsing(fn ($record) => $record->slug . '-' . rand(1, 99)) 38 | ->required() 39 | ->maxLength(255) 40 | ->rules(['alpha_dash']) 41 | ->unique(ignoreRecord: true) 42 | ->label(__('Form Slug')), 43 | ]) 44 | ->beforeReplicaSaved(function (ZeusForm $replica, ZeusForm $record, array $data): void { 45 | $repForm = $replica->fill($data); 46 | $repForm->save(); 47 | $formID = $repForm->id; 48 | $record->sections->each(function ($item) use ($formID) { 49 | $repSec = $item->replicate()->fill(['form_id' => $formID]); 50 | $repSec->save(); 51 | $sectionID = $repSec->id; 52 | $item->fields->each(function ($item) use ($sectionID) { 53 | $repField = $item->replicate()->fill(['section_id' => $sectionID]); 54 | $repField->save(); 55 | }); 56 | }); 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Filament/Actions/SetResponseStatus.php: -------------------------------------------------------------------------------- 1 | visible(function (Response $record): bool { 29 | return $record->form->extensions === null; 30 | }); 31 | 32 | $this->label(__('Set Status')); 33 | 34 | $this->icon('heroicon-o-tag'); 35 | 36 | $this->action(function (array $data): void { 37 | $this->record->status = $data['status']; 38 | $this->record->notes = $data['notes']; 39 | $this->record->save(); 40 | }); 41 | 42 | $this->form([ 43 | Select::make('status') 44 | ->label(__('status')) 45 | ->default(fn (Response $record) => $record->status) 46 | ->options(BoltPlugin::getModel('FormsStatus')::query()->pluck('label', 'key')) 47 | ->required(), 48 | Textarea::make('notes') 49 | ->default(fn (Response $record) => $record->notes) 50 | ->label(__('Notes')), 51 | ]); 52 | } 53 | 54 | public function mutateRecordDataUsing(?Closure $callback): static 55 | { 56 | $this->mutateRecordDataUsing = $callback; 57 | 58 | return $this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Filament/Exports/ResponseExporter.php: -------------------------------------------------------------------------------- 1 | getRecord(); 22 | // todo refactor with v4 23 | $userModel = BoltPlugin::getModel('User') ?? config('auth.providers.users.model'); 24 | $getUserModel = $userModel::getBoltUserFullNameAttribute(); 25 | $mainColumns = [ 26 | ExportColumn::make('user.' . $getUserModel) 27 | ->label(__('Name')) 28 | ->default(__('guest')), 29 | 30 | ExportColumn::make('status') 31 | ->label(__('status')), 32 | 33 | ExportColumn::make('notes') 34 | ->label(__('notes')), 35 | ]; 36 | 37 | /** 38 | * @var Field $field. 39 | */ 40 | foreach ($record->fields->sortBy('ordering') as $field) { 41 | $getFieldTableColumn = (new $field->type)->ExportColumn($field); 42 | 43 | if ($getFieldTableColumn !== null) { 44 | $mainColumns[] = $getFieldTableColumn; 45 | } 46 | } 47 | 48 | $mainColumns[] = ExportColumn::make('created_at') 49 | ->label(__('created at')); 50 | 51 | return $mainColumns; 52 | } 53 | 54 | /*public static function getOptionsFormComponents(): array 55 | { 56 | return [ 57 | TextInput::make('descriptionLimit') 58 | ->label('Limit the length of the description column content') 59 | ->integer(), 60 | ]; 61 | }*/ 62 | 63 | public static function getCompletedNotificationBody(Export $export): string 64 | { 65 | $body = 'Your response export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.'; 66 | 67 | if ($failedRowsCount = $export->getFailedRowsCount()) { 68 | $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.'; 69 | } 70 | 71 | return $body; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Filament/Resources/BoltResource.php: -------------------------------------------------------------------------------- 1 | getHiddenResources()) 16 | && parent::canViewAny(); 17 | } 18 | 19 | public static function getNavigationGroup(): ?string 20 | { 21 | return BoltPlugin::get()->getNavigationGroupLabel(); 22 | } 23 | 24 | public static function getGloballySearchableAttributes(): array 25 | { 26 | return BoltPlugin::get()->getGlobalAttributes(static::class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Filament/Resources/CategoryResource/Pages/CreateCategory.php: -------------------------------------------------------------------------------- 1 | paginated([1]) 32 | ->query(BoltPlugin::getModel('Response')::query()->where('form_id', $this->record->id)) 33 | ->columns([ 34 | ViewColumn::make('response') 35 | ->label(__('Browse Entries')) 36 | ->view('zeus::filament.resources.response-resource.pages.browse-entry'), 37 | ]) 38 | ->actions([ 39 | SetResponseStatus::make(), 40 | ], position: ActionsPosition::AfterContent) 41 | ->filters([ 42 | SelectFilter::make('status') 43 | ->options(BoltPlugin::getModel('FormsStatus')::query()->pluck('label', 'key')) 44 | ->label(__('Status')), 45 | ]); 46 | } 47 | 48 | public static function getNavigationLabel(): string 49 | { 50 | return __('Browse Entries'); 51 | } 52 | 53 | public function getTitle(): string 54 | { 55 | return __('Browse Entries'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Pages/CreateForm.php: -------------------------------------------------------------------------------- 1 | isFormActionsAreSticky(); 19 | } 20 | 21 | protected function getHeaderActions(): array 22 | { 23 | return [ 24 | Actions\LocaleSwitcher::make(), 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Pages/EditForm.php: -------------------------------------------------------------------------------- 1 | isFormActionsAreSticky(); 25 | } 26 | 27 | public function getTitle(): string | Htmlable 28 | { 29 | return __('Edit Form'); 30 | } 31 | 32 | public static function getNavigationLabel(): string 33 | { 34 | return __('Edit Form'); 35 | } 36 | 37 | protected function getHeaderActions(): array 38 | { 39 | return [ 40 | LocaleSwitcher::make(), 41 | Action::make('open') 42 | ->label(__('Open')) 43 | ->icon('heroicon-o-arrow-top-right-on-square') 44 | ->tooltip(__('open form')) 45 | ->color('warning') 46 | ->url(fn () => route(BoltPlugin::get()->getRouteNamePrefix() . 'bolt.form.show', $this->record)) 47 | ->openUrlInNewTab(), 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Pages/ListForms.php: -------------------------------------------------------------------------------- 1 | label(__('Open')) 25 | ->icon('heroicon-o-arrow-top-right-on-square') 26 | ->tooltip(__('open all forms')) 27 | ->color('warning') 28 | ->url(fn () => route(BoltPlugin::get()->getRouteNamePrefix() . 'bolt.forms.list')) 29 | ->openUrlInNewTab(), 30 | ]; 31 | 32 | if (Bolt::hasPro()) { 33 | // @phpstan-ignore-next-line 34 | $actions[] = \LaraZeus\BoltPro\Actions\PresetAction::make('new from preset') 35 | ->visible(config('zeus-bolt.show_presets')); 36 | } 37 | 38 | return $actions; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Pages/ViewForm.php: -------------------------------------------------------------------------------- 1 | label(__('Open')) 39 | ->icon('heroicon-o-arrow-top-right-on-square') 40 | ->tooltip(__('open form')) 41 | ->color('warning') 42 | ->url(fn () => route(BoltPlugin::get()->getRouteNamePrefix() . 'bolt.form.show', $this->record)) 43 | ->visible(fn (Form $record) => $record->extensions === null) 44 | ->openUrlInNewTab(), 45 | ]; 46 | } 47 | 48 | protected function getFooterWidgets(): array 49 | { 50 | $widgets = [ 51 | FormResource\Widgets\FormOverview::class, 52 | FormResource\Widgets\ResponsesPerMonth::class, 53 | FormResource\Widgets\ResponsesPerStatus::class, 54 | FormResource\Widgets\ResponsesPerFields::class, 55 | ]; 56 | 57 | if (Bolt::hasPro()) { 58 | // @phpstan-ignore-next-line 59 | $widgets[] = \LaraZeus\BoltPro\Widgets\ResponsesCharts::class; 60 | } 61 | 62 | return $widgets; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Pages/ViewResponse.php: -------------------------------------------------------------------------------- 1 | response = Response::find($this->responseID); 33 | static::authorizeResourceAccess(); 34 | } 35 | 36 | protected function getHeaderActions(): array 37 | { 38 | return [ 39 | Action::make('set-status') 40 | ->visible(function (): bool { 41 | return $this->response->form->extensions === null; 42 | }) 43 | ->label(__('Set Status')) 44 | ->icon('heroicon-o-tag') 45 | ->form([ 46 | Select::make('status') 47 | ->label(__('status')) 48 | ->default(fn () => $this->response->status) 49 | ->options(BoltPlugin::getModel('FormsStatus')::query()->pluck('label', 'key')) 50 | ->required(), 51 | Textarea::make('notes') 52 | ->default(fn () => $this->response->notes) 53 | ->label(__('Notes')), 54 | ]) 55 | ->action(function (array $data): void { 56 | $this->response->status = $data['status']; 57 | $this->response->notes = $data['notes']; 58 | $this->response->save(); 59 | }), 60 | ]; 61 | } 62 | 63 | public function getTitle(): string | Htmlable 64 | { 65 | return __('view response #') . $this->response->id; 66 | } 67 | 68 | public function getBreadcrumbs(): array 69 | { 70 | return [ 71 | FormResource::getUrl() => FormResource::getBreadcrumb(), 72 | FormResource::getUrl('view', ['record' => $this->record->slug]) => $this->record->name, 73 | FormResource::getUrl('report', ['record' => $this->record->slug]) => __('Entries Report'), 74 | __('view the entry'), 75 | ]; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Widgets/FormOverview.php: -------------------------------------------------------------------------------- 1 | record->fields()->count())->label(__('Fields')), 17 | Stat::make('responses', $this->record->responses()->count())->label(__('Responses')), 18 | Stat::make('fields_responses', $this->record->fieldsResponses()->count())->label(__('Fields Responses')), 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Widgets/ResponsesPerFields.php: -------------------------------------------------------------------------------- 1 | 1, 17 | 'md' => 2, 18 | ]; 19 | 20 | protected static ?array $options = [ 21 | 'scales' => [ 22 | 'y' => [ 23 | 'grid' => [ 24 | 'display' => false, 25 | ], 26 | 'ticks' => [ 27 | 'display' => false, 28 | ], 29 | ], 30 | 'x' => [ 31 | 'grid' => [ 32 | 'display' => false, 33 | ], 34 | 'ticks' => [ 35 | 'display' => false, 36 | ], 37 | ], 38 | ], 39 | ]; 40 | 41 | protected function getType(): string 42 | { 43 | return 'pie'; 44 | } 45 | 46 | public function getHeading(): string 47 | { 48 | return __('Responses Entries'); 49 | } 50 | 51 | protected function getData(): array 52 | { 53 | $dataset = []; 54 | 55 | $form = BoltPlugin::getModel('Form')::query() 56 | ->with(['fields', 'fieldsResponses']) 57 | ->where('id', $this->record->id) 58 | ->first(); 59 | 60 | $fields = $form->fields; 61 | foreach ($fields as $field) { 62 | $dataset[] = $form->fieldsResponses 63 | ->where('field_id', $field->id) 64 | ->count(); 65 | } 66 | 67 | return [ 68 | 'datasets' => [ 69 | [ 70 | 'label' => __('entries per month'), 71 | 'data' => $dataset, 72 | 'backgroundColor' => '#8A8AFF', 73 | 'borderColor' => '#ffffff', 74 | ], 75 | ], 76 | 77 | 'labels' => $fields->pluck('name'), 78 | ]; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Widgets/ResponsesPerMonth.php: -------------------------------------------------------------------------------- 1 | __('Per Day'), 30 | 'per_month' => __('Per month'), 31 | 'per_year' => __('Per year'), 32 | ]; 33 | } 34 | 35 | public function getHeading(): string 36 | { 37 | return __('Responses Count'); 38 | } 39 | 40 | protected function getData(): array 41 | { 42 | $label = null; 43 | 44 | $data = Trend::model(BoltPlugin::getModel('Response')) 45 | ->between( 46 | start: now()->startOfYear(), 47 | end: now()->endOfYear(), 48 | ); 49 | 50 | if ($this->filter == 'per_day') { 51 | $label = __('Per day'); 52 | $data = $data->perDay(); 53 | } elseif ($this->filter == 'per_month') { 54 | $label = __('Per month'); 55 | $data = $data->perMonth(); 56 | } elseif ($this->filter == 'per_year') { 57 | $label = __('Per year'); 58 | $data = $data->perYear(); 59 | } 60 | 61 | $data = $data->count(); 62 | 63 | return [ 64 | 'datasets' => [ 65 | [ 66 | 'label' => $label, 67 | 'data' => $data->map(fn (TrendValue $value) => $value->aggregate), 68 | ], 69 | ], 70 | 'labels' => $data->map(fn (TrendValue $value) => $value->date), 71 | ]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Filament/Resources/FormResource/Widgets/ResponsesPerStatus.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'y' => [ 18 | 'grid' => [ 19 | 'display' => false, 20 | ], 21 | 'ticks' => [ 22 | 'display' => false, 23 | ], 24 | ], 25 | 'x' => [ 26 | 'grid' => [ 27 | 'display' => false, 28 | ], 29 | 'ticks' => [ 30 | 'display' => false, 31 | ], 32 | ], 33 | ], 34 | ]; 35 | 36 | protected int | string | array $columnSpan = [ 37 | 'lg' => 1, 38 | 'md' => 2, 39 | ]; 40 | 41 | public function getHeading(): string 42 | { 43 | return __('Responses Status'); 44 | } 45 | 46 | protected function getData(): array 47 | { 48 | $dataset = []; 49 | $statuses = BoltPlugin::getModel('FormsStatus')::get(); 50 | 51 | $form = BoltPlugin::getModel('Form')::query() 52 | ->with(['responses']) 53 | ->where('id', $this->record->id) 54 | ->first(); 55 | 56 | foreach ($statuses as $status) { 57 | $dataset[] = $form->responses 58 | ->where('status', $status->key) 59 | ->count(); 60 | } 61 | 62 | return [ 63 | 'datasets' => [ 64 | [ 65 | 'label' => __('entries per month'), 66 | 'data' => $dataset, 67 | 'backgroundColor' => $statuses->pluck('chartColor'), 68 | 'borderColor' => '#ffffff', 69 | ], 70 | ], 71 | 72 | 'labels' => $statuses->pluck('label'), 73 | ]; 74 | } 75 | 76 | protected function getType(): string 77 | { 78 | return 'pie'; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Livewire/ListEntries.php: -------------------------------------------------------------------------------- 1 | query( 26 | config('zeus-bolt.models.Response')::query()->where('user_id', auth()->user()->id) 27 | ) 28 | ->contentGrid([ 29 | 'sm' => 1, 30 | 'md' => 2, 31 | 'xl' => 3, 32 | ]) 33 | ->columns([ 34 | Split::make([ 35 | TextColumn::make('status') 36 | ->badge() 37 | ->label(__('status')) 38 | ->colors(config('zeus-bolt.models.FormsStatus')::pluck('key', 'color')->toArray()) 39 | ->icons(config('zeus-bolt.models.FormsStatus')::pluck('key', 'icon')->toArray()) 40 | ->grow(false), 41 | TextColumn::make('form.name') 42 | ->searchable('name') 43 | ->label(__('Form Name')) 44 | ->url(fn (Response $record): string => route('bolt.entry.show', $record)), 45 | ]), 46 | Stack::make([ 47 | TextColumn::make('updated_at')->label(__('Updated At'))->dateTime(), 48 | ]), 49 | ]); 50 | } 51 | 52 | public function render(): View 53 | { 54 | seo() 55 | ->title(__('My Responses') . ' ' . config('zeus.site_title', 'Laravel')) 56 | ->description(__('My Responses') . ' ' . config('zeus.site_description', 'Laravel')) 57 | ->site(config('zeus.site_title', 'Laravel')) 58 | ->rawTag('favicon', '') 59 | ->rawTag('') 60 | ->withUrl() 61 | ->twitter(); 62 | 63 | return view(app('boltTheme') . '.list-entries') 64 | ->layout(config('zeus.layout')); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Livewire/ListForms.php: -------------------------------------------------------------------------------- 1 | site(config('zeus.site_title', 'Laravel')) 14 | ->title(__('Forms') . ' - ' . config('zeus.site_title')) 15 | ->description(__('Forms') . ' - ' . config('zeus.site_description') . ' ' . config('zeus.site_title')) 16 | ->rawTag('favicon', '') 17 | ->rawTag('') 18 | ->withUrl() 19 | ->twitter(); 20 | 21 | return view(app('boltTheme') . '.list-forms') 22 | ->with( 23 | 'categories', 24 | config('zeus-bolt.models.Category')::query() 25 | ->whereHas('forms', function ($query) { 26 | $query->whereNull('extensions'); 27 | }) 28 | ->where('is_active', 1) 29 | ->orderBy('ordering') 30 | ->get() 31 | ) 32 | ->layout(config('zeus.layout')); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Livewire/ShowEntry.php: -------------------------------------------------------------------------------- 1 | response = config('zeus-bolt.models.Response')::with('user') 19 | ->where('user_id', auth()->user()->id) 20 | ->where('id', $responseID) 21 | ->firstOrFail(); 22 | } 23 | 24 | public function render(): View 25 | { 26 | seo() 27 | ->title(__('Show entry') . ' #' . $this->response->id . ' - ' . config('zeus.site_title', 'Laravel')) 28 | ->description(__('Show entry') . ' - ' . config('zeus.site_description', 'Laravel')) 29 | ->site(config('zeus.site_title', 'Laravel')) 30 | ->rawTag('favicon', '') 31 | ->rawTag('') 32 | ->withUrl() 33 | ->twitter(); 34 | 35 | return view(app('boltTheme') . '.show-entry') 36 | ->with('response', $this->response) 37 | ->layout(config('zeus.layout')); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Mail/FormSubmission.php: -------------------------------------------------------------------------------- 1 | form = $form; 30 | $this->response = $response; 31 | } 32 | 33 | /** 34 | * Get the message envelope. 35 | */ 36 | public function envelope(): Envelope 37 | { 38 | return new Envelope( 39 | subject: __('New Submission in') . ' ' . $this->form->name, 40 | ); 41 | } 42 | 43 | /** 44 | * Get the message content definition. 45 | */ 46 | public function content(): Content 47 | { 48 | return new Content( 49 | markdown: 'zeus::emails.form-submission', 50 | with: [ 51 | 'url' => url(Filament::getDefaultPanel()->getPath() . '/responses/' . $this->response->id), 52 | ], 53 | ); 54 | } 55 | 56 | /** 57 | * Get the attachments for the message. 58 | * 59 | * @return array 60 | */ 61 | public function attachments(): array 62 | { 63 | return []; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Models/Category.php: -------------------------------------------------------------------------------- 1 | hasMany(config('zeus-bolt.models.Form')); 45 | } 46 | 47 | /** 48 | * @return Attribute 49 | */ 50 | protected function logoUrl(): Attribute 51 | { 52 | return Attribute::make( 53 | get: fn () => Storage::disk(config('zeus-bolt.uploadDisk'))->url($this->logo), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Models/Collection.php: -------------------------------------------------------------------------------- 1 | 'collection', 27 | ]; 28 | 29 | public function getTable(): string 30 | { 31 | return config('zeus-bolt.table-prefix') . 'collections'; 32 | } 33 | 34 | public function getValuesListAttribute(): ?string 35 | { 36 | $allValues = collect($this->values); 37 | 38 | if ($allValues->isNotEmpty()) { 39 | return $allValues 40 | ->take(5) 41 | ->map(function ($item) { 42 | return $item['itemValue'] ?? null; 43 | }) 44 | ->join(','); 45 | } 46 | 47 | return null; 48 | } 49 | 50 | protected static function newFactory(): Factory 51 | { 52 | return CollectionFactory::new(); 53 | } 54 | 55 | public function user(): BelongsTo 56 | { 57 | return $this->belongsTo(config('zeus-bolt.models.User') ?? config('auth.providers.users.model')); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Models/Concerns/BelongToBolt.php: -------------------------------------------------------------------------------- 1 | 'array', 34 | ]; 35 | 36 | public function getTable(): string 37 | { 38 | return config('zeus-bolt.table-prefix') . 'fields'; 39 | } 40 | 41 | protected static function booted(): void 42 | { 43 | static::deleting(function (Field $field) { 44 | if ($field->isForceDeleting()) { 45 | // @phpstan-ignore-next-line 46 | $field->fieldResponses()->withTrashed()->get()->each(function ($item) { 47 | $item->forceDelete(); 48 | }); 49 | } else { 50 | $field->fieldResponses->each(function ($item) { 51 | $item->delete(); 52 | }); 53 | } 54 | }); 55 | } 56 | 57 | protected static function newFactory(): Factory 58 | { 59 | return FieldFactory::new(); 60 | } 61 | 62 | public function section(): BelongsTo 63 | { 64 | return $this->belongsTo(config('zeus-bolt.models.Section')); 65 | } 66 | 67 | public function fieldResponses(): HasMany 68 | { 69 | return $this->hasMany(config('zeus-bolt.models.FieldResponse')); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Models/FieldResponse.php: -------------------------------------------------------------------------------- 1 | belongsTo(config('zeus-bolt.models.Field')); 42 | } 43 | 44 | public function parentResponse(): BelongsTo 45 | { 46 | return $this->belongsTo(config('zeus-bolt.models.Response'), 'response_id', 'id'); 47 | } 48 | 49 | public function form(): BelongsTo 50 | { 51 | return $this->belongsTo(config('zeus-bolt.models.Form')); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Models/FormsStatus.php: -------------------------------------------------------------------------------- 1 | 'NEW', 25 | 'label' => __('New'), 26 | 'description' => 'used when a new form created by the user or an employee', 27 | 'color' => 'success', 28 | 'chartColor' => '#21C55D', 29 | 'icon' => 'heroicon-o-document', 30 | 'class' => 'px-2 py-0.5 text-xs rounded-xl text-success-700 bg-success-500/10', 31 | ], 32 | [ 33 | 'key' => 'OPEN', 34 | 'label' => __('Open'), 35 | 'description' => 'used when a new form created by the user or an employee', 36 | 'color' => 'success', 37 | 'chartColor' => '#21C55D', 38 | 'icon' => 'heroicon-o-document', 39 | 'class' => 'px-2 py-0.5 text-xs rounded-xl text-success-700 bg-success-500/10', 40 | ], 41 | [ 42 | 'key' => 'CLOSE', 43 | 'label' => __('closed'), 44 | 'description' => 'used when a new form created by the user or an employee', 45 | 'color' => 'danger', 46 | 'chartColor' => '#EF4444', 47 | 'icon' => 'heroicon-o-x-circle', 48 | 'class' => 'px-2 py-0.5 text-xs rounded-xl text-danger-700 bg-danger-500/10', 49 | ], 50 | ]; 51 | } 52 | 53 | protected function sushiShouldCache(): bool 54 | { 55 | return ! app()->isLocal(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Models/Response.php: -------------------------------------------------------------------------------- 1 | form, 'canDeleteResponse', ['response' => $response]); 45 | 46 | if ($canDelete === null) { 47 | $canDelete = true; 48 | } 49 | 50 | if (! $canDelete) { 51 | Notification::make() 52 | ->title(__('Can\'t delete a form linked to an Extensions')) 53 | ->danger() 54 | ->send(); 55 | 56 | return false; 57 | } 58 | 59 | if ($response->isForceDeleting()) { 60 | // @phpstan-ignore-next-line 61 | $response->fieldsResponses()->withTrashed()->get()->each(fn ($item) => $item->forceDelete()); 62 | } else { 63 | $response->fieldsResponses->each(fn ($item) => $item->delete()); 64 | } 65 | 66 | return true; 67 | }); 68 | } 69 | 70 | protected static function newFactory(): Factory 71 | { 72 | return ResponseFactory::new(); 73 | } 74 | 75 | public function fieldsResponses(): HasMany 76 | { 77 | return $this->hasMany(config('zeus-bolt.models.FieldResponse')); 78 | } 79 | 80 | public function user(): BelongsTo 81 | { 82 | return $this->belongsTo(config('zeus-bolt.models.User') ?? config('auth.providers.users.model')); 83 | } 84 | 85 | public function form(): BelongsTo 86 | { 87 | return $this->belongsTo(config('zeus-bolt.models.Form')); 88 | } 89 | 90 | /** 91 | * get status detail. 92 | */ 93 | public function statusDetails(): array 94 | { 95 | $getStatues = config('zeus-bolt.models.FormsStatus')::where('key', $this->status)->first(); 96 | 97 | return [ 98 | 'class' => $getStatues->class ?? '', 99 | 'icon' => $getStatues->icon ?? 'heroicon-s-document', 100 | 'label' => $getStatues->label ?? $this->status, 101 | 'key' => $getStatues->key ?? '', 102 | 'color' => $getStatues->color ?? '', 103 | ]; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Models/Section.php: -------------------------------------------------------------------------------- 1 | 'array', 36 | ]; 37 | 38 | public function getTable(): string 39 | { 40 | return config('zeus-bolt.table-prefix') . 'sections'; 41 | } 42 | 43 | protected static function booted(): void 44 | { 45 | parent::booted(); 46 | static::deleting(function (Section $section) { 47 | if ($section->isForceDeleting()) { 48 | // @phpstan-ignore-next-line 49 | $section->fields()->withTrashed()->get()->each(function ($item) { 50 | $item->fieldResponses()->withTrashed()->get()->each(function ($item) { 51 | $item->forceDelete(); 52 | }); 53 | $item->forceDelete(); 54 | }); 55 | } else { 56 | $section->fields->each(function ($item) { 57 | $item->fieldResponses->each(function ($item) { 58 | $item->delete(); 59 | }); 60 | $item->delete(); 61 | }); 62 | } 63 | }); 64 | } 65 | 66 | protected static function newFactory(): Factory 67 | { 68 | return SectionFactory::new(); 69 | } 70 | 71 | public function fields(): HasMany 72 | { 73 | return $this->hasMany(config('zeus-bolt.models.Field'), 'section_id', 'id'); 74 | } 75 | 76 | public function form(): BelongsTo 77 | { 78 | return $this->belongsTo(config('zeus-bolt.models.Form')); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /stubs/ZeusDataSources.stub: -------------------------------------------------------------------------------- 1 | default() 27 | ->id('admin') 28 | ->login() 29 | ->plugins([ 30 | BoltPlugin::make(), 31 | SpatieLaravelTranslatablePlugin::make() 32 | ->defaultLocales(['en']), 33 | ]) 34 | ->resources([ 35 | CategoryResource::class, 36 | ]) 37 | ->registration() 38 | ->passwordReset() 39 | ->emailVerification() 40 | ->middleware([ 41 | EncryptCookies::class, 42 | AddQueuedCookiesToResponse::class, 43 | StartSession::class, 44 | AuthenticateSession::class, 45 | ShareErrorsFromSession::class, 46 | VerifyCsrfToken::class, 47 | SubstituteBindings::class, 48 | DisableBladeIconComponents::class, 49 | DispatchServingFilamentEvent::class, 50 | ]) 51 | ->authMiddleware([ 52 | Authenticate::class, 53 | ]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/ArchTest.php: -------------------------------------------------------------------------------- 1 | expect(['dd', 'dump', 'ray']) 5 | ->each->not->toBeUsed(); 6 | -------------------------------------------------------------------------------- /tests/Models/User.php: -------------------------------------------------------------------------------- 1 | in(__DIR__); 7 | -------------------------------------------------------------------------------- /tests/ResourcesTest.php: -------------------------------------------------------------------------------- 1 | assertSuccessful(); 12 | }); 13 | 14 | it('can render collection list', function () { 15 | get(CollectionResource::getUrl()) 16 | ->assertSuccessful(); 17 | }); 18 | 19 | it('can render form list', function () { 20 | get(FormResource::getUrl()) 21 | ->assertSuccessful(); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | actingAs( 38 | User::create(['email' => 'admin@domain.com', 'name' => 'Admin', 'password' => 'password']) 39 | ); 40 | } 41 | 42 | protected function defineDatabaseMigrations(): void 43 | { 44 | $this->loadMigrationsFrom(__DIR__ . '/migrations'); 45 | } 46 | 47 | protected function getPackageProviders($app): array 48 | { 49 | return [ 50 | ActionsServiceProvider::class, 51 | BladeCaptureDirectiveServiceProvider::class, 52 | BladeHeroiconsServiceProvider::class, 53 | BladeIconsServiceProvider::class, 54 | FilamentServiceProvider::class, 55 | FormsServiceProvider::class, 56 | InfolistsServiceProvider::class, 57 | LivewireServiceProvider::class, 58 | NotificationsServiceProvider::class, 59 | SpatieLaravelTranslatablePluginServiceProvider::class, 60 | SupportServiceProvider::class, 61 | TablesServiceProvider::class, 62 | WidgetsServiceProvider::class, 63 | BladeTablerIconsServiceProvider::class, 64 | AdminPanelProvider::class, 65 | CoreServiceProvider::class, 66 | BoltServiceProvider::class, 67 | SEOServiceProvider::class, 68 | FilamentIconPickerServiceProvider::class, 69 | ]; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/migrations/001_create_categories_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->text('name'); 19 | $table->integer('ordering')->default(1); 20 | $table->boolean('is_active')->default(1); 21 | $table->text('description')->nullable(); 22 | $table->string('slug'); 23 | $table->string('logo')->nullable(); 24 | $table->timestamps(); 25 | $table->softDeletes(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists(config('zeus-bolt.table-prefix') . 'categories'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /tests/migrations/002_create_collections_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->text('name'); 19 | $table->longText('values')->nullable(); 20 | $table->timestamps(); 21 | $table->softDeletes(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists(config('zeus-bolt.table-prefix') . 'collections'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /tests/migrations/003_create_forms_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained('users'); 19 | $table->foreignId('category_id')->nullable()->constrained(config('zeus-bolt.table-prefix') . 'categories'); 20 | $table->text('name'); 21 | $table->text('description')->nullable(); 22 | $table->string('slug'); 23 | $table->integer('ordering')->default(1); 24 | $table->boolean('is_active'); 25 | $table->longText('details')->nullable(); 26 | $table->longText('options')->nullable(); 27 | $table->dateTime('start_date')->nullable(); 28 | $table->dateTime('end_date')->nullable(); 29 | $table->text('extensions')->nullable(); 30 | $table->timestamps(); 31 | $table->softDeletes(); 32 | }); 33 | } 34 | 35 | /** 36 | * Reverse the migrations. 37 | * 38 | * @return void 39 | */ 40 | public function down() 41 | { 42 | Schema::dropIfExists(config('zeus-bolt.table-prefix') . 'forms'); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /tests/migrations/004_create_sections_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('form_id')->constrained(config('zeus-bolt.table-prefix') . 'forms'); 19 | $table->text('name')->nullable(); 20 | $table->integer('ordering')->default(1); 21 | $table->integer('columns')->default(1); 22 | $table->text('description')->nullable(); 23 | $table->string('icon')->nullable(); 24 | $table->boolean('aside')->default(0); 25 | $table->boolean('borderless')->default(0); 26 | $table->boolean('compact')->default(0); 27 | $table->text('options')->nullable(); 28 | $table->timestamps(); 29 | $table->softDeletes(); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | * 36 | * @return void 37 | */ 38 | public function down() 39 | { 40 | Schema::dropIfExists(config('zeus-bolt.table-prefix') . 'sections'); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /tests/migrations/005_create_fields_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('section_id')->constrained(config('zeus-bolt.table-prefix') . 'sections'); 19 | $table->text('name'); 20 | $table->text('description')->nullable(); 21 | $table->string('type'); 22 | $table->integer('ordering')->default(1); 23 | $table->text('options')->nullable(); 24 | $table->timestamps(); 25 | $table->softDeletes(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists(config('zeus-bolt.table-prefix') . 'fields'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /tests/migrations/006_create_responses_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('form_id')->constrained(config('zeus-bolt.table-prefix') . 'forms'); 19 | $table->foreignId('user_id')->nullable()->constrained('users'); 20 | $table->string('status')->default('NEW'); 21 | $table->text('notes')->nullable(); 22 | $table->integer('extension_item_id')->nullable(); 23 | $table->timestamps(); 24 | $table->softDeletes(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists(config('zeus-bolt.table-prefix') . 'responses'); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /tests/migrations/007_create_field_responses_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('form_id')->constrained(config('zeus-bolt.table-prefix') . 'forms'); 19 | $table->foreignId('field_id')->constrained(config('zeus-bolt.table-prefix') . 'fields'); 20 | $table->foreignId('response_id')->constrained(config('zeus-bolt.table-prefix') . 'responses'); 21 | $table->longText('response'); 22 | $table->timestamps(); 23 | $table->softDeletes(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists(config('zeus-bolt.table-prefix') . 'field_responses'); 35 | } 36 | }; 37 | --------------------------------------------------------------------------------