├── .github ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug.yml │ └── config.yml ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── dependabot-auto-merge.yml │ ├── fix-php-code-styling.yml │ └── tests.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── 3x1io-tomato-docs.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── arts ├── .gitkeep ├── 3x1io-tomato-docs.jpg ├── create-document.png ├── create-template.png ├── document-action.png ├── document-relation.png ├── documents-filters.png ├── documents.png ├── edit-template-icon.png ├── edit-template-vars.png ├── edit-template.png ├── generate-document.png ├── generate-notification.png ├── print-document.png └── templates.png ├── composer.json ├── composer.lock ├── config ├── .gitkeep └── filament-docs.php ├── database └── migrations │ ├── .gitkeep │ ├── 2024_02_29_113840_create_document_templates_table.php │ ├── 2024_02_29_113841_create_document_template_metas_table.php │ └── 2024_02_29_124019_create_documents_table.php ├── module.json ├── phpunit.xml ├── pint.json ├── publish └── public │ └── css │ └── filament-docs.css ├── resources ├── js │ └── .gitkeep ├── lang │ ├── .gitkeep │ ├── ar │ │ └── messages.php │ └── en │ │ └── messages.php └── views │ ├── .gitkeep │ ├── layout.blade.php │ └── print.blade.php ├── src ├── Console │ ├── .gitkeep │ └── FilamentDocsInstall.php ├── Facades │ └── FilamentDocs.php ├── Filament │ ├── Actions │ │ ├── DocumentAction.php │ │ ├── Notifications │ │ │ └── PrintAction.php │ │ ├── PrintAction.php │ │ └── Table │ │ │ └── PrintAction.php │ ├── RelationManager │ │ └── DocumentRelationManager.php │ └── Resources │ │ ├── DocumentResource.php │ │ ├── DocumentResource │ │ └── Pages │ │ │ ├── CreateDocument.php │ │ │ ├── EditDocument.php │ │ │ ├── ListDocuments.php │ │ │ └── PrintDocument.php │ │ ├── DocumentTemplateResource.php │ │ └── DocumentTemplateResource │ │ └── Pages │ │ ├── CreateDocumentTemplate.php │ │ ├── EditDocumentTemplate.php │ │ └── ListDocumentTemplates.php ├── FilamentDocsPlugin.php ├── FilamentDocsServiceProvider.php ├── Models │ ├── .gitkeep │ ├── Document.php │ ├── DocumentTemplate.php │ └── DocumentTemplateVar.php ├── Services │ ├── Contracts │ │ └── DocsVar.php │ └── FilamentDocsServices.php └── Traits │ └── InteractsWithDocs.php ├── testbench.yaml └── tests ├── Pest.php ├── database ├── database.sqlite └── factories │ ├── DocumentFactory.php │ ├── DocumentTemplateFactory.php │ ├── DocumentTemplateVarFactory.php │ └── UserFactory.php └── src ├── AdminPanelProvider.php ├── DebugTest.php ├── DocumentResourceTest.php ├── Models ├── Document.php ├── DocumentTemplate.php ├── DocumentTemplateVar.php └── User.php ├── PluginTest.php └── TestCase.php /.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 skills, 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: [3x1io] 2 | -------------------------------------------------------------------------------- /.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/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/tomatophp/filament-types/discussions/new?category=q-a 5 | about: Ask the community for help 6 | - name: Request a feature 7 | url: https://github.com/tomatophp/filament-types/discussions/new?category=ideas 8 | about: Share ideas for new features 9 | - name: Report a security issue 10 | url: https://github.com/tomatophp/filament-types/security/policy 11 | about: Learn how to notify us for sensitive bugs 12 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you discover any security related issues, please email info@3x1.io 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" 13 | -------------------------------------------------------------------------------- /.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}} 33 | -------------------------------------------------------------------------------- /.github/workflows/fix-php-code-styling.yml: -------------------------------------------------------------------------------- 1 | name: 'PHP Code Styling' 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | paths: 9 | - '**.php' 10 | 11 | permissions: 12 | contents: write 13 | 14 | jobs: 15 | lint: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | with: 21 | ref: ${{ github.head_ref }} 22 | 23 | - name: Fix PHP code style issues 24 | uses: aglipanci/laravel-pint-action@v2 25 | 26 | - name: Commit changes 27 | uses: stefanzweifel/git-auto-commit-action@v5 28 | with: 29 | commit_message: "Format Code" 30 | commit_user_name: 'GitHub Actions' 31 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: "Tests" 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | paths: 9 | - '**.php' 10 | pull_request: 11 | types: 12 | - opened 13 | - synchronize 14 | branches: 15 | - master 16 | paths: 17 | - '**.php' 18 | - '.github/workflows/tests.yml' 19 | - 'phpunit.xml.dist' 20 | - 'composer.json' 21 | - 'composer.lock' 22 | 23 | jobs: 24 | test: 25 | runs-on: ${{ matrix.os }} 26 | strategy: 27 | fail-fast: true 28 | matrix: 29 | os: [ubuntu-latest] 30 | php: [8.4, 8.3, 8.2] 31 | laravel: [12.*, 11.*] 32 | stability: [prefer-stable] 33 | include: 34 | - laravel: 12.* 35 | testbench: 10.* 36 | carbon: 3.* 37 | collision: 8.* 38 | - laravel: 11.* 39 | testbench: 9.* 40 | carbon: 3.* 41 | collision: 8.* 42 | exclude: 43 | - laravel: 11.* 44 | php: 8.1 45 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} 46 | steps: 47 | - name: Checkout Code 48 | uses: actions/checkout@v4 49 | 50 | - name: Cache Dependencies 51 | uses: actions/cache@v4 52 | with: 53 | path: ~/.composer/cache/files 54 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 55 | 56 | - name: Setup PHP 57 | uses: shivammathur/setup-php@v2 58 | with: 59 | php-version: ${{ matrix.php }} 60 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 61 | coverage: none 62 | 63 | - name: Install Dependencies 64 | run: | 65 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 66 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 67 | 68 | - name: Install Dependencies 69 | run: | 70 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.carbon }}" "nunomaduro/collision:${{ matrix.collision }}" --no-interaction --no-update 71 | composer update --${{ matrix.stability }} --prefer-dist --no-interaction 72 | composer db 73 | 74 | - name: Execute tests 75 | run: vendor/bin/pest 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /storage/pail 8 | /vendor 9 | .env 10 | .env.backup 11 | .env.production 12 | .phpactor.json 13 | .phpunit.result.cache 14 | .DS_Store 15 | Homestead.json 16 | Homestead.yaml 17 | auth.json 18 | npm-debug.log 19 | yarn-error.log 20 | /.fleet 21 | /.idea 22 | /.vscode 23 | /.zed 24 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | notPath('bootstrap/*') 5 | ->notPath('storage/*') 6 | ->notPath('resources/view/mail/*') 7 | ->in([ 8 | __DIR__ . '/src', 9 | __DIR__ . '/tests', 10 | ]) 11 | ->name('*.php') 12 | ->notName('*.blade.php') 13 | ->ignoreDotFiles(true) 14 | ->ignoreVCS(true); 15 | 16 | return (new PhpCsFixer\Config()) 17 | ->setRules([ 18 | '@PSR2' => true, 19 | 'array_syntax' => ['syntax' => 'short'], 20 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 21 | 'no_unused_imports' => true, 22 | 'not_operator_with_successor_space' => true, 23 | 'trailing_comma_in_multiline' => true, 24 | 'phpdoc_scalar' => true, 25 | 'unary_operator_spaces' => true, 26 | 'binary_operator_spaces' => true, 27 | 'blank_line_before_statement' => [ 28 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 29 | ], 30 | 'phpdoc_single_line_var_spacing' => true, 31 | 'phpdoc_var_without_name' => true, 32 | 'method_argument_space' => [ 33 | 'on_multiline' => 'ensure_fully_multiline', 34 | 'keep_multiple_spaces_after_comma' => true, 35 | ] 36 | ]) 37 | ->setFinder($finder); 38 | -------------------------------------------------------------------------------- /3x1io-tomato-docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documents Editor 3 | slug: 3x1io-tomato-docs 4 | author_slug: 3x1io 5 | categories: [developer-tool] 6 | description: Manage your documents and contracts all in one place with template builder 7 | discord_url: 8 | docs_url: https://raw.githubusercontent.com/tomatophp/filament-docs/master/README.md 9 | github_repository: tomatophp/filament-docs 10 | has_dark_theme: true 11 | has_translations: true 12 | versions: [3] 13 | publish_date: 2024-10-14 14 | --- 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/CHANGELOG.md -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Screenshot](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/3x1io-tomato-docs.jpg) 2 | 3 | # Filament Documents Editor 4 | 5 | [![Dependabot Updates](https://github.com/tomatophp/filament-docs/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/tomatophp/filament-docs/actions/workflows/dependabot/dependabot-updates) 6 | [![PHP Code Styling](https://github.com/tomatophp/filament-docs/actions/workflows/fix-php-code-styling.yml/badge.svg)](https://github.com/tomatophp/filament-docs/actions/workflows/fix-php-code-styling.yml) 7 | [![Tests](https://github.com/tomatophp/filament-docs/actions/workflows/tests.yml/badge.svg)](https://github.com/tomatophp/filament-docs/actions/workflows/tests.yml) 8 | [![Latest Stable Version](https://poser.pugx.org/tomatophp/filament-docs/version.svg)](https://packagist.org/packages/tomatophp/filament-docs) 9 | [![License](https://poser.pugx.org/tomatophp/filament-docs/license.svg)](https://packagist.org/packages/tomatophp/filament-docs) 10 | [![Downloads](https://poser.pugx.org/tomatophp/filament-docs/d/total.svg)](https://packagist.org/packages/tomatophp/filament-docs) 11 | 12 | Manage your documents and contracts all in one place with template builder 13 | 14 | ## Features 15 | 16 | - [x] Generate Documents From Template 17 | - [x] Build Template using Tiptop Editor 18 | - [x] Add Custom Vars By Facade 19 | - [x] Generate Documents Action 20 | - [x] Documents Filter By Template 21 | - [x] Print Document or Export as PDF 22 | - [x] Documents Relation Manager 23 | - [x] Custom Print Header & Footer 24 | - [x] Custom Print CSS 25 | 26 | ## Screenshots 27 | 28 | ![Documents](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/documents.png) 29 | ![Create Document](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/create-document.png) 30 | ![Documents Filters](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/documents-filters.png) 31 | ![Print Document](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/print-document.png) 32 | ![Templates](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/templates.png) 33 | ![Create Template](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/create-template.png) 34 | ![Edit Template](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/edit-template.png) 35 | ![Edit Template Vars](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/edit-template-vars.png) 36 | ![Edit Template Icons](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/edit-template-icon.png) 37 | ![Document Action](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/document-action.png) 38 | ![Document Relation Manager](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/document-relation.png) 39 | ![Generate Document](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/generate-document.png) 40 | ![Generate Document Notification](https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/generate-notification.png) 41 | 42 | ## Installation 43 | 44 | ```bash 45 | composer require tomatophp/filament-docs 46 | ``` 47 | after install your package please run this command 48 | 49 | ```bash 50 | php artisan filament-docs:install 51 | ``` 52 | 53 | if you are not using this package as a plugin please register the plugin on `/app/Providers/Filament/AdminPanelProvider.php` 54 | 55 | ```php 56 | ->plugin( 57 | \TomatoPHP\FilamentDocs\FilamentDocsPlugin::make() 58 | ) 59 | ``` 60 | 61 | ## Using 62 | 63 | you can add the action to any table like this 64 | 65 | ```php 66 | use TomatoPHP\FilamentDocs\Filament\Actions\DocumentAction; 67 | 68 | DocumentAction::make() 69 | ->vars(fn($record) => [ 70 | DocsVar::make('$ACCOUNT_NAME') 71 | ->value($record->name), 72 | DocsVar::make('$ACCOUNT_EMAIL') 73 | ->value($record->email), 74 | DocsVar::make('$ACCOUNT_PHONE') 75 | ->value($record->phone) 76 | ]) 77 | ``` 78 | 79 | and then you can use `$ACCOUNT_NAME` in your template 80 | 81 | if you like to add a Global Var you can use Facade class like this 82 | 83 | ```php 84 | use TomatoPHP\FilamentDocs\Facades\FilamentDocs; 85 | use TomatoPHP\FilamentDocs\Services\Contracts\DocsVar; 86 | 87 | public function boot() 88 | { 89 | FilamentDocs::register([ 90 | DocsVar::make('$POST_TITLE') 91 | ->label('Post Title') 92 | ->model(Post::class) 93 | ->column('title'), 94 | DocsVar::make('$POST_TYPE') 95 | ->label('Post Type') 96 | ->model(Post::class) 97 | ->column('type'), 98 | DocsVar::make('$SELECTED_TIME') 99 | ->label('SELECTED TIME') 100 | ->value(fn () => Carbon::now()->subDays(10)->translatedFormat('D-M-Y')), 101 | ]); 102 | } 103 | ``` 104 | 105 | as you can see you can use data from selected table or from a static function 106 | 107 | 108 | ## Add Fixed Header & Footer to Document Print 109 | 110 | 111 | if you like to add a fixed header and footer to your document print you can use this method on your `AppServiceProvider.php` file 112 | 113 | ```php 114 | use TomatoPHP\FilamentDocs\Facades\FilamentDocs; 115 | 116 | public function boot() { 117 | FilamentDocs::header('filament.header'); 118 | FilamentDocs::footer('filament.footer'); 119 | } 120 | ``` 121 | 122 | ## Custom CSS on Document Print 123 | 124 | if you like to add a custom css to your document print you can use this method on your `AppServiceProvider.php` file 125 | 126 | ```php 127 | use TomatoPHP\FilamentDocs\Facades\FilamentDocs; 128 | 129 | public function boot() { 130 | FilamentDocs::css('filament.css'); 131 | } 132 | ``` 133 | 134 | ## Allow Tenants 135 | 136 | to allow tenants just use this method 137 | 138 | ```php 139 | ->plugin( 140 | \TomatoPHP\FilamentDocs\FilamentDocsPlugin::make() 141 | ->isScopedToTenant() 142 | ) 143 | ``` 144 | 145 | and add this migration 146 | 147 | 148 | ```php 149 | foreignId('team_id')->nullable()->constrained('teams')->onDelete('cascade'); 164 | }); 165 | 166 | Schema::table('document_templates', function (Blueprint $table) { 167 | $table->foreignId('team_id')->nullable()->constrained('teams')->onDelete('cascade'); 168 | }); 169 | } 170 | 171 | /** 172 | * Reverse the migrations. 173 | */ 174 | public function down(): void 175 | { 176 | Schema::table('documents', function (Blueprint $table) { 177 | $table->dropForeign(['team_id']); 178 | $table->dropColumn('team_id'); 179 | }); 180 | 181 | Schema::table('document_templates', function (Blueprint $table) { 182 | $table->dropForeign(['team_id']); 183 | $table->dropColumn('team_id'); 184 | }); 185 | } 186 | }; 187 | 188 | ``` 189 | 190 | ## Publish Assets 191 | 192 | you can publish config file by use this command 193 | 194 | ```bash 195 | php artisan vendor:publish --tag="filament-docs-config" 196 | ``` 197 | 198 | you can publish views file by use this command 199 | 200 | ```bash 201 | php artisan vendor:publish --tag="filament-docs-views" 202 | ``` 203 | 204 | you can publish languages file by use this command 205 | 206 | ```bash 207 | php artisan vendor:publish --tag="filament-docs-lang" 208 | ``` 209 | 210 | you can publish migrations file by use this command 211 | 212 | ```bash 213 | php artisan vendor:publish --tag="filament-docs-migrations" 214 | ``` 215 | 216 | ## Other Filament Packages 217 | 218 | Checkout our [Awesome TomatoPHP](https://github.com/tomatophp/awesome) 219 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/SECURITY.md -------------------------------------------------------------------------------- /arts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/.gitkeep -------------------------------------------------------------------------------- /arts/3x1io-tomato-docs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/3x1io-tomato-docs.jpg -------------------------------------------------------------------------------- /arts/create-document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/create-document.png -------------------------------------------------------------------------------- /arts/create-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/create-template.png -------------------------------------------------------------------------------- /arts/document-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/document-action.png -------------------------------------------------------------------------------- /arts/document-relation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/document-relation.png -------------------------------------------------------------------------------- /arts/documents-filters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/documents-filters.png -------------------------------------------------------------------------------- /arts/documents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/documents.png -------------------------------------------------------------------------------- /arts/edit-template-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/edit-template-icon.png -------------------------------------------------------------------------------- /arts/edit-template-vars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/edit-template-vars.png -------------------------------------------------------------------------------- /arts/edit-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/edit-template.png -------------------------------------------------------------------------------- /arts/generate-document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/generate-document.png -------------------------------------------------------------------------------- /arts/generate-notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/generate-notification.png -------------------------------------------------------------------------------- /arts/print-document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/print-document.png -------------------------------------------------------------------------------- /arts/templates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/arts/templates.png -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomatophp/filament-docs", 3 | "type": "library", 4 | "description": "Manage your documents and contracts all in one place with template builder", 5 | "keywords": [ 6 | "php", 7 | "laravel", 8 | "template", 9 | "filament", 10 | "document", 11 | "contract" 12 | ], 13 | "license": "MIT", 14 | "autoload": { 15 | "psr-4": { 16 | "TomatoPHP\\FilamentDocs\\": "src/" 17 | } 18 | }, 19 | "autoload-dev": { 20 | "psr-4": { 21 | "TomatoPHP\\FilamentDocs\\Tests\\": "tests/src", 22 | "TomatoPHP\\FilamentDocs\\Tests\\Database\\Factories\\": "tests/database/factories" 23 | } 24 | }, 25 | "extra": { 26 | "laravel": { 27 | "providers": [ 28 | "TomatoPHP\\FilamentDocs\\FilamentDocsServiceProvider" 29 | ] 30 | } 31 | }, 32 | "scripts": { 33 | "testbench": "vendor/bin/testbench package:discover --ansi", 34 | "db": "vendor/bin/testbench package:create-sqlite-db && vendor/bin/testbench migrate", 35 | "analyse": "vendor/bin/phpstan analyse src tests", 36 | "test": "vendor/bin/pest", 37 | "test-coverage": "vendor/bin/pest --coverage", 38 | "format": "vendor/bin/pint" 39 | }, 40 | "config": { 41 | "sort-packages": true, 42 | "allow-plugins": { 43 | "pestphp/pest-plugin": true, 44 | "phpstan/extension-installer": true 45 | } 46 | }, 47 | "authors": [ 48 | { 49 | "name": "Fady Mondy", 50 | "email": "info@3x1.io" 51 | } 52 | ], 53 | "require": { 54 | "php": "^8.2|^8.3|^8.4", 55 | "tomatophp/console-helpers": "^1.1", 56 | "tomatophp/filament-icons": "^1.1", 57 | "filament/filament": "^3.3", 58 | "awcodes/filament-tiptap-editor": "^3.5" 59 | }, 60 | "require-dev": { 61 | "laravel/pint": "^1.21", 62 | "livewire/livewire": "^2.10|^3.0", 63 | "nunomaduro/larastan": "^3.1", 64 | "orchestra/testbench": "^10.0", 65 | "pestphp/pest": "^3.7", 66 | "pestphp/pest-plugin-laravel": "^3.1", 67 | "pestphp/pest-plugin-livewire": "^3.0", 68 | "phpstan/extension-installer": "^1.4", 69 | "phpstan/phpstan-deprecation-rules": "^2.0", 70 | "phpstan/phpstan-phpunit": "^2.0" 71 | }, 72 | "version": "2.0.0" 73 | } 74 | -------------------------------------------------------------------------------- /config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/config/.gitkeep -------------------------------------------------------------------------------- /config/filament-docs.php: -------------------------------------------------------------------------------- 1 | "App\Models\Team", 10 | 11 | /* 12 | * --------------------------------------------------------------------------- 13 | * The default morph model attribute. 14 | * --------------------------------------------------------------------------- 15 | */ 16 | 'displayname_attribute' => 'name', 17 | 18 | /* 19 | * --------------------------------------------------------------------------- 20 | * The default date format. 21 | * --------------------------------------------------------------------------- 22 | */ 23 | 'dates' => [ 24 | 'day' => 'D', 25 | 'date' => 'Y-m-d', 26 | 'time' => 'H:i:s', 27 | ], 28 | 29 | /* 30 | * --------------------------------------------------------------------------- 31 | * The default views for printing documents. 32 | * --------------------------------------------------------------------------- 33 | */ 34 | 'views' => [ 35 | 'layout' => 'filament-docs::layout', 36 | 'view' => 'filament-docs::print', 37 | ], 38 | ]; 39 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/database/migrations/.gitkeep -------------------------------------------------------------------------------- /database/migrations/2024_02_29_113840_create_document_templates_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | 17 | $table->string('name'); 18 | 19 | $table->longText('body'); 20 | 21 | $table->boolean('is_active')->default(1)->nullable(); 22 | 23 | $table->string('icon')->default('bx bx-file')->nullable(); 24 | $table->string('color')->default('primary')->nullable(); 25 | $table->timestamps(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | */ 32 | public function down(): void 33 | { 34 | Schema::dropIfExists('document_templates'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/2024_02_29_113841_create_document_template_metas_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | 17 | $table->foreignId('document_template_id') 18 | ->constrained('document_templates') 19 | ->cascadeOnDelete(); 20 | 21 | $table->string('var'); 22 | $table->string('model')->nullable(); 23 | $table->json('value')->nullable(); 24 | 25 | $table->timestamps(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | */ 32 | public function down(): void 33 | { 34 | Schema::dropIfExists('document_template_vars'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/2024_02_29_124019_create_documents_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | 17 | $table->string('ref')->nullable()->index(); 18 | 19 | $table->string('model_type')->nullable(); 20 | $table->string('model_id')->nullable(); 21 | 22 | $table->foreignId('document_template_id') 23 | ->constrained('document_templates') 24 | ->cascadeOnDelete(); 25 | 26 | $table->longText('body'); 27 | 28 | $table->boolean('is_send') 29 | ->default(false) 30 | ->nullable(); 31 | 32 | $table->timestamps(); 33 | }); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | */ 39 | public function down(): void 40 | { 41 | Schema::dropIfExists('documents'); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /module.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FilamentDocs", 3 | "alias": "filament-docs", 4 | "description": { 5 | "ar": "Manage your documents and contracts all in one place with template builder", 6 | "en": "Manage your documents and contracts all in one place with template builder", 7 | "gr": "Manage your documents and contracts all in one place with template builder", 8 | "sp": "Manage your documents and contracts all in one place with template builder" 9 | }, 10 | "keywords": [], 11 | "priority": 0, 12 | "providers": [ 13 | "TomatoPHP\\FilamentDocs\\FilamentDocsServiceProvider" 14 | ], 15 | "files": [], 16 | "title": { 17 | "ar": "Documents Editor", 18 | "en": "Documents Editor", 19 | "gr": "Documents Editor", 20 | "sp": "Documents Editor" 21 | }, 22 | "color": "#007dff", 23 | "icon": "heroicon-c-arrow-trending-up", 24 | "placeholder": "https://raw.githubusercontent.com/tomatophp/filament-docs/master/arts/3x1io-tomato-docs.jpg", 25 | "type": "plugin", 26 | "version": "v2.0.0", 27 | "github" : "https://github.com/tomatophp/filament-docs", 28 | "docs" : "https://github.com/tomatophp/filament-docs" 29 | } 30 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | ./src 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /publish/public/css/filament-docs.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Night Owl for highlight.js (c) Carl Baxter 4 | 5 | An adaptation of Sarah Drasner's Night Owl VS Code Theme 6 | https://github.com/sdras/night-owl-vscode-theme 7 | 8 | Copyright (c) 2018 Sarah Drasner 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | .tiptap-editor .ProseMirror { 31 | .hljs { 32 | background: theme('colors.gray.800'); 33 | color: #d6deeb; 34 | padding: 0.5rem 1rem; 35 | border-radius: 0.5rem; 36 | font-size: 0.875rem; 37 | } 38 | 39 | /* General Purpose */ 40 | 41 | .hljs-keyword { 42 | color: #c792ea; 43 | } 44 | 45 | .hljs-built_in { 46 | color: #addb67; 47 | } 48 | 49 | .hljs-type { 50 | color: #82aaff; 51 | } 52 | 53 | .hljs-literal { 54 | color: #ff5874; 55 | } 56 | 57 | .hljs-number { 58 | color: #F78C6C; 59 | } 60 | 61 | .hljs-regexp { 62 | color: #5ca7e4; 63 | } 64 | 65 | .hljs-string { 66 | color: #ecc48d; 67 | } 68 | 69 | .hljs-subst { 70 | color: #d3423e; 71 | } 72 | 73 | .hljs-symbol { 74 | color: #82aaff; 75 | } 76 | 77 | .hljs-class { 78 | color: #ffcb8b; 79 | } 80 | 81 | .hljs-function { 82 | color: #82AAFF; 83 | } 84 | 85 | .hljs-title { 86 | color: #DCDCAA; 87 | } 88 | 89 | .hljs-params { 90 | color: #7fdbca; 91 | } 92 | 93 | /* Meta */ 94 | 95 | .hljs-comment { 96 | color: #637777; 97 | } 98 | 99 | .hljs-doctag { 100 | color: #7fdbca; 101 | } 102 | 103 | .hljs-meta { 104 | color: #82aaff; 105 | } 106 | 107 | .hljs-meta .hljs-keyword { 108 | 109 | color: #82aaff; 110 | } 111 | 112 | .hljs-meta .hljs-string { 113 | color: #ecc48d; 114 | } 115 | 116 | /* Tags, attributes, config */ 117 | 118 | .hljs-section { 119 | color: #82b1ff; 120 | } 121 | 122 | .hljs-tag, 123 | .hljs-name { 124 | color: #7fdbca; 125 | } 126 | 127 | .hljs-attr { 128 | color: #7fdbca; 129 | } 130 | 131 | .hljs-attribute { 132 | color: #80cbc4; 133 | } 134 | 135 | .hljs-variable { 136 | color: #addb67; 137 | } 138 | 139 | /* Markup */ 140 | 141 | .hljs-bullet { 142 | color: #d9f5dd; 143 | } 144 | 145 | .hljs-code { 146 | color: #80CBC4; 147 | } 148 | 149 | .hljs-emphasis { 150 | color: #c792ea; 151 | font-style: italic; 152 | } 153 | 154 | .hljs-strong { 155 | color: #addb67; 156 | font-weight: bold; 157 | } 158 | 159 | .hljs-formula { 160 | color: #c792ea; 161 | } 162 | 163 | .hljs-link { 164 | color: #ff869a; 165 | } 166 | 167 | .hljs-quote { 168 | color: #697098; 169 | } 170 | 171 | /* CSS */ 172 | 173 | .hljs-selector-tag { 174 | color: #ff6363; 175 | } 176 | 177 | .hljs-selector-id { 178 | color: #fad430; 179 | } 180 | 181 | .hljs-selector-class { 182 | color: #addb67; 183 | } 184 | 185 | .hljs-selector-attr, 186 | .hljs-selector-pseudo { 187 | color: #c792ea; 188 | } 189 | 190 | /* Templates */ 191 | 192 | .hljs-template-tag { 193 | color: #c792ea; 194 | } 195 | 196 | .hljs-template-variable { 197 | color: #addb67; 198 | } 199 | 200 | /* diff */ 201 | 202 | .hljs-addition { 203 | color: #addb67ff; 204 | font-style: italic; 205 | } 206 | 207 | .hljs-deletion { 208 | color: #EF535090; 209 | font-style: italic; 210 | } 211 | } 212 | 213 | [wire\:key*="filament_tiptap_source"] { 214 | .fi-fo-component-ctn { 215 | height: 100%; 216 | 217 | > div { 218 | height: 100%; 219 | 220 | .fi-fo-field-wrp { 221 | height: 100%; 222 | 223 | > div { 224 | height: 100%; 225 | grid-template-rows: auto 1fr; 226 | 227 | .source_code_editor * { 228 | height: 100% !important; 229 | } 230 | } 231 | } 232 | } 233 | } 234 | } 235 | 236 | .sorting .tiptap-wrapper { 237 | pointer-events: none; 238 | } 239 | 240 | .tiptap-wrapper.tiptap-fullscreen { 241 | position: fixed; 242 | top: 0; 243 | left: 0; 244 | bottom: 0; 245 | right: 0; 246 | z-index: 40; 247 | display: flex; 248 | flex-direction: column; 249 | height: 100%; 250 | 251 | .tiptap-toolbar { 252 | border-radius: 0; 253 | } 254 | 255 | .tiptap-prosemirror-wrapper { 256 | max-height: 100%; 257 | padding-block-end: 3rem; 258 | } 259 | } 260 | 261 | .tiptap-editor .tiptap-content { 262 | display: flex; 263 | flex-direction: column; 264 | } 265 | 266 | .tiptap-prosemirror-wrapper { 267 | &.prosemirror-w-sm { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.sm')) / 2)); } 268 | &.prosemirror-w-md { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.md')) / 2)); } 269 | &.prosemirror-w-lg { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.lg')) / 2)); } 270 | &.prosemirror-w-xl { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.xl')) / 2)); } 271 | &.prosemirror-w-2xl { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.2xl')) / 2)); } 272 | &.prosemirror-w-3xl { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.3xl')) / 2)); } 273 | &.prosemirror-w-4xl { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.4xl')) / 2)); } 274 | &.prosemirror-w-5xl { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.5xl')) / 2)); } 275 | &.prosemirror-w-6xl { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.6xl')) / 2)); } 276 | &.prosemirror-w-7xl { padding: 0 max(theme('padding.4'), calc((100% - theme('maxWidth.7xl')) / 2)); } 277 | &.prosemirror-w-none { padding: 0 theme('padding.4'); } 278 | } 279 | 280 | .tiptap-editor .ProseMirror { 281 | border-bottom-left-radius: 0.375rem; 282 | border-bottom-right-radius: 0.375rem; 283 | flex: 1 1 0; 284 | padding-block: 1rem; 285 | margin-inline: auto; 286 | position: relative; 287 | width: 100%; 288 | color: theme('colors.black'); 289 | 290 | &.ProseMirror-focused .ProseMirror-selectednode { 291 | @apply outline-2 outline-offset-2 outline-dashed outline-gray-700 dark:outline-gray-300; 292 | } 293 | 294 | .tiptap-block-wrapper { 295 | @apply rounded-md overflow-hidden bg-gray-100 dark:bg-gray-800; 296 | 297 | .tiptap-block { 298 | .tiptap-block-heading { 299 | @apply flex items-center justify-between py-1 px-3 leading-none text-gray-900 bg-gray-200 dark:text-white dark:bg-gray-950; 300 | 301 | .tiptap-block-title { 302 | @apply text-sm uppercase font-bold opacity-80; 303 | } 304 | } 305 | 306 | .tiptap-block-actions { 307 | @apply flex items-center gap-2; 308 | 309 | button { 310 | @apply opacity-75 hover:opacity-100 focus:opacity-100 hover:text-primary-500 focus:text-primary-500; 311 | } 312 | } 313 | 314 | .preview { 315 | @apply p-4; 316 | } 317 | } 318 | } 319 | 320 | .filament-tiptap-hurdle { 321 | width: 100%; 322 | max-width: 100vw; 323 | padding-block: 1rem; 324 | background-color: theme('colors.gray.800'); 325 | position: relative; 326 | 327 | &::before, 328 | &::after { 329 | content: ''; 330 | position: absolute; 331 | display: block; 332 | width: 100%; 333 | top: 0; 334 | bottom: 0; 335 | background-color: inherit; 336 | } 337 | 338 | &::before { 339 | left: -100%; 340 | } 341 | 342 | &::after { 343 | right: -100%; 344 | } 345 | 346 | &[data-color="gray_light"] { 347 | color: theme('colors.gray.900'); 348 | background-color: theme('colors.gray.300'); 349 | } 350 | 351 | &[data-color="gray"] { 352 | color: white; 353 | background-color: theme('colors.gray.500'); 354 | } 355 | 356 | &[data-color="gray_dark"] { 357 | color: white; 358 | background-color: theme('colors.gray.800'); 359 | } 360 | 361 | &[data-color="primary"] { 362 | color: theme('colors.gray.900'); 363 | background-color: theme('colors.primary.500'); 364 | } 365 | 366 | &[data-color="secondary"] { 367 | color: theme('colors.gray.900'); 368 | background-color: theme('colors.warning.500'); 369 | } 370 | 371 | &[data-color="tertiary"] { 372 | color: white; 373 | background-color: theme('colors.success.500'); 374 | } 375 | 376 | &[data-color="accent"] { 377 | color: white; 378 | background-color: theme('colors.danger.500'); 379 | } 380 | } 381 | 382 | &.ProseMirror-focused { 383 | outline: none; 384 | } 385 | 386 | > * + * { 387 | margin-block-start: 1rem; 388 | } 389 | 390 | > * + h1, 391 | > * + h2, 392 | > * + h3, 393 | > * + h4, 394 | > * + h5, 395 | > * + h6 { 396 | margin-block-start: 2rem; 397 | } 398 | 399 | img { 400 | display: inline-block; 401 | } 402 | 403 | h1, 404 | h2, 405 | h3, 406 | h4, 407 | h5, 408 | h6 { 409 | font-weight: bold; 410 | } 411 | 412 | h1 { 413 | font-size: 1.75rem; 414 | line-height: 1.1; 415 | } 416 | 417 | h2 { 418 | font-size: 1.5rem; 419 | line-height: 1.1; 420 | } 421 | 422 | h3 { 423 | font-size: 1.25rem; 424 | line-height: 1.25; 425 | } 426 | 427 | h4 { 428 | font-size: 1.125rem; 429 | } 430 | 431 | .lead { 432 | font-size: 1.375rem; 433 | line-height: 1.3; 434 | } 435 | 436 | small { 437 | font-size: 0.75rem; 438 | } 439 | 440 | ul, 441 | ol { 442 | @apply space-y-2; 443 | padding-inline-start: 1rem; 444 | margin-inline-start: 1rem; 445 | } 446 | 447 | ul { 448 | list-style: disc; 449 | } 450 | 451 | ol { 452 | list-style: decimal; 453 | } 454 | 455 | ul.checked-list { 456 | list-style-type: none; 457 | margin-inline-start: 0; 458 | } 459 | 460 | ul.checked-list li { 461 | display: flex; 462 | align-items: baseline; 463 | gap: 0.375em; 464 | } 465 | 466 | ul.checked-list li::before { 467 | content: '✓'; 468 | width: 1.25rem; 469 | height: 1.25rem; 470 | flex-shrink: 0; 471 | } 472 | 473 | blockquote { 474 | border-left: 0.25rem solid theme("colors.gray.400"); 475 | padding-inline-start: 0.5rem; 476 | margin-inline-start: 1rem; 477 | font-size: 1.25rem; 478 | } 479 | 480 | hr { 481 | border-color: theme("colors.gray.400"); 482 | } 483 | 484 | a { 485 | color: theme("colors.blue.600"); 486 | text-decoration: underline; 487 | } 488 | 489 | a[id] { 490 | color: theme('colors.black'); 491 | text-decoration: none; 492 | &::before { 493 | content: '# '; 494 | color: theme("colors.gray.500"); 495 | opacity: 50; 496 | } 497 | } 498 | 499 | a[data-as-button="true"] { 500 | background-color: theme("colors.gray.900"); 501 | color: white !important; 502 | text-decoration: none; 503 | display: inline-block; 504 | @apply rounded-md py-2 px-5; 505 | 506 | &[data-as-button-theme="primary"] { 507 | background-color: theme("colors.primary.600"); 508 | } 509 | 510 | &[data-as-button-theme="secondary"] { 511 | background-color: theme("colors.warning.600"); 512 | } 513 | 514 | &[data-as-button-theme="tertiary"] { 515 | background-color: theme("colors.success.600"); 516 | } 517 | 518 | &[data-as-button-theme="accent"] { 519 | background-color: theme("colors.danger.600"); 520 | } 521 | } 522 | 523 | sup { 524 | font-size: 65%; 525 | } 526 | 527 | img { 528 | border: dashed 2px transparent; 529 | 530 | &.ProseMirror-selectednode { 531 | border-radius: theme("borderRadius.DEFAULT"); 532 | outline-offset: 2px; 533 | outline: theme("colors.gray.900") dashed 2px; 534 | } 535 | } 536 | 537 | table { 538 | border-collapse: collapse; 539 | margin: 0; 540 | overflow: hidden; 541 | table-layout: fixed; 542 | width: 100%; 543 | position: relative; 544 | } 545 | 546 | table td, 547 | table th { 548 | border: 1px solid theme("colors.gray.400"); 549 | min-width: 1em; 550 | padding: 3px 5px; 551 | vertical-align: top; 552 | background-clip: padding-box 553 | } 554 | 555 | table td > *, 556 | table th > * { 557 | margin-bottom: 0; 558 | } 559 | 560 | table th { 561 | background-color: theme("colors.gray.200"); 562 | color: theme("colors.gray.700"); 563 | font-weight: 700; 564 | text-align: left; 565 | } 566 | 567 | table .selectedCell { 568 | position: relative; 569 | } 570 | 571 | table .selectedCell:after { 572 | background: rgba(200, 200, 255, 0.4); 573 | content: ""; 574 | left: 0; 575 | right: 0; 576 | top: 0; 577 | bottom: 0; 578 | pointer-events: none; 579 | position: absolute; 580 | z-index: 2; 581 | } 582 | 583 | table .column-resize-handle { 584 | background-color: #adf; 585 | bottom: -2px; 586 | position: absolute; 587 | right: -2px; 588 | pointer-events: none; 589 | top: 0; 590 | width: 4px; 591 | } 592 | 593 | table p { 594 | margin: 0; 595 | } 596 | 597 | .tableWrapper { 598 | padding: 1rem 0; 599 | overflow-x: auto; 600 | } 601 | 602 | .resize-cursor { 603 | cursor: col-resize; 604 | } 605 | 606 | pre { 607 | padding: .75rem 1rem; 608 | border-radius: .25rem; 609 | font-size: .875rem; 610 | 611 | code { 612 | border-radius: 0; 613 | padding-inline: 0; 614 | } 615 | } 616 | 617 | code { 618 | background-color: theme("colors.gray.300"); 619 | border-radius: 0.25rem; 620 | padding-inline: 0.25rem; 621 | } 622 | 623 | pre.hljs { 624 | direction: ltr; 625 | code { 626 | background-color: transparent; 627 | } 628 | } 629 | 630 | .filament-tiptap-grid, 631 | .filament-tiptap-grid-builder { 632 | display: grid; 633 | gap: 1rem; 634 | box-sizing: border-box; 635 | 636 | .filament-tiptap-grid__column, 637 | .filament-tiptap-grid-builder__column { 638 | box-sizing: border-box; 639 | border-style: dashed; 640 | border-width: 1px; 641 | border-color: theme("colors.gray.400"); 642 | padding: 0.5rem; 643 | border-radius: theme("borderRadius.DEFAULT"); 644 | 645 | > * + * { 646 | margin-block-start: 1rem; 647 | } 648 | } 649 | 650 | &.ProseMirror-selectednode { 651 | border-radius: theme("borderRadius.DEFAULT"); 652 | outline-offset: 2px; 653 | outline: theme("colors.gray.900") dashed 2px; 654 | } 655 | } 656 | 657 | .filament-tiptap-grid[type^="asymetric"] { 658 | grid-template-columns: 1fr; 659 | grid-template-rows: auto; 660 | } 661 | 662 | @media (max-width: theme('screens.sm')) { 663 | .filament-tiptap-grid-builder[data-stack-at="sm"] { 664 | grid-template-columns: 1fr !important; 665 | 666 | .filament-tiptap-grid-builder__column { 667 | grid-column: span 1 !important; 668 | } 669 | } 670 | } 671 | 672 | @media (max-width: theme('screens.md')) { 673 | .filament-tiptap-grid-builder[data-stack-at="md"] { 674 | grid-template-columns: 1fr !important; 675 | 676 | .filament-tiptap-grid-builder__column { 677 | grid-column: span 1 !important; 678 | } 679 | } 680 | } 681 | 682 | @media (max-width: theme('screens.lg')) { 683 | .filament-tiptap-grid-builder[data-stack-at="lg"] { 684 | grid-template-columns: 1fr !important; 685 | 686 | .filament-tiptap-grid-builder__column { 687 | grid-column: span 1 !important; 688 | } 689 | } 690 | } 691 | 692 | .filament-tiptap-grid[type="asymetric-right-thirds"] { 693 | @screen md { 694 | grid-template-columns: 1fr 2fr; 695 | } 696 | } 697 | 698 | .filament-tiptap-grid[type="asymetric-left-thirds"] { 699 | @screen md { 700 | grid-template-columns: 2fr 1fr; 701 | } 702 | } 703 | 704 | .filament-tiptap-grid[type="asymetric-right-fourths"] { 705 | @screen md { 706 | grid-template-columns: 1fr 3fr; 707 | } 708 | } 709 | 710 | .filament-tiptap-grid[type="asymetric-left-fourths"] { 711 | @screen md { 712 | grid-template-columns: 3fr 1fr; 713 | } 714 | } 715 | 716 | .filament-tiptap-grid[type="responsive"] { 717 | grid-template-columns: 1fr; 718 | grid-template-rows: auto; 719 | 720 | &[cols="2"] { 721 | @screen md { 722 | grid-template-columns: repeat(2, 1fr); 723 | } 724 | } 725 | 726 | &[cols="3"] { 727 | @screen md { 728 | grid-template-columns: repeat(3, 1fr); 729 | } 730 | } 731 | 732 | &[cols="4"] { 733 | @screen md { 734 | grid-template-columns: repeat(2, 1fr); 735 | } 736 | 737 | @screen lg { 738 | grid-template-columns: repeat(4, 1fr); 739 | } 740 | } 741 | 742 | &[cols="5"] { 743 | @screen md { 744 | grid-template-columns: repeat(5, 1fr); 745 | } 746 | } 747 | } 748 | 749 | .filament-tiptap-grid[type="fixed"] { 750 | &[cols="2"] { 751 | grid-template-columns: repeat(2, 1fr); 752 | } 753 | 754 | &[cols="3"] { 755 | grid-template-columns: repeat(3, 1fr); 756 | } 757 | 758 | &[cols="4"] { 759 | grid-template-columns: repeat(4, 1fr); 760 | } 761 | 762 | &[cols="5"] { 763 | grid-template-columns: repeat(5, 1fr); 764 | } 765 | } 766 | 767 | [data-youtube-video], 768 | [data-vimeo-video], 769 | [data-native-video] { 770 | border: dashed 1px transparent; 771 | 772 | &.ProseMirror-selectednode { 773 | border-radius: theme("borderRadius.DEFAULT"); 774 | outline-offset: 2px; 775 | outline: theme("colors.gray.900") dashed 2px; 776 | } 777 | 778 | iframe, 779 | video { 780 | pointer-events: none; 781 | } 782 | } 783 | 784 | div[data-type="details"] { 785 | box-sizing: border-box; 786 | border-style: dashed; 787 | border-width: 1px; 788 | border-color: theme("colors.gray.400"); 789 | border-radius: theme("borderRadius.DEFAULT"); 790 | position: relative; 791 | 792 | button { 793 | position: absolute; 794 | z-index: 1; 795 | top: 0.125rem; 796 | right: 0.25rem; 797 | color: theme("colors.gray.400"); 798 | } 799 | 800 | summary { 801 | padding: 0.375rem 0.5rem; 802 | font-weight: 700; 803 | border-bottom-style: solid; 804 | border-bottom-width: 1px; 805 | border-bottom-color: theme("colors.gray.200"); 806 | 807 | &::marker { 808 | content: ""; 809 | display: none; 810 | } 811 | } 812 | 813 | div[data-type="details-content"] { 814 | padding: 0.5rem; 815 | height: auto; 816 | 817 | > * + * { 818 | margin-block-start: 1rem; 819 | } 820 | } 821 | } 822 | } 823 | 824 | .dark { 825 | .tiptap-editor .ProseMirror { 826 | color: theme('colors.gray.200'); 827 | 828 | blockquote { 829 | border-left-color: theme("colors.gray.500"); 830 | } 831 | 832 | hr { 833 | border-color: theme("colors.gray.500"); 834 | } 835 | 836 | a { 837 | color: theme("colors.blue.400"); 838 | } 839 | 840 | a[id] { 841 | color: theme('colors.gray.200'); 842 | } 843 | 844 | code { 845 | background-color: theme("colors.gray.800"); 846 | } 847 | 848 | table td, 849 | table th { 850 | border-color: theme("colors.gray.600"); 851 | } 852 | 853 | table th { 854 | background-color: theme("colors.gray.800"); 855 | color: theme('colors.gray.100'); 856 | } 857 | 858 | .filament-tiptap-grid { 859 | .filament-tiptap-grid__column { 860 | border-color: theme("colors.gray.500"); 861 | } 862 | 863 | &.ProseMirror-selectednode { 864 | outline-color: theme("colors.gray.400"); 865 | } 866 | } 867 | 868 | img.ProseMirror-selectednode { 869 | outline-color: theme("colors.gray.400"); 870 | } 871 | 872 | [data-youtube-video], 873 | [data-vimeo-video], 874 | [data-native-video] { 875 | &.ProseMirror-selectednode { 876 | outline-color: theme("colors.gray.400"); 877 | } 878 | } 879 | 880 | div[data-type="details"] { 881 | box-sizing: border-box; 882 | border-color: theme("colors.gray.500"); 883 | border-radius: theme("borderRadius.DEFAULT"); 884 | position: relative; 885 | 886 | summary { 887 | border-bottom-color: theme("colors.gray.500"); 888 | } 889 | } 890 | } 891 | 892 | .tiptap-editor .ProseMirror-focused .ProseMirror-gapcursor:after { 893 | border-top: 1px solid white; 894 | } 895 | } 896 | 897 | 898 | .tiptap-editor p.is-editor-empty:first-child::before { 899 | content: attr(data-placeholder); 900 | float: left; 901 | height: 0; 902 | pointer-events: none; 903 | @apply text-gray-400 dark:text-gray-500; 904 | } 905 | 906 | .tippy-content-p-0 { 907 | @apply -mx-2 -my-1; 908 | } 909 | 910 | span[data-type="mergeTag"] { 911 | @apply bg-gray-100 dark:bg-gray-800 px-2 py-1 mx-1 rounded; 912 | } 913 | -------------------------------------------------------------------------------- /resources/js/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/resources/js/.gitkeep -------------------------------------------------------------------------------- /resources/lang/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/resources/lang/.gitkeep -------------------------------------------------------------------------------- /resources/lang/ar/messages.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'title' => 'الملفات', 6 | 'group' => 'المحتوى', 7 | 'single' => 'ملف', 8 | 'form' => [ 9 | 'ref' => 'المرجع', 10 | 'id' => 'مسلسل', 11 | 'model' => 'خاص بـ', 12 | 'document_template_id' => 'القالب', 13 | 'document' => 'الملف', 14 | 'body' => 'المحتوى', 15 | 'is_send' => 'تم الإرسال', 16 | 'template' => 'القالب', 17 | 'values' => 'القيم', 18 | 'var-value' => 'القيمة', 19 | 'var-label' => 'التسمية', 20 | ], 21 | 'actions' => [ 22 | 'print' => 'طباعة', 23 | 'document' => [ 24 | 'title' => 'إنشاء ملف', 25 | 'notification' => [ 26 | 'title' => 'تم إنشاء الملف', 27 | 'body' => 'تم إنشاء الملف', 28 | 'action' => 'عرض الملف', 29 | ], 30 | ], 31 | ], 32 | ], 33 | 'document-templates' => [ 34 | 'title' => 'قوالب الملفات', 35 | 'group' => 'المحتوى', 36 | 'single' => 'قالب', 37 | 'form' => [ 38 | 'name' => 'الاسم', 39 | 'vars' => 'المتغيرات', 40 | 'vars-key' => 'المفتاح', 41 | 'vars-label' => 'القيمة', 42 | 'is_active' => 'نشط', 43 | 'body' => 'المحتوى', 44 | 'icon' => 'أيقونة', 45 | 'color' => 'لون', 46 | ], 47 | ], 48 | 'vars' => [ 49 | 'day' => 'يوم', 50 | 'date' => 'تاريخ', 51 | 'time' => 'وقت', 52 | 'random' => 'عشوائي', 53 | 'uuid' => 'UUID', 54 | ], 55 | ]; 56 | -------------------------------------------------------------------------------- /resources/lang/en/messages.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'title' => 'Documents', 6 | 'group' => 'Content', 7 | 'single' => 'Document', 8 | 'form' => [ 9 | 'ref' => 'Reference', 10 | 'id' => 'ID', 11 | 'model' => 'Connected To', 12 | 'document_template_id' => 'Template', 13 | 'document' => 'Document', 14 | 'body' => 'Body', 15 | 'is_send' => 'Is Send', 16 | 'template' => 'Template', 17 | 'values' => 'Values', 18 | 'var-value' => 'Value', 19 | 'var-label' => 'Label', 20 | ], 21 | 'actions' => [ 22 | 'print' => 'Print', 23 | 'document' => [ 24 | 'title' => 'Create Document', 25 | 'notification' => [ 26 | 'title' => 'Document Created', 27 | 'body' => 'Document has been created', 28 | 'action' => 'View Document', 29 | ], 30 | ], 31 | ], 32 | ], 33 | 'document-templates' => [ 34 | 'title' => 'Document Templates', 35 | 'group' => 'Content', 36 | 'single' => 'Template', 37 | 'form' => [ 38 | 'name' => 'Name', 39 | 'vars' => 'Variables', 40 | 'vars-key' => 'Key', 41 | 'vars-label' => 'Value', 42 | 'is_active' => 'Is Active', 43 | 'body' => 'Body', 44 | 'icon' => 'Icon', 45 | 'color' => 'Color', 46 | ], 47 | ], 48 | 'vars' => [ 49 | 'day' => 'Day', 50 | 'date' => 'Date', 51 | 'time' => 'Time', 52 | 'random' => 'Random', 53 | 'uuid' => 'UUID', 54 | ], 55 | ]; 56 | -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/resources/views/.gitkeep -------------------------------------------------------------------------------- /resources/views/layout.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | 75 | 76 | 77 | 78 | @if(\TomatoPHP\FilamentDocs\Facades\FilamentDocs::getHeader()) 79 |
80 | {!! \TomatoPHP\FilamentDocs\Facades\FilamentDocs::getHeader() !!} 81 |
82 |
83 | @endif 84 |
85 | {{ $slot }} 86 |
87 | @if(\TomatoPHP\FilamentDocs\Facades\FilamentDocs::getFooter()) 88 |
89 | 92 | @endif 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /resources/views/print.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @if(isset($record)) 3 | {!! $record->body !!} 4 | @else 5 | {!! $this->getRecord()->body !!} 6 | @endif 7 |
8 | -------------------------------------------------------------------------------- /src/Console/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/src/Console/.gitkeep -------------------------------------------------------------------------------- /src/Console/FilamentDocsInstall.php: -------------------------------------------------------------------------------- 1 | info('Publish Vendor Assets'); 39 | $this->artisanCommand(['migrate']); 40 | $this->artisanCommand(['optimize:clear']); 41 | $this->artisanCommand(['filament:optimize-clear']); 42 | $this->artisanCommand(['icon:cache']); 43 | $this->info('Filament Docs installed successfully.'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Facades/FilamentDocs.php: -------------------------------------------------------------------------------- 1 | requiresConfirmation(); 22 | $this->iconButton(); 23 | $this->name('document'); 24 | $this->icon('heroicon-m-document-text'); 25 | $this->label(trans('filament-docs::messages.documents.actions.document.title')); 26 | $this->tooltip(trans('filament-docs::messages.documents.actions.document.title')); 27 | $this->color('success'); 28 | $this->form([ 29 | Forms\Components\Select::make('document_template_id') 30 | ->preload() 31 | ->label(trans('filament-docs::messages.documents.form.document_template_id')) 32 | ->searchable() 33 | ->options(DocumentTemplate::query()->where('is_active', 1)->pluck('name', 'id')->toArray()) 34 | ->columnSpanFull() 35 | ->required(), 36 | ]); 37 | $this->action(function (array $data, $record) { 38 | $body = FilamentDocs::body($data['document_template_id'], $this->getVars()); 39 | 40 | $document = Document::query()->create([ 41 | 'model_id' => $record->id, 42 | 'model_type' => get_class($record), 43 | 'document_template_id' => $data['document_template_id'], 44 | 'body' => $body, 45 | ]); 46 | 47 | Notification::make() 48 | ->title(trans('filament-docs::messages.documents.actions.document.notification.title')) 49 | ->body(trans('filament-docs::messages.documents.actions.document.notification.body')) 50 | ->actions([ 51 | \Filament\Notifications\Actions\Action::make('document') 52 | ->icon('heroicon-o-document-text') 53 | ->label(trans('filament-docs::messages.documents.actions.document.notification.action')) 54 | ->url(DocumentResource::getUrl('edit', ['record' => $document])) 55 | ->openUrlInNewTab(), 56 | ]) 57 | ->success() 58 | ->send(); 59 | }); 60 | } 61 | 62 | /** 63 | * @return $this 64 | */ 65 | public function vars(array | \Closure $var): static 66 | { 67 | $this->vars = $var; 68 | 69 | return $this; 70 | } 71 | 72 | protected function getVars(): array 73 | { 74 | if ($this->vars instanceof \Closure) { 75 | return $this->evaluate($this->vars); 76 | } 77 | 78 | return $this->vars; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Filament/Actions/Notifications/PrintAction.php: -------------------------------------------------------------------------------- 1 | route = $route; 23 | 24 | return $this; 25 | } 26 | 27 | public function getRoute() 28 | { 29 | return $this->evaluate($this->route); 30 | } 31 | 32 | public function title(Closure | string $title): self 33 | { 34 | $this->title = $title; 35 | 36 | return $this; 37 | } 38 | 39 | public function getTitle() 40 | { 41 | return $this->evaluate($this->title); 42 | } 43 | 44 | protected function setUp(): void 45 | { 46 | parent::setUp(); 47 | 48 | $this 49 | ->label(trans('filament-docs::messages.documents.actions.print')) 50 | ->icon('heroicon-o-printer') 51 | ->action(function (Livewire $livewire) { 52 | $livewire->js( 53 | << { 66 | document.body.removeChild(iframe); 67 | document.title = orginalTitle; 68 | }, 1000); 69 | }; 70 | JS 71 | ); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Filament/Actions/PrintAction.php: -------------------------------------------------------------------------------- 1 | route = $route; 23 | 24 | return $this; 25 | } 26 | 27 | public function getRoute() 28 | { 29 | return $this->evaluate($this->route); 30 | } 31 | 32 | public function title(Closure | string $title): self 33 | { 34 | $this->title = $title; 35 | 36 | return $this; 37 | } 38 | 39 | public function getTitle() 40 | { 41 | return $this->evaluate($this->title); 42 | } 43 | 44 | protected function setUp(): void 45 | { 46 | parent::setUp(); 47 | 48 | $this 49 | ->label(trans('filament-docs::messages.documents.actions.print')) 50 | ->icon('heroicon-o-printer') 51 | ->action(function (Livewire $livewire) { 52 | $livewire->js( 53 | << { 66 | document.body.removeChild(iframe); 67 | document.title = orginalTitle; 68 | }, 1000); 69 | }; 70 | JS 71 | ); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Filament/Actions/Table/PrintAction.php: -------------------------------------------------------------------------------- 1 | route = $route; 23 | 24 | return $this; 25 | } 26 | 27 | public function getRoute() 28 | { 29 | return $this->evaluate($this->route); 30 | } 31 | 32 | public function title(Closure | string $title): self 33 | { 34 | $this->title = $title; 35 | 36 | return $this; 37 | } 38 | 39 | public function getTitle() 40 | { 41 | return $this->evaluate($this->title); 42 | } 43 | 44 | protected function setUp(): void 45 | { 46 | parent::setUp(); 47 | 48 | $this 49 | ->label(trans('filament-docs::messages.documents.actions.print')) 50 | ->icon('heroicon-o-printer') 51 | ->action(function (Livewire $livewire) { 52 | $livewire->js( 53 | << { 66 | document.body.removeChild(iframe); 67 | document.title = orginalTitle; 68 | }, 1000); 69 | }; 70 | JS 71 | ); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Filament/RelationManager/DocumentRelationManager.php: -------------------------------------------------------------------------------- 1 | preload() 57 | ->label(trans('filament-docs::messages.documents.form.document_template_id')) 58 | ->searchable() 59 | ->relationship('documentTemplate', 'name') 60 | ->live() 61 | ->afterStateUpdated(function (Forms\Get $get, Forms\Set $set, $record) { 62 | if (! $record) { 63 | $documentTemplate = DocumentTemplate::query()->find($get('document_template_id')); 64 | if ($documentTemplate) { 65 | $documentTemplateVars = $documentTemplate->documentTemplateVars; 66 | $fields = []; 67 | foreach ($documentTemplateVars as $var) { 68 | $fields[] = [ 69 | 'var' => $var->var, 70 | 'label' => FilamentDocs::load()->where('key', $var->var)->first()?->label, 71 | 'key' => $var->value, 72 | 'value' => '', 73 | 'model' => FilamentDocs::load()->where('key', $var->var)->first()?->model, 74 | ]; 75 | } 76 | 77 | $collect = collect($fields)->sortBy('model')->toArray(); 78 | $set('body', $collect); 79 | } 80 | } 81 | }) 82 | ->columnSpanFull() 83 | ->required(), 84 | Forms\Components\Section::make(trans('filament-docs::messages.documents.form.document')) 85 | ->hidden(fn (Forms\Get $get) => (! $get('document_template_id')) || ! $get('body')) 86 | ->schema(function ($record) { 87 | if ($record) { 88 | return [ 89 | TiptapEditor::make('body') 90 | ->label(trans('filament-docs::messages.documents.form.body')) 91 | ->required(), 92 | ]; 93 | } else { 94 | return [ 95 | Forms\Components\Repeater::make('body') 96 | ->hidden(fn ($record, Forms\Get $get) => $record || ! $get('body')) 97 | ->schema([ 98 | Forms\Components\Hidden::make('var')->live(), 99 | Forms\Components\Hidden::make('model')->live(), 100 | Forms\Components\Hidden::make('key')->live(), 101 | Forms\Components\TextInput::make('label') 102 | ->disabled() 103 | ->label(trans('filament-docs::messages.documents.form.var-label')), 104 | Forms\Components\Select::make('value') 105 | ->label(trans('filament-docs::messages.documents.form.var-value')) 106 | ->searchable() 107 | ->options(function (Forms\Get $get) { 108 | if ($get('model') && $get('var')) { 109 | return $get('model')::query()->pluck(FilamentDocs::load()->where('key', $get('var'))->first()?->column, 'id')->toArray(); 110 | } else { 111 | return []; 112 | } 113 | }) 114 | ->live() 115 | ->afterStateUpdated(function (Forms\Get $get, Forms\Set $set) { 116 | $body = []; 117 | $groups = []; 118 | foreach ($get('../../body') as $item) { 119 | if (! array_key_exists($item['model'], $groups)) { 120 | if ($item['value']) { 121 | $groups[$item['model']] = $item['value']; 122 | } else { 123 | $groups[$item['model']] = ''; 124 | } 125 | } else { 126 | $item['value'] = $groups[$item['model']] ?? null; 127 | } 128 | 129 | $body[] = $item; 130 | } 131 | $set('../../body', $body); 132 | }) 133 | ->required(), 134 | ]) 135 | ->label(trans('filament-docs::messages.documents.form.values')) 136 | ->addable(false) 137 | ->deletable(false) 138 | ->reorderable(false) 139 | ->columns(2) 140 | ->columnSpanFull() 141 | ->required(), 142 | ]; 143 | } 144 | 145 | }), 146 | Forms\Components\TextInput::make('ref') 147 | ->columnSpanFull() 148 | ->nullable(), 149 | ]; 150 | 151 | if (filament('filament-docs')::$isScopedToTenant) { 152 | $schema[] = Forms\Components\Select::make('team_id') 153 | ->label(trans('filament-docs::messages.documents.form.team_id')) 154 | ->visible(fn (Forms\Get $get) => $get('team_id') === null) 155 | ->default(filament()->getTenant()?->id) 156 | ->relationship('team', 'name'); 157 | } 158 | 159 | return $form 160 | ->schema($schema); 161 | } 162 | 163 | public static function table(Table $table): Table 164 | { 165 | return $table 166 | ->columns([ 167 | Tables\Columns\TextColumn::make('id') 168 | ->searchable() 169 | ->prefix('#') 170 | ->label(trans('filament-docs::messages.documents.form.id')) 171 | ->numeric() 172 | ->sortable(), 173 | Tables\Columns\TextColumn::make('ref') 174 | ->searchable() 175 | ->label(trans('filament-docs::messages.documents.form.ref')) 176 | ->numeric() 177 | ->sortable(), 178 | Tables\Columns\TextColumn::make('documentTemplate.name') 179 | ->badge() 180 | ->color('warning') 181 | ->icon(fn ($record) => $record->documentTemplate->icon) 182 | ->label(trans('filament-docs::messages.documents.form.document_template_id')) 183 | ->url(fn ($record) => DocumentTemplateResource::getUrl('edit', ['record' => $record->documentTemplate->id])) 184 | ->sortable(), 185 | Tables\Columns\TextColumn::make('model' . config('filament-docs.displayname_attribute')) 186 | ->label(trans('filament-docs::messages.documents.form.model')) 187 | ->badge() 188 | ->color('info') 189 | ->icon(function ($record) { 190 | $resources = filament()->getCurrentPanel()->getResources(); 191 | foreach ($resources as $item) { 192 | $resourceClass = app($item); 193 | if ($resourceClass->getModel() === $record->model_type) { 194 | return $resourceClass::getNavigationIcon(); 195 | } 196 | } 197 | }) 198 | ->url(function ($record) { 199 | $resources = filament()->getCurrentPanel()->getResources(); 200 | foreach ($resources as $item) { 201 | $resourceClass = app($item); 202 | if ($resourceClass->getModel() === $record->model_type) { 203 | try { 204 | return $resourceClass::getUrl('edit', ['record' => $record->model_id]); 205 | } catch (\Exception $e) { 206 | return '#'; 207 | } 208 | } 209 | } 210 | }) 211 | ->sortable(), 212 | Tables\Columns\ToggleColumn::make('is_send') 213 | ->label(trans('filament-docs::messages.documents.form.is_send')), 214 | Tables\Columns\TextColumn::make('created_at') 215 | ->dateTime() 216 | ->sortable() 217 | ->toggleable(isToggledHiddenByDefault: true), 218 | Tables\Columns\TextColumn::make('updated_at') 219 | ->dateTime() 220 | ->sortable() 221 | ->toggleable(isToggledHiddenByDefault: true), 222 | ]) 223 | ->defaultSort('id', 'desc') 224 | ->filters([ 225 | Tables\Filters\SelectFilter::make('document_template_id') 226 | ->label(trans('filament-docs::messages.documents.form.document_template_id')) 227 | ->searchable() 228 | ->options(DocumentTemplate::query()->where('is_active', 1)->pluck('name', 'id')->toArray()), 229 | ]) 230 | ->actions([ 231 | Tables\Actions\Action::make('view') 232 | ->color('info') 233 | ->modalContent(fn ($record) => view('filament-docs::print', [ 234 | 'record' => $record, 235 | ])) 236 | ->icon('heroicon-s-eye') 237 | ->iconButton() 238 | ->tooltip(__('filament-actions::view.single.label')), 239 | PrintAction::make('print') 240 | ->icon('heroicon-s-printer') 241 | ->title(fn ($record) => $record->documentTemplate->name . '#' . $record->id) 242 | ->route( 243 | fn ($record) => Pages\PrintDocument::getUrl(['record' => $record]) 244 | ) 245 | ->color('warning') 246 | ->iconButton() 247 | ->tooltip(trans('filament-docs::messages.documents.actions.print')), 248 | Tables\Actions\EditAction::make() 249 | ->iconButton() 250 | ->tooltip(__('filament-actions::edit.single.label')), 251 | Tables\Actions\DeleteAction::make() 252 | ->iconButton() 253 | ->tooltip(__('filament-actions::delete.single.label')), 254 | Tables\Actions\ReplicateAction::make() 255 | ->iconButton() 256 | ->tooltip(__('filament-actions::replicate.single.label')), 257 | ]) 258 | ->bulkActions([ 259 | Tables\Actions\BulkActionGroup::make([ 260 | Tables\Actions\DeleteBulkAction::make(), 261 | ]), 262 | ]); 263 | } 264 | 265 | public static function getRelations(): array 266 | { 267 | return [ 268 | // 269 | ]; 270 | } 271 | 272 | public static function getPages(): array 273 | { 274 | return [ 275 | 'index' => Pages\ListDocuments::route('/'), 276 | 'create' => Pages\CreateDocument::route('/create'), 277 | 'edit' => Pages\EditDocument::route('/{record}/edit'), 278 | 'print' => Pages\PrintDocument::route('/{record}/print'), 279 | ]; 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/Filament/Resources/DocumentResource/Pages/CreateDocument.php: -------------------------------------------------------------------------------- 1 | title($this->getRecord()->ref ?: $this->getRecord()->documentTemplate->name) 20 | ->route(PrintDocument::getUrl(['record' => $this->getRecord()])), 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Filament/Resources/DocumentResource/Pages/ListDocuments.php: -------------------------------------------------------------------------------- 1 | label(trans('filament-docs::messages.document-templates.title')) 20 | ->tooltip(trans('filament-docs::messages.document-templates.title')) 21 | ->hiddenLabel() 22 | ->icon('heroicon-o-document') 23 | ->color('info') 24 | ->url(DocumentTemplateResource::getUrl('index')), 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Filament/Resources/DocumentResource/Pages/PrintDocument.php: -------------------------------------------------------------------------------- 1 | record = $this->resolveRecord($record); 18 | } 19 | 20 | public function getLayout(): string 21 | { 22 | return config('filament-docs.views.layout') ?: 'filament-docs::layout'; 23 | } 24 | 25 | public function getView(): string 26 | { 27 | return config('filament-docs.views.view') ?: 'filament-docs::print'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Filament/Resources/DocumentTemplateResource.php: -------------------------------------------------------------------------------- 1 | pluck('label', 'key')->toArray(), 62 | [ 63 | '$UUID' => trans('filament-docs::messages.vars.uuid'), 64 | '$RANDOM' => trans('filament-docs::messages.vars.random'), 65 | '$DAY' => trans('filament-docs::messages.vars.day'), 66 | '$DATE' => trans('filament-docs::messages.vars.date'), 67 | '$TIME' => trans('filament-docs::messages.vars.time'), 68 | ] 69 | ); 70 | 71 | $schema = [ 72 | Forms\Components\TextInput::make('name') 73 | ->label(trans('filament-docs::messages.document-templates.form.name')) 74 | ->required() 75 | ->columnSpanFull() 76 | ->maxLength(255), 77 | Forms\Components\KeyValue::make('vars') 78 | ->disabled() 79 | ->valueLabel(trans('filament-docs::messages.document-templates.form.vars-label')) 80 | ->keyLabel(trans('filament-docs::messages.document-templates.form.vars-key')) 81 | ->label(trans('filament-docs::messages.document-templates.form.vars')) 82 | ->columnSpanFull() 83 | ->default($keys), 84 | TiptapEditor::make('body') 85 | ->label(trans('filament-docs::messages.document-templates.form.body')) 86 | ->required() 87 | ->columnSpanFull(), 88 | IconPicker::make('icon') 89 | ->label(trans('filament-docs::messages.document-templates.form.icon')), 90 | Forms\Components\ColorPicker::make('color') 91 | ->label(trans('filament-docs::messages.document-templates.form.color')), 92 | Forms\Components\Toggle::make('is_active') 93 | ->label(trans('filament-docs::messages.document-templates.form.is_active')), 94 | 95 | ]; 96 | 97 | if (filament('filament-docs')::$isScopedToTenant) { 98 | $schema[] = Forms\Components\Select::make('team_id') 99 | ->label(trans('filament-docs::messages.document-templates.form.team_id')) 100 | ->visible(fn (Forms\Get $get) => $get('team_id') === null) 101 | ->default(filament()->getTenant()?->id) 102 | ->relationship('team', 'name'); 103 | } 104 | 105 | return $form 106 | ->schema($schema); 107 | } 108 | 109 | public static function table(Table $table): Table 110 | { 111 | return $table 112 | ->columns([ 113 | Tables\Columns\TextColumn::make('name') 114 | ->label(trans('filament-docs::messages.document-templates.form.name')) 115 | ->searchable(), 116 | Tables\Columns\ToggleColumn::make('is_active') 117 | ->label(trans('filament-docs::messages.document-templates.form.is_active')), 118 | IconColumn::make('icon') 119 | ->label(trans('filament-docs::messages.document-templates.form.icon')) 120 | ->searchable(), 121 | Tables\Columns\ColorColumn::make('color') 122 | ->label(trans('filament-docs::messages.document-templates.form.color')) 123 | ->searchable(), 124 | Tables\Columns\TextColumn::make('created_at') 125 | ->dateTime() 126 | ->sortable() 127 | ->toggleable(isToggledHiddenByDefault: true), 128 | Tables\Columns\TextColumn::make('updated_at') 129 | ->dateTime() 130 | ->sortable() 131 | ->toggleable(isToggledHiddenByDefault: true), 132 | ]) 133 | ->filters([ 134 | // 135 | ]) 136 | ->actions([ 137 | Tables\Actions\ViewAction::make() 138 | ->iconButton() 139 | ->tooltip(__('filament-actions::view.single.label')), 140 | Tables\Actions\EditAction::make() 141 | ->iconButton() 142 | ->tooltip(__('filament-actions::edit.single.label')), 143 | Tables\Actions\DeleteAction::make() 144 | ->iconButton() 145 | ->tooltip(__('filament-actions::delete.single.label')), 146 | Tables\Actions\ReplicateAction::make() 147 | ->iconButton() 148 | ->tooltip(__('filament-actions::replicate.single.label')), 149 | ]) 150 | ->bulkActions([ 151 | Tables\Actions\BulkActionGroup::make([ 152 | Tables\Actions\DeleteBulkAction::make(), 153 | ]), 154 | ]); 155 | } 156 | 157 | public static function getRelations(): array 158 | { 159 | return []; 160 | } 161 | 162 | public static function getPages(): array 163 | { 164 | return [ 165 | 'index' => Pages\ListDocumentTemplates::route('/'), 166 | 'create' => Pages\CreateDocumentTemplate::route('/create'), 167 | 'edit' => Pages\EditDocumentTemplate::route('/{record}/edit'), 168 | ]; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/Filament/Resources/DocumentTemplateResource/Pages/CreateDocumentTemplate.php: -------------------------------------------------------------------------------- 1 | getRecord()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Filament/Resources/DocumentTemplateResource/Pages/EditDocumentTemplate.php: -------------------------------------------------------------------------------- 1 | pluck('label', 'key')->toArray(), 25 | [ 26 | '$UUID' => trans('filament-docs::messages.vars.uuid'), 27 | '$RANDOM' => trans('filament-docs::messages.vars.random'), 28 | '$DAY' => trans('filament-docs::messages.vars.day'), 29 | '$DATE' => trans('filament-docs::messages.vars.date'), 30 | '$TIME' => trans('filament-docs::messages.vars.time'), 31 | ] 32 | ); 33 | 34 | return $data; 35 | } 36 | 37 | public function afterSave(): void 38 | { 39 | FilamentDocs::create($this->getRecord()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Filament/Resources/DocumentTemplateResource/Pages/ListDocumentTemplates.php: -------------------------------------------------------------------------------- 1 | resources([ 29 | DocumentTemplateResource::class, 30 | DocumentResource::class, 31 | ]); 32 | } 33 | 34 | public function boot(Panel $panel): void 35 | { 36 | // 37 | } 38 | 39 | public static function make(): static 40 | { 41 | return new static; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/FilamentDocsServiceProvider.php: -------------------------------------------------------------------------------- 1 | commands([ 18 | \TomatoPHP\FilamentDocs\Console\FilamentDocsInstall::class, 19 | ]); 20 | 21 | // Register Config file 22 | $this->mergeConfigFrom(__DIR__ . '/../config/filament-docs.php', 'filament-docs'); 23 | 24 | // Publish Config 25 | $this->publishes([ 26 | __DIR__ . '/../config/filament-docs.php' => config_path('filament-docs.php'), 27 | ], 'filament-docs-config'); 28 | 29 | // Register Migrations 30 | $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); 31 | 32 | // Publish Migrations 33 | $this->publishes([ 34 | __DIR__ . '/../database/migrations' => database_path('migrations'), 35 | ], 'filament-docs-migrations'); 36 | // Register views 37 | $this->loadViewsFrom(__DIR__ . '/../resources/views', 'filament-docs'); 38 | 39 | // Publish Views 40 | $this->publishes([ 41 | __DIR__ . '/../resources/views' => resource_path('views/vendor/filament-docs'), 42 | ], 'filament-docs-views'); 43 | 44 | // Register Langs 45 | $this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'filament-docs'); 46 | 47 | // Publish Lang 48 | $this->publishes([ 49 | __DIR__ . '/../resources/lang' => base_path('lang/vendor/filament-docs'), 50 | ], 'filament-docs-lang'); 51 | 52 | $this->app->bind('filament-docs', function () { 53 | return new FilamentDocsServices; 54 | }); 55 | 56 | Livewire::component('tomato-p-h-p.filament-docs.filament.relation-manager.document-relation-manager', DocumentRelationManager::class); 57 | } 58 | 59 | public function boot(): void 60 | { 61 | FilamentAsset::register([ 62 | Css::make('filament-docs', __DIR__ . '/../publish/public/css/filament-docs.css'), 63 | ], package: 'tomatophp/filament-docs'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/src/Models/.gitkeep -------------------------------------------------------------------------------- /src/Models/Document.php: -------------------------------------------------------------------------------- 1 | 'boolean', 35 | ]; 36 | 37 | public function model() 38 | { 39 | return $this->morphTo(); 40 | } 41 | 42 | public function documentTemplate(): BelongsTo 43 | { 44 | return $this->belongsTo(\TomatoPHP\FilamentDocs\Models\DocumentTemplate::class, 'document_template_id'); 45 | } 46 | 47 | public function team(): BelongsTo 48 | { 49 | return $this->belongsTo(config('filament-docs.team_model'), 'team_id'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Models/DocumentTemplate.php: -------------------------------------------------------------------------------- 1 | 'boolean', 34 | ]; 35 | 36 | public function team(): BelongsTo 37 | { 38 | return $this->belongsTo(config('filament-docs.team_model'), 'team_id'); 39 | } 40 | 41 | public function documentTemplateVars(): HasMany 42 | { 43 | return $this->hasMany(DocumentTemplateVar::class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Models/DocumentTemplateVar.php: -------------------------------------------------------------------------------- 1 | 'json', 30 | ]; 31 | 32 | public function documentTemplate(): BelongsTo 33 | { 34 | return $this->belongsTo(\TomatoPHP\FilamentDocs\Models\DocumentTemplate::class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Services/Contracts/DocsVar.php: -------------------------------------------------------------------------------- 1 | key($key); 20 | } 21 | 22 | /** 23 | * @return $this 24 | */ 25 | public function key(string $key): static 26 | { 27 | $this->key = $key; 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * @return $this 34 | */ 35 | public function label(string $label): static 36 | { 37 | $this->label = $label; 38 | 39 | return $this; 40 | } 41 | 42 | /** 43 | * @return $this 44 | */ 45 | public function model(string $model): static 46 | { 47 | $this->model = $model; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * @return $this 54 | */ 55 | public function column(string $column): static 56 | { 57 | $this->column = $column; 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * @return $this 64 | */ 65 | public function value(string | \Closure | null $value): static 66 | { 67 | $this->value = $value; 68 | 69 | return $this; 70 | } 71 | 72 | public function toArray(): array 73 | { 74 | return [ 75 | 'key' => $this->key, 76 | 'label' => $this->label, 77 | 'model' => $this->model, 78 | 'column' => $this->column, 79 | 'value' => $this->value, 80 | ]; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Services/FilamentDocsServices.php: -------------------------------------------------------------------------------- 1 | register($item); 28 | } 29 | } else { 30 | $this->vars[] = $var; 31 | } 32 | } 33 | 34 | public function load(): Collection 35 | { 36 | return collect($this->vars); 37 | } 38 | 39 | public function create(Model $model): void 40 | { 41 | foreach ($this->load()->where('value', null) as $item) { 42 | if (str($model->body)->contains($item->key)) { 43 | $exists = $model->documentTemplateVars()->where('var', $item->key)->first(); 44 | if (! $exists) { 45 | $model->documentTemplateVars()->create([ 46 | 'var' => $item->key, 47 | 'model' => $item->model, 48 | 'value' => $item->column, 49 | ]); 50 | } 51 | } 52 | } 53 | } 54 | 55 | public function body(int $template, ?array $vars = null): string 56 | { 57 | $templateBody = ''; 58 | $getDocumentTemplate = DocumentTemplate::query()->find($template); 59 | if ($getDocumentTemplate) { 60 | $templateBody = $getDocumentTemplate->body; 61 | if (! empty($vars)) { 62 | foreach ($vars as $var) { 63 | if (is_array($var) && isset($var['var'])) { 64 | $templateBody = str($templateBody)->replace($var['var'], is_array($var['model']::query()->find($var['value'])?->toArray()[$var['key']]) ? $var['model']::query()->find($var['value'])?->toArray()[$var['key']][app()->getLocale()] : $var['model']::query()->find($var['value'])?->toArray()[$var['key']])->toString(); 65 | } 66 | } 67 | } 68 | 69 | Carbon::setLocale(app()->getLocale()); 70 | $templateBody = str($templateBody) 71 | ->replace('$DAY', Carbon::now()->translatedFormat(config('filament-docs.dates.day'))) 72 | ->replace('$DATE', Carbon::now()->translatedFormat(config('filament-docs.dates.date'))) 73 | ->replace('$TIME', Carbon::now()->translatedFormat(config('filament-docs.dates.time'))) 74 | ->replace('$UUID', Str::uuid()) 75 | ->replace('$RANDOM', Str::random(6)) 76 | ->toString(); 77 | 78 | $fixedVars = array_merge(FilamentDocs::load()->where('value', '!=', null)->toArray(), $vars ?? []); 79 | foreach ($fixedVars as $item) { 80 | if (! is_array($item)) { 81 | $value = ''; 82 | if ($item->value instanceof \Closure) { 83 | $value = call_user_func($item->value); 84 | } else { 85 | $value = $item->value; 86 | } 87 | $templateBody = str($templateBody)->replace($item->key, $value)->toString(); 88 | } 89 | } 90 | } 91 | 92 | return $templateBody; 93 | } 94 | 95 | public function header(string $header) 96 | { 97 | $this->header = $header; 98 | } 99 | 100 | public function getHeader() 101 | { 102 | if ($this->header) { 103 | return view($this->header)->render(); 104 | } 105 | } 106 | 107 | public function footer(string $footer) 108 | { 109 | $this->footer = $footer; 110 | } 111 | 112 | public function getFooter() 113 | { 114 | if ($this->footer) { 115 | return view($this->footer)->render(); 116 | } 117 | } 118 | 119 | public function css(string $css) 120 | { 121 | $this->css = $css; 122 | } 123 | 124 | public function getCss() 125 | { 126 | if ($this->css) { 127 | return view($this->css)->render(); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Traits/InteractsWithDocs.php: -------------------------------------------------------------------------------- 1 | morphMany(\TomatoPHP\FilamentDocs\Models\Document::class, 'model'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /testbench.yaml: -------------------------------------------------------------------------------- 1 | providers: 2 | - BladeUI\Icons\BladeIconsServiceProvider 3 | - BladeUI\Heroicons\BladeHeroiconsServiceProvider 4 | - Filament\Actions\ActionsServiceProvider 5 | - Filament\FilamentServiceProvider 6 | - Filament\Forms\FormsServiceProvider 7 | - Filament\Infolists\InfolistsServiceProvider 8 | - Filament\Notifications\NotificationsServiceProvider 9 | - Filament\Support\SupportServiceProvider 10 | - Filament\Tables\TablesServiceProvider 11 | - Filament\Widgets\WidgetsServiceProvider 12 | - RyanChandler\BladeCaptureDirective\BladeCaptureDirectiveServiceProvider 13 | - TomatoPHP\FilamentIcons\FilamentIconsServiceProvider 14 | - FilamentTiptapEditor\FilamentTiptapEditorServiceProvider 15 | - TomatoPHP\FilamentDocs\FilamentDocsServiceProvider 16 | - TomatoPHP\FilamentDocs\Tests\AdminPanelProvider 17 | workbench: 18 | welcome: true 19 | install: true 20 | start: / 21 | guard: testing 22 | discovers: 23 | web: true 24 | api: false 25 | commands: false 26 | views: true 27 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | in(__DIR__); 6 | -------------------------------------------------------------------------------- /tests/database/database.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomatophp/filament-docs/c584390e2dd98b07de03115860f61ff3c4f30cee/tests/database/database.sqlite -------------------------------------------------------------------------------- /tests/database/factories/DocumentFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->word(), 16 | 'body' => $this->faker->text(), 17 | 'is_send' => $this->faker->boolean(), 18 | 'model_id' => $this->faker->uuid(), 19 | 'model_type' => $this->faker->word(), 20 | ]; 21 | } 22 | 23 | public function withId($id) 24 | { 25 | return $this->state([ 26 | 'document_template_id' => $id, 27 | ]); 28 | } 29 | 30 | public function withTeam($id) 31 | { 32 | return $this->state([ 33 | 'team_id' => $id, 34 | ]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/database/factories/DocumentTemplateFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->word(), 16 | 'body' => $this->faker->text(), 17 | 'is_active' => $this->faker->boolean(), 18 | 'icon' => 'heroicon-o-document-text', 19 | 'color' => $this->faker->word(), 20 | ]; 21 | } 22 | 23 | public function withTeam($id) 24 | { 25 | return $this->state([ 26 | 'team_id' => $id, 27 | ]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/database/factories/DocumentTemplateVarFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->word(), 16 | 'model' => $this->faker->word(), 17 | 'value' => $this->faker->text(), 18 | ]; 19 | } 20 | 21 | public function withId($id) 22 | { 23 | return $this->state([ 24 | 'document_template_id' => $id, 25 | ]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name(), 17 | 'email' => $this->faker->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 | -------------------------------------------------------------------------------- /tests/src/AdminPanelProvider.php: -------------------------------------------------------------------------------- 1 | default() 26 | ->id('admin') 27 | ->path('admin') 28 | ->login() 29 | ->pages([ 30 | Pages\Dashboard::class, 31 | ]) 32 | ->plugin( 33 | FilamentDocsPlugin::make() 34 | ) 35 | ->middleware([ 36 | EncryptCookies::class, 37 | AddQueuedCookiesToResponse::class, 38 | StartSession::class, 39 | AuthenticateSession::class, 40 | ShareErrorsFromSession::class, 41 | VerifyCsrfToken::class, 42 | SubstituteBindings::class, 43 | DisableBladeIconComponents::class, 44 | DispatchServingFilamentEvent::class, 45 | ]) 46 | ->authMiddleware([ 47 | Authenticate::class, 48 | ]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/src/DebugTest.php: -------------------------------------------------------------------------------- 1 | each->not->toBeUsed(); 5 | }); 6 | -------------------------------------------------------------------------------- /tests/src/DocumentResourceTest.php: -------------------------------------------------------------------------------- 1 | create()); 19 | }); 20 | 21 | it('can render document resource', function () { 22 | get(DocumentResource::getUrl())->assertSuccessful(); 23 | }); 24 | 25 | it('can list documents', function () { 26 | Document::query()->delete(); 27 | $template = DocumentTemplate::factory()->create(); 28 | $documents = Document::factory()->count(10)->withId($template->id)->create(); 29 | 30 | livewire(Pages\ListDocuments::class) 31 | ->loadTable() 32 | ->assertCanSeeTableRecords($documents) 33 | ->assertCountTableRecords(10); 34 | }); 35 | 36 | it('can render document type/for/key column in table', function () { 37 | $template = DocumentTemplate::factory()->create(); 38 | $documents = Document::factory()->count(10)->withId($template->id)->create(); 39 | 40 | livewire(Pages\ListDocuments::class) 41 | ->loadTable() 42 | ->assertCanRenderTableColumn('id') 43 | ->assertCanRenderTableColumn('ref') 44 | ->assertCanRenderTableColumn('is_send') 45 | ->assertCanRenderTableColumn('documentTemplate.name'); 46 | }); 47 | 48 | it('can render document list page', function () { 49 | livewire(Pages\ListDocuments::class)->assertSuccessful(); 50 | }); 51 | 52 | it('can render view document page', function () { 53 | $template = DocumentTemplate::factory()->create(); 54 | 55 | livewire(Pages\ListDocuments::class, [ 56 | 'record' => Document::factory()->withId($template->id)->create(), 57 | ]) 58 | ->mountAction('view') 59 | ->assertSuccessful(); 60 | }); 61 | 62 | it('can render document create page', function () { 63 | livewire(Pages\ListDocuments::class) 64 | ->mountAction('create') 65 | ->assertSuccessful(); 66 | }); 67 | 68 | it('can create new document', function () { 69 | $template = DocumentTemplate::factory()->create(); 70 | $newData = Document::factory()->withId($template->id)->make(); 71 | 72 | livewire(Pages\CreateDocument::class) 73 | ->fillForm([ 74 | 'document_template_id' => $template->id, 75 | 'body' => [], 76 | 'ref' => $newData->ref, 77 | ]) 78 | ->call('create') 79 | ->assertHasNoFormErrors(); 80 | 81 | assertDatabaseHas(Document::class, [ 82 | 'body' => $template->body, 83 | 'ref' => $newData->ref, 84 | ]); 85 | }); 86 | 87 | it('can validate document input', function () { 88 | livewire(Pages\CreateDocument::class) 89 | ->fillForm([ 90 | 'document_template_id' => null, 91 | 'body' => null, 92 | 'ref' => null, 93 | 'is_send' => null, 94 | ]) 95 | ->call('create') 96 | ->assertHasFormErrors([ 97 | 'document_template_id' => 'required', 98 | ]); 99 | }); 100 | 101 | it('can render document edit action', function () { 102 | $template = DocumentTemplate::factory()->create(); 103 | 104 | livewire(Pages\ListDocuments::class, [ 105 | 'record' => Document::factory()->withId($template->id)->create(), 106 | ]) 107 | ->mountAction('edit') 108 | ->assertSuccessful(); 109 | }); 110 | 111 | it('can render document edit page', function () { 112 | $template = DocumentTemplate::factory()->create(); 113 | get(DocumentResource::getUrl('edit', [ 114 | 'record' => Document::factory()->withId($template->id)->create(), 115 | ]))->assertSuccessful(); 116 | }); 117 | 118 | it('can retrieve document data', function () { 119 | $template = DocumentTemplate::factory()->create(); 120 | $document = Document::factory()->withId($template->id)->create(); 121 | 122 | livewire(Pages\EditDocument::class, [ 123 | 'record' => $document->getRouteKey(), 124 | ]) 125 | ->assertFormSet([ 126 | 'document_template_id' => $document->document_template_id, 127 | 'ref' => $document->ref, 128 | ]); 129 | }); 130 | 131 | it('can validate edit document input', function () { 132 | $template = DocumentTemplate::factory()->create(); 133 | $document = Document::factory()->withId($template->id)->create(); 134 | 135 | livewire(Pages\EditDocument::class, [ 136 | 'record' => $document->getRouteKey(), 137 | ]) 138 | ->fillForm([ 139 | 'document_template_id' => null, 140 | 'body' => null, 141 | 'ref' => null, 142 | 'is_send' => null, 143 | ]) 144 | ->call('save') 145 | ->assertHasFormErrors([ 146 | 'document_template_id' => 'required', 147 | ]); 148 | }); 149 | 150 | it('can save document data', function () { 151 | $template = DocumentTemplate::factory()->create(); 152 | $document = Document::factory()->withId($template->id)->create(); 153 | $newData = Document::factory()->withId($template->id)->make(); 154 | 155 | livewire(Pages\EditDocument::class, [ 156 | 'record' => $document->getRouteKey(), 157 | ]) 158 | ->fillForm([ 159 | 'document_template_id' => $template->id, 160 | 'body' => [], 161 | 'ref' => $newData->ref, 162 | ]) 163 | ->call('save') 164 | ->assertHasNoFormErrors(); 165 | 166 | expect($document->refresh()) 167 | ->document_template_id->toBe($newData->document_template_id); 168 | }); 169 | 170 | it('can delete document', function () { 171 | $template = DocumentTemplate::factory()->create(); 172 | $document = Document::factory()->withId($template->id)->create(); 173 | 174 | livewire(Pages\ListDocuments::class) 175 | ->callTableAction('delete', $document) 176 | ->assertHasNoTableActionErrors(); 177 | 178 | assertModelMissing($document); 179 | }); 180 | -------------------------------------------------------------------------------- /tests/src/Models/Document.php: -------------------------------------------------------------------------------- 1 | 'boolean', 39 | ]; 40 | 41 | public function model() 42 | { 43 | return $this->morphTo(); 44 | } 45 | 46 | public function documentTemplate(): BelongsTo 47 | { 48 | return $this->belongsTo(\TomatoPHP\FilamentDocs\Models\DocumentTemplate::class, 'document_template_id'); 49 | } 50 | 51 | public function team(): BelongsTo 52 | { 53 | return $this->belongsTo(config('filament-docs.team_model'), 'team_id'); 54 | } 55 | 56 | protected static function newFactory(): DocumentFactory 57 | { 58 | return DocumentFactory::new(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/src/Models/DocumentTemplate.php: -------------------------------------------------------------------------------- 1 | 'boolean', 37 | ]; 38 | 39 | public function team(): BelongsTo 40 | { 41 | return $this->belongsTo(config('filament-docs.team_model'), 'team_id'); 42 | } 43 | 44 | public function documentTemplateVars(): HasMany 45 | { 46 | return $this->hasMany(DocumentTemplateVar::class); 47 | } 48 | 49 | protected static function newFactory(): DocumentTemplateFactory 50 | { 51 | return DocumentTemplateFactory::new(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/src/Models/DocumentTemplateVar.php: -------------------------------------------------------------------------------- 1 | 'json', 33 | ]; 34 | 35 | public function documentTemplate(): BelongsTo 36 | { 37 | return $this->belongsTo(\TomatoPHP\FilamentDocs\Models\DocumentTemplate::class); 38 | } 39 | 40 | protected static function newFactory(): DocumentTemplateVarFactory 41 | { 42 | return DocumentTemplateVarFactory::new(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/src/Models/User.php: -------------------------------------------------------------------------------- 1 | plugins([ 10 | FilamentDocsPlugin::make(), 11 | ]); 12 | 13 | expect($panel->getPlugin('filament-docs')) 14 | ->not() 15 | ->toThrow(Exception::class); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/src/TestCase.php: -------------------------------------------------------------------------------- 1 | label('User ID') 41 | ->model(User::class) 42 | ->column('name'), 43 | ]); 44 | } 45 | 46 | protected function getPackageProviders($app): array 47 | { 48 | return [ 49 | ActionsServiceProvider::class, 50 | BladeCaptureDirectiveServiceProvider::class, 51 | BladeHeroiconsServiceProvider::class, 52 | BladeIconsServiceProvider::class, 53 | FilamentServiceProvider::class, 54 | FormsServiceProvider::class, 55 | InfolistsServiceProvider::class, 56 | LivewireServiceProvider::class, 57 | NotificationsServiceProvider::class, 58 | SupportServiceProvider::class, 59 | TablesServiceProvider::class, 60 | WidgetsServiceProvider::class, 61 | FilamentIconsServiceProvider::class, 62 | FilamentTiptapEditorServiceProvider::class, 63 | FilamentDocsServiceProvider::class, 64 | AdminPanelProvider::class, 65 | ]; 66 | } 67 | 68 | protected function defineDatabaseMigrations(): void 69 | { 70 | $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations'); 71 | } 72 | 73 | public function getEnvironmentSetUp($app): void 74 | { 75 | $app['config']->set('filament-users.model', User::class); 76 | $app['config']->set('database.default', 'testing'); 77 | $app['config']->set('filament-icons.cache', false); 78 | 79 | $app['config']->set('view.paths', [ 80 | ...$app['config']->get('view.paths'), 81 | __DIR__ . '/../resources/views', 82 | ]); 83 | } 84 | } 85 | --------------------------------------------------------------------------------