From 293101bef9a5f86571a460e6aa8578ae10b52871 Mon Sep 17 00:00:00 2001 From: Fady Mondy Date: Wed, 27 Nov 2024 15:52:10 +0200 Subject: [PATCH] =?UTF-8?q?first=20commit=20=F0=9F=94=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/CONTRIBUTING.md | 55 ++++ .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug.yml | 66 ++++ .github/ISSUE_TEMPLATE/config.yaml | 11 + .github/SECURITY.md | 3 + .github/dependabot.yml | 12 + .github/workflows/dependabot-auto-merge.yml | 32 ++ .github/workflows/fix-php-code-styling.yml | 30 ++ .github/workflows/tests.yml | 68 +++++ .php-cs-fixer.dist.php | 37 +++ 3x1io-tomato-categories.md | 14 + CHANGELOG.md | 3 + CODE_OF_CONDUCT.md | 128 ++++++++ LICENSE.md | 21 ++ README.md | 69 +++++ SECURITY.md | 3 + arts/.gitkeep | 0 composer.json | 68 +++++ config/.gitkeep | 0 config/filament-categories.php | 5 + ...1_11_23_130725_create_categories_table.php | 47 +++ module.json | 29 ++ phpunit.xml | 26 ++ pint.json | 14 + resources/lang/.gitkeep | 0 resources/lang/ar/messages.php | 32 ++ resources/lang/en/messages.php | 32 ++ src/Console/.gitkeep | 0 src/Console/FilamentCategoriesInstall.php | 44 +++ src/Filament/Resources/.gitkeep | 0 src/Filament/Resources/CategoryResource.php | 285 ++++++++++++++++++ .../CategoryResource/Pages/CreateCategory.php | 21 ++ .../CategoryResource/Pages/EditCategory.php | 23 ++ .../CategoryResource/Pages/ListCategories.php | 23 ++ src/FilamentCategoriesPlugin.php | 30 ++ src/FilamentCategoriesServiceProvider.php | 57 ++++ src/Models/.gitkeep | 0 src/Models/Category.php | 89 ++++++ testbench.yaml | 24 ++ tests/Pest.php | 5 + tests/database/factories/.gitkeep | 0 tests/database/factories/UserFactory.php | 23 ++ tests/src/AdminPanelProvider.php | 50 +++ tests/src/DebugTest.php | 5 + tests/src/Models/.gitkeep | 0 tests/src/Models/User.php | 34 +++ tests/src/TestCase.php | 53 ++++ 47 files changed, 1572 insertions(+) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yaml create mode 100644 .github/SECURITY.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 .github/workflows/fix-php-code-styling.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .php-cs-fixer.dist.php create mode 100644 3x1io-tomato-categories.md create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 arts/.gitkeep create mode 100644 composer.json create mode 100644 config/.gitkeep create mode 100644 config/filament-categories.php create mode 100644 database/migrations/2021_11_23_130725_create_categories_table.php create mode 100644 module.json create mode 100644 phpunit.xml create mode 100644 pint.json create mode 100644 resources/lang/.gitkeep create mode 100644 resources/lang/ar/messages.php create mode 100644 resources/lang/en/messages.php create mode 100644 src/Console/.gitkeep create mode 100644 src/Console/FilamentCategoriesInstall.php create mode 100644 src/Filament/Resources/.gitkeep create mode 100644 src/Filament/Resources/CategoryResource.php create mode 100644 src/Filament/Resources/CategoryResource/Pages/CreateCategory.php create mode 100644 src/Filament/Resources/CategoryResource/Pages/EditCategory.php create mode 100644 src/Filament/Resources/CategoryResource/Pages/ListCategories.php create mode 100644 src/FilamentCategoriesPlugin.php create mode 100644 src/FilamentCategoriesServiceProvider.php create mode 100644 src/Models/.gitkeep create mode 100644 src/Models/Category.php create mode 100644 testbench.yaml create mode 100644 tests/Pest.php create mode 100644 tests/database/factories/.gitkeep create mode 100644 tests/database/factories/UserFactory.php create mode 100644 tests/src/AdminPanelProvider.php create mode 100644 tests/src/DebugTest.php create mode 100644 tests/src/Models/.gitkeep create mode 100644 tests/src/Models/User.php create mode 100644 tests/src/TestCase.php diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..b0ee5d8 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +Please read and understand the contribution guide before creating an issue or pull request. + +## Etiquette + +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. + +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +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. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. + +## Requirements + +If the project maintainer has any additional requirements, you will find them listed here. + +- **[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). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **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. + +**Happy coding**! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..892ba05 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [3x1io] diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..8fa85ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,66 @@ +name: Bug Report +description: Report an Issue or Bug with the Package +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + We're sorry to hear you have a problem. Can you help us solve it by providing the following details. + - type: textarea + id: what-happened + attributes: + label: What happened? + description: What did you expect to happen? + placeholder: I cannot currently do X thing because when I do, it breaks X thing. + validations: + required: true + - type: textarea + id: how-to-reproduce + attributes: + label: How to reproduce the bug + description: How did this occur, please add any config values used and provide a set of reliable steps if possible. + placeholder: When I do X I see Y. + validations: + required: true + - type: input + id: package-version + attributes: + label: Package Version + description: What version of our Package are you running? Please be as specific as possible + placeholder: 2.0.0 + validations: + required: true + - type: input + id: php-version + attributes: + label: PHP Version + description: What version of PHP are you running? Please be as specific as possible + placeholder: 8.2.0 + validations: + required: true + - type: input + id: laravel-version + attributes: + label: Laravel Version + description: What version of Laravel are you running? Please be as specific as possible + placeholder: 9.0.0 + validations: + required: true + - type: dropdown + id: operating-systems + attributes: + label: Which operating systems does with happen with? + description: You may select more than one. + multiple: true + options: + - macOS + - Windows + - Linux + - type: textarea + id: notes + attributes: + label: Notes + description: Use this field to provide any other notes that you feel might be relevant to the issue. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 0000000..a6b748e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/tomatophp/filament-categories/discussions/new?category=q-a + about: Ask the community for help + - name: Request a feature + url: https://github.com/tomatophp/filament-categories/discussions/new?category=ideas + about: Share ideas for new features + - name: Report a security issue + url: https://github.com/tomatophp/filament-categories/security/policy + about: Learn how to notify us for sensitive bugs diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..b2490a9 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email info@3x1.io instead of using the issue tracker. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0bc378d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "dependencies" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..27c23a4 --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,32 @@ +name: dependabot-auto-merge +on: pull_request_target + +permissions: + pull-requests: write + contents: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2.2.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Auto-merge Dependabot PRs for semver-minor updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge Dependabot PRs for semver-patch updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/fix-php-code-styling.yml b/.github/workflows/fix-php-code-styling.yml new file mode 100644 index 0000000..e71024d --- /dev/null +++ b/.github/workflows/fix-php-code-styling.yml @@ -0,0 +1,30 @@ +name: 'PHP Code Styling' + +on: + workflow_dispatch: + push: + branches: + - master + paths: + - '**.php' + +permissions: + contents: write + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Fix PHP code style issues + uses: aglipanci/laravel-pint-action@v2 + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Format Code" + commit_user_name: 'GitHub Actions' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..14a3ed3 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,68 @@ +name: "Tests" + +on: + workflow_dispatch: + push: + branches: + - master + paths: + - '**.php' + pull_request: + types: + - opened + - synchronize + branches: + - master + paths: + - '**.php' + - '.github/workflows/tests.yml' + - 'phpunit.xml.dist' + - 'composer.json' + - 'composer.lock' + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest] + php: [8.3, 8.2] + laravel: [11.*] + stability: [prefer-stable] + include: + - laravel: 11.* + testbench: 9.* + carbon: 3.* + collision: 8.* + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Cache Dependencies + uses: actions/cache@v4 + with: + path: ~/.composer/cache/files + key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + + - name: Install Dependencies + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install Dependencies + run: | + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.carbon }}" "nunomaduro/collision:${{ matrix.collision }}" --no-interaction --no-update + composer update --${{ matrix.stability }} --prefer-dist --no-interaction + composer db + + - name: Execute tests + run: vendor/bin/pest diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..4123157 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,37 @@ +notPath('bootstrap/*') + ->notPath('storage/*') + ->notPath('resources/view/mail/*') + ->in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => true, + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ] + ]) + ->setFinder($finder); diff --git a/3x1io-tomato-categories.md b/3x1io-tomato-categories.md new file mode 100644 index 0000000..bf38bea --- /dev/null +++ b/3x1io-tomato-categories.md @@ -0,0 +1,14 @@ +--- +name: Categories Manager +slug: 3x1io-tomato-categories +author_slug: 3x1io +categories: [developer-tools] +description: Manage your categories for any model type with relation manager integration for TomatoPHP +discord_url: +docs_url: https://raw.githubusercontent.com/tomatophp/filament-categories/master/README.md +github_repository: tomatophp/filament-categories +has_dark_theme: true +has_translations: true +versions: [3] +publish_date: 2024-11-27 +--- diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e0a5086 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# V1.0.0 + +First release of the package diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..18c9147 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..e66364e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ccac422 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +![Screenshot](https://raw.githubusercontent.com/tomatophp/filament-categories/master/art/screenshot.jpg) + +# Filament categories + +[![Latest Stable Version](https://poser.pugx.org/tomatophp/filament-categories/version.svg)](https://packagist.org/packages/tomatophp/filament-categories) +[![License](https://poser.pugx.org/tomatophp/filament-categories/license.svg)](https://packagist.org/packages/tomatophp/filament-categories) +[![Downloads](https://poser.pugx.org/tomatophp/filament-categories/d/total.svg)](https://packagist.org/packages/tomatophp/filament-categories) + +Manage your categories for any model type with relation manager integration for TomatoPHP + +## Installation + +```bash +composer require tomatophp/filament-categories +``` +after install your package please run this command + +```bash +php artisan filament-categories:install +``` + +finally register the plugin on `/app/Providers/Filament/AdminPanelProvider.php` + +```php +->plugin(\TomatoPHP\FilamentCategories\FilamentCategoriesPlugin::make()) +``` + + +## Publish Assets + +you can publish config file by use this command + +```bash +php artisan vendor:publish --tag="filament-categories-config" +``` + +you can publish views file by use this command + +```bash +php artisan vendor:publish --tag="filament-categories-views" +``` + +you can publish languages file by use this command + +```bash +php artisan vendor:publish --tag="filament-categories-lang" +``` + +you can publish migrations file by use this command + +```bash +php artisan vendor:publish --tag="filament-categories-migrations" +``` + +## Changelog + +Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. + +## Security + +Please see [SECURITY](SECURITY.md) for more information about security. + +## Credits + +- [Fady Mondy](mailto:info@3x1.io) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..b2490a9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email info@3x1.io instead of using the issue tracker. diff --git a/arts/.gitkeep b/arts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5b5bfab --- /dev/null +++ b/composer.json @@ -0,0 +1,68 @@ +{ + "name": "tomatophp/filament-categories", + "type": "library", + "description": "Manage your categories for any model type with relation manager integration for TomatoPHP", + "keywords": [ + "php", + "laravel", + "template" + ], + "license": "MIT", + "autoload": { + "psr-4": { + "TomatoPHP\\FilamentCategories\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "TomatoPHP\\FilamentCategories\\Tests\\": "tests/src/", + "TomatoPHP\\FilamentCategories\\Tests\\Database\\Factories\\": "tests/database/factories" + } + }, + "extra": { + "laravel": { + "providers": [ + "TomatoPHP\\FilamentCategories\\FilamentCategoriesServiceProvider" + ] + } + }, + "authors": [ + { + "name": "Fady Mondy", + "email": "info@3x1.io" + } + ], + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "phpstan/extension-installer": true + } + }, + "scripts": { + "testbench": "vendor/bin/testbench package:discover --ansi", + "db": "vendor/bin/testbench package:create-sqlite-db && vendor/bin/testbench migrate", + "analyse": "vendor/bin/phpstan analyse src tests", + "test": "vendor/bin/pest", + "test-coverage": "vendor/bin/pest --coverage", + "format": "vendor/bin/pint" + }, + "require": { + "php": "^8.1|^8.2", + "tomatophp/console-helpers": "^1.1", + "filament/filament": "^3.2" + }, + "require-dev": { + "laravel/pint": "^1.18", + "livewire/livewire": "^2.10|^3.0", + "nunomaduro/larastan": "^2.9", + "orchestra/testbench": "^9.5", + "pestphp/pest": "^2.36", + "pestphp/pest-plugin-laravel": "^2.4", + "pestphp/pest-plugin-livewire": "^2.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.2", + "phpstan/phpstan-phpunit": "^1.4" + }, + "version": "v1.0.0" +} diff --git a/config/.gitkeep b/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/config/filament-categories.php b/config/filament-categories.php new file mode 100644 index 0000000..f5bf951 --- /dev/null +++ b/config/filament-categories.php @@ -0,0 +1,5 @@ +id(); + + $table->unsignedBigInteger('team_id')->nullable()->after('id'); + $table->foreignId('parent_id')->nullable()->references('id')->on('categories')->onDelete('cascade'); + $table->string('for')->default('posts')->nullable(); + $table->string('type')->default('category')->nullable(); + $table->json('name'); + $table->string('slug')->unique()->index(); + $table->json('description')->nullable(); + $table->string('icon')->nullable(); + $table->string('color')->nullable(); + + $table->boolean('is_active')->default(1)->nullable(); + $table->boolean('show_in_menu')->default(0)->nullable(); + + $table->timestamps(); + + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('categories'); + } +}; diff --git a/module.json b/module.json new file mode 100644 index 0000000..0ccecb1 --- /dev/null +++ b/module.json @@ -0,0 +1,29 @@ +{ + "name": "FilamentCategories", + "alias": "filament-categories", + "description": { + "ar": "Manage your categories for any model type with relation manager integration for TomatoPHP", + "en": "Manage your categories for any model type with relation manager integration for TomatoPHP", + "gr": "Manage your categories for any model type with relation manager integration for TomatoPHP", + "sp": "Manage your categories for any model type with relation manager integration for TomatoPHP" + }, + "keywords": [], + "priority": 0, + "providers": [ + "TomatoPHP\\FilamentCategories\\FilamentCategoriesServiceProvider" + ], + "files": [], + "title": { + "ar": "Filament categories", + "en": "Filament categories", + "gr": "Filament categories", + "sp": "Filament categories" + }, + "color": "#cc1448", + "icon": "heroicon-c-users", + "placeholder": "https://raw.githubusercontent.com/tomatophp/filament-categories/master/art/screenshot.jpg", + "type": "plugin", + "version": "v1.0.0", + "github" : "https://github.com/tomatophp/filament-categories", + "docs" : "https://github.com/tomatophp/filament-categories" +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..e542661 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,26 @@ + + + + + ./tests/ + + + + + ./src + + + + + + + diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..c6ddb49 --- /dev/null +++ b/pint.json @@ -0,0 +1,14 @@ +{ + "preset": "laravel", + "rules": { + "blank_line_before_statement": true, + "concat_space": { + "spacing": "one" + }, + "method_argument_space": true, + "single_trait_insert_per_statement": true, + "types_spaces": { + "space": "single" + } + } +} diff --git a/resources/lang/.gitkeep b/resources/lang/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resources/lang/ar/messages.php b/resources/lang/ar/messages.php new file mode 100644 index 0000000..d4729b9 --- /dev/null +++ b/resources/lang/ar/messages.php @@ -0,0 +1,32 @@ + [ + "title" => "التصنيفات", + "single" => "التصنيف", + "sections" => [ + "details" => [ + "title" => "تفاصيل التصنيف", + "description" => "إنشاء تصنيف جديد", + "columns" => [ + "name" => "الاسم", + "slug" => "الرابط المختصر", + "description" => "الوصف", + "icon" => "الأيقونة", + "color" => "اللون", + ] + ], + "status" => [ + "title" => "الحالة", + "description" => "إعدادات الحالة", + "columns" => [ + "parent_id" => "الأب", + "type" => "النوع", + "for" => "لـ", + "is_active" => "نشط", + "show_in_menu" => "إظهار في القائمة", + ] + ] + ] + ], +]; diff --git a/resources/lang/en/messages.php b/resources/lang/en/messages.php new file mode 100644 index 0000000..6d6660e --- /dev/null +++ b/resources/lang/en/messages.php @@ -0,0 +1,32 @@ + [ + "title" => "Categories", + "single" => "Category", + "sections" => [ + "details" => [ + "title" => "Category Details", + "description" => "Create a new category", + "columns" => [ + "name" => "Name", + "slug" => "Slug", + "description" => "Description", + "icon" => "Icon", + "color" => "Color", + ] + ], + "status" => [ + "title" => "Status", + "description" => "Status settings", + "columns" => [ + "parent_id" => "Parent", + "type" => "Type", + "for" => "For", + "is_active" => "Is Active", + "show_in_menu" => "Show In Menu", + ] + ] + ] + ], +]; diff --git a/src/Console/.gitkeep b/src/Console/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Console/FilamentCategoriesInstall.php b/src/Console/FilamentCategoriesInstall.php new file mode 100644 index 0000000..b1ebd2c --- /dev/null +++ b/src/Console/FilamentCategoriesInstall.php @@ -0,0 +1,44 @@ +info('Publish Vendor Assets'); + $this->artisanCommand(["migrate"]); + $this->artisanCommand(["optimize"]); + $this->info('Filament Categories installed successfully.'); + } +} diff --git a/src/Filament/Resources/.gitkeep b/src/Filament/Resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Filament/Resources/CategoryResource.php b/src/Filament/Resources/CategoryResource.php new file mode 100644 index 0000000..0e77cb0 --- /dev/null +++ b/src/Filament/Resources/CategoryResource.php @@ -0,0 +1,285 @@ +schema([ + Forms\Components\Grid::make([ + 'default' => 1, + 'sm' => 1, + 'md' => 6, + 'lg' => 12, + ])->schema([ + Forms\Components\Grid::make() + ->schema([ + Forms\Components\Section::make(trans('filament-cms::messages.content.category.sections.details.title')) + ->description(trans('filament-cms::messages.content.category.sections.details.description')) + ->schema([ + Forms\Components\TextInput::make('name') + ->afterStateUpdated( fn(Forms\Get $get, Forms\Set $set)=> $set('slug', Str::of($get('name'))->replace(' ', '-')->lower()->toString())) + ->label(trans('filament-cms::messages.content.category.sections.details.columns.name')) + ->lazy() + ->required(), + Forms\Components\TextInput::make('slug') + ->label(trans('filament-cms::messages.content.category.sections.details.columns.slug')) + ->required() + ->maxLength(255), + Forms\Components\Textarea::make('description') + ->columnSpanFull() + ->label(trans('filament-cms::messages.content.category.sections.details.columns.description')), + IconPicker::make('icon') + ->label(trans('filament-cms::messages.content.category.sections.details.columns.icon')), + Forms\Components\ColorPicker::make('color') + ->label(trans('filament-cms::messages.content.category.sections.details.columns.color')) + ]) + ->columns(2), + Forms\Components\Section::make(trans('filament-cms::messages.content.posts.sections.images.title')) + ->description(trans('filament-cms::messages.content.posts.sections.images.description')) + ->schema([ + Forms\Components\SpatieMediaLibraryFileUpload::make('feature_image') + ->label(trans('filament-cms::messages.content.posts.sections.images.columns.feature_image')) + ->collection('feature_image') + ->image() + ->maxFiles(1) + ->maxSize(2048) + ->maxWidth(1920), + Forms\Components\SpatieMediaLibraryFileUpload::make('cover_image') + ->label(trans('filament-cms::messages.content.posts.sections.images.columns.cover_image')) + ->collection('cover_image') + ->image() + ->maxFiles(1) + ->maxSize(2048) + ->maxWidth(1920), + ]), + ]) + ->columnSpan([ + 'sm' => 1, + 'md' => 4, + 'lg' => 8, + ]), + Forms\Components\Section::make(trans('filament-cms::messages.content.category.sections.status.title')) + ->description(trans('filament-cms::messages.content.category.sections.status.description')) + ->schema([ + Forms\Components\Select::make('for') + ->label(trans('filament-cms::messages.content.category.sections.status.columns.for')) + ->searchable() + ->live() + ->options(fn() => FilamentCMSTypes::getOptions()->pluck('label', 'key')->toArray()) + ->default('post'), + Forms\Components\Select::make('type') + ->hidden(function(Forms\Get $get){ + $for = FilamentCMSTypes::getOptions()->where('key', $get('for'))->first(); + if($for && count($for->sub)){ + return false; + } + }) + ->label(trans('filament-cms::messages.content.category.sections.status.columns.type')) + ->searchable() + ->options(fn(Forms\Get $get) => FilamentCMSTypes::getOptions()->where('key', $get('for'))->first()?->getSub()->pluck('label', 'key')->toArray()) + ->default('category'), + Forms\Components\Select::make('parent_id') + ->label(trans('filament-cms::messages.content.category.sections.status.columns.parent_id')) + ->searchable() + ->options(fn() => Category::query()->pluck('name', 'id')->toArray()), + Forms\Components\Toggle::make('is_active') + ->label(trans('filament-cms::messages.content.category.sections.status.columns.is_active')), + Forms\Components\Toggle::make('show_in_menu') + ->label(trans('filament-cms::messages.content.category.sections.status.columns.show_in_menu')), + ]) + ->columnSpan([ + 'sm' => 1, + 'md' => 2, + 'lg' => 4, + ]) + ]) + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\SpatieMediaLibraryImageColumn::make('feature_image') + ->label(trans('filament-cms::messages.content.posts.sections.images.columns.feature_image')) + ->defaultImageUrl(fn(Category $category)=> 'https://ui-avatars.com/api/?name='.Str::of($category->slug)->replace('-','+').'&color=FFFFFF&background=020617') + ->square() + ->collection('feature_image'), + Tables\Columns\TextColumn::make('name') + ->description(fn(Category $category)=> Str::of($category->description)->limit(50)) + ->label(trans('filament-cms::messages.content.category.sections.details.columns.name')) + ->searchable(), + Tables\Columns\TextColumn::make('for') + ->state(function (Category $category){ + return FilamentCMSTypes::getOptions()->where('key', $category->for)->first()?->label; + }) + ->color(function (Category $category){ + return FilamentCMSTypes::getOptions()->where('key', $category->for)->first()?->color; + }) + ->icon(function (Category $category){ + return FilamentCMSTypes::getOptions()->where('key', $category->for)->first()?->icon; + }) + ->badge() + ->sortable() + ->label(trans('filament-cms::messages.content.category.sections.status.columns.for')) + ->searchable(), + Tables\Columns\TextColumn::make('type') + ->state(function (Category $category){ + return FilamentCMSTypes::getOptions()->where('key', $category->for)->first()?->getSub()->where('key', $category->type)->first()?->label; + }) + ->color(function (Category $category){ + return FilamentCMSTypes::getOptions()->where('key', $category->for)->first()?->getSub()->where('key', $category->type)->first()?->color; + }) + ->icon(function (Category $category){ + return FilamentCMSTypes::getOptions()->where('key', $category->for)->first()?->getSub()->where('key', $category->type)->first()?->icon; + }) + ->badge() + ->sortable() + ->label(trans('filament-cms::messages.content.category.sections.status.columns.type')) + ->searchable(), + IconColumn::make('icon') + ->label(trans('filament-cms::messages.content.category.sections.details.columns.icon')) + ->sortable() + ->searchable(), + Tables\Columns\ColorColumn::make('color') + ->label(trans('filament-cms::messages.content.category.sections.details.columns.color')) + ->sortable() + ->searchable(), + Tables\Columns\ToggleColumn::make('is_active') + ->sortable() + ->label(trans('filament-cms::messages.content.category.sections.status.columns.is_active')), + Tables\Columns\ToggleColumn::make('show_in_menu') + ->sortable() + ->label(trans('filament-cms::messages.content.category.sections.status.columns.show_in_menu')), + Tables\Columns\TextColumn::make('parent.name') + ->sortable() + ->label(trans('filament-cms::messages.content.category.sections.status.columns.parent_id')) + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\Filter::make('for') + ->form([ + Forms\Components\Select::make('for') + ->label(trans('filament-cms::messages.content.category.sections.status.columns.for')) + ->searchable() + ->live() + ->options(fn() => FilamentCMSTypes::getOptions()->pluck('label', 'key')->toArray()), + Forms\Components\Select::make('type') + ->hidden(function(Forms\Get $get){ + $for = FilamentCMSTypes::getOptions()->where('key', $get('for'))->first(); + if($for && count($for->sub)){ + return false; + } + }) + ->label(trans('filament-cms::messages.content.category.sections.status.columns.type')) + ->searchable() + ->options(fn(Forms\Get $get) => FilamentCMSTypes::getOptions()->where('key', $get('for'))->first()?->getSub()->pluck('label', 'key')->toArray()), + + ]) + ->query(function (Builder $query, array $data){ + $query->when( + $data['for'], + fn(Builder $query, $for) => $query->where('for', $for) + )->when( + $data['type'], + fn(Builder $query, $type) => $query->where('type', $type) + ); + }), + Tables\Filters\TrashedFilter::make() + ]) + ->actions([ + Tables\Actions\ViewAction::make() + ->iconButton() + ->tooltip(__('filament-actions::view.single.label')), + Tables\Actions\EditAction::make() + ->iconButton() + ->tooltip(__('filament-actions::edit.single.label')), + Tables\Actions\DeleteAction::make() + ->iconButton() + ->tooltip(__('filament-actions::delete.single.label')), + Tables\Actions\ForceDeleteAction::make() + ->iconButton() + ->tooltip(__('filament-actions::force-delete.single.label')), + Tables\Actions\RestoreAction::make() + ->iconButton() + ->tooltip(__('filament-actions::restore.single.label')), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListCategories::route('/'), + 'create' => Pages\CreateCategory::route('/create'), + 'edit' => Pages\EditCategory::route('/{record}/edit'), + ]; + } +} diff --git a/src/Filament/Resources/CategoryResource/Pages/CreateCategory.php b/src/Filament/Resources/CategoryResource/Pages/CreateCategory.php new file mode 100644 index 0000000..3f66418 --- /dev/null +++ b/src/Filament/Resources/CategoryResource/Pages/CreateCategory.php @@ -0,0 +1,21 @@ +commands([ + \TomatoPHP\FilamentCategories\Console\FilamentCategoriesInstall::class, + ]); + + //Register Config file + $this->mergeConfigFrom(__DIR__.'/../config/filament-categories.php', 'filament-categories'); + + //Publish Config + $this->publishes([ + __DIR__.'/../config/filament-categories.php' => config_path('filament-categories.php'), + ], 'filament-categories-config'); + + //Register Migrations + $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); + + //Publish Migrations + $this->publishes([ + __DIR__.'/../database/migrations' => database_path('migrations'), + ], 'filament-categories-migrations'); + //Register views + $this->loadViewsFrom(__DIR__.'/../resources/views', 'filament-categories'); + + //Publish Views + $this->publishes([ + __DIR__.'/../resources/views' => resource_path('views/vendor/filament-categories'), + ], 'filament-categories-views'); + + //Register Langs + $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'filament-categories'); + + //Publish Lang + $this->publishes([ + __DIR__.'/../resources/lang' => base_path('lang/vendor/filament-categories'), + ], 'filament-categories-lang'); + + //Register Routes + $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); + + } + + public function boot(): void + { + //you boot methods here + } +} diff --git a/src/Models/.gitkeep b/src/Models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Models/Category.php b/src/Models/Category.php new file mode 100644 index 0000000..c17b0f6 --- /dev/null +++ b/src/Models/Category.php @@ -0,0 +1,89 @@ + 'boolean', + 'show_in_menu' => 'boolean', + ]; + + /** + * @return ?BelongsTo + */ + public function team(): ?BelongsTo + { + return class_exists(Team::class) ? $this->belongsTo(Team::class) : null; + } + + /** + * @return HasMany + */ + public function children(): HasMany + { + return $this->hasMany('TomatoPHP\FilamentCategories\Models\Category', 'parent_id'); + } + + + /** + * @return BelongsTo + */ + public function parent(): BelongsTo + { + return $this->belongsTo('TomatoPHP\FilamentCategories\Models\Category', 'parent_id'); + } + +} diff --git a/testbench.yaml b/testbench.yaml new file mode 100644 index 0000000..b603a20 --- /dev/null +++ b/testbench.yaml @@ -0,0 +1,24 @@ +providers: + - BladeUI\Icons\BladeIconsServiceProvider + - BladeUI\Heroicons\BladeHeroiconsServiceProvider + - Filament\Actions\ActionsServiceProvider + - Filament\FilamentServiceProvider + - Filament\Forms\FormsServiceProvider + - Filament\Infolists\InfolistsServiceProvider + - Filament\Notifications\NotificationsServiceProvider + - Filament\Support\SupportServiceProvider + - Filament\Tables\TablesServiceProvider + - Filament\Widgets\WidgetsServiceProvider + - RyanChandler\BladeCaptureDirective\BladeCaptureDirectiveServiceProvider + - TomatoPHP\FilamentCategories\FilamentCategoriesServiceProvider + - TomatoPHP\FilamentCategories\Tests\AdminPanelProvider +workbench: + welcome: true + install: true + start: / + guard: testing + discovers: + web: true + api: false + commands: false + views: true diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..7c6588a --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,5 @@ +in(__DIR__); diff --git a/tests/database/factories/.gitkeep b/tests/database/factories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/database/factories/UserFactory.php b/tests/database/factories/UserFactory.php new file mode 100644 index 0000000..02ffeb2 --- /dev/null +++ b/tests/database/factories/UserFactory.php @@ -0,0 +1,23 @@ + $this->faker->name(), + 'email' => $this->faker->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } +} diff --git a/tests/src/AdminPanelProvider.php b/tests/src/AdminPanelProvider.php new file mode 100644 index 0000000..1f71c3a --- /dev/null +++ b/tests/src/AdminPanelProvider.php @@ -0,0 +1,50 @@ +default() + ->id('admin') + ->path('admin') + ->login() + ->pages([ + Pages\Dashboard::class, + ]) + ->plugin( + FilamentCategoriesPlugin::make() + ) + ->middleware([ + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + AuthenticateSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + SubstituteBindings::class, + DisableBladeIconComponents::class, + DispatchServingFilamentEvent::class, + ]) + ->authMiddleware([ + Authenticate::class, + ]); + } +} diff --git a/tests/src/DebugTest.php b/tests/src/DebugTest.php new file mode 100644 index 0000000..91bf6cd --- /dev/null +++ b/tests/src/DebugTest.php @@ -0,0 +1,5 @@ +each->not->toBeUsed(); +}); diff --git a/tests/src/Models/.gitkeep b/tests/src/Models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/src/Models/User.php b/tests/src/Models/User.php new file mode 100644 index 0000000..c5ec895 --- /dev/null +++ b/tests/src/Models/User.php @@ -0,0 +1,34 @@ +set('database.default', 'sqlite'); + $app['config']->set('database.connections.sqlite.database', __DIR__ . '/../database/database.sqlite'); + + $app['config']->set('view.paths', [ + ...$app['config']->get('view.paths'), + __DIR__ . '/../resources/views', + ]); + } +}