├── .prettierignore
├── .prettierrc.json
├── .vscode
├── settings.json
└── tailwind.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── composer.json
├── config
└── modular.php
├── eslint.config.js
├── package-lock.json
├── package.json
├── pint.json
├── rector.php
├── resources
├── lang
│ ├── en
│ │ ├── en.json
│ │ └── validation.php
│ └── pt_BR
│ │ └── pt_BR.json
└── views
│ └── .gitkeep
├── src
├── Components
│ └── Translations.php
├── Console
│ ├── InstallCommand.php
│ ├── InstallerTraits
│ │ ├── BackendPackages.php
│ │ ├── CoreModules.php
│ │ ├── FrontendPackages.php
│ │ ├── ModuleExists.php
│ │ └── PestTests.php
│ ├── MakeComponentCommand.php
│ ├── MakeComposableCommand.php
│ ├── MakeControllerCommand.php
│ ├── MakeFactoryCommand.php
│ ├── MakeMigrationCommand.php
│ ├── MakeModelCommand.php
│ ├── MakeModuleCommand.php
│ ├── MakePageCommand.php
│ ├── MakeRouteCommand.php
│ ├── MakeSeederCommand.php
│ ├── MakeServiceCommand.php
│ ├── MakeTestCommand.php
│ ├── MakeValidateCommand.php
│ ├── PublishLaravelTranslationsCommand.php
│ ├── PublishSiteFilesCommand.php
│ ├── RegisterServiceProviderCommand.php
│ └── SiteTraits
│ │ ├── ConfigureViews.php
│ │ ├── CopySiteFiles.php
│ │ └── PublishConfigFile.php
└── ModularServiceProvider.php
└── stubs
├── .prettierrc.json
├── app
└── Http
│ └── Middleware
│ └── HandleInertiaRequests.php
├── config
└── auth.php
├── database
└── seeders
│ └── DatabaseSeeder.php
├── lang
├── bn
│ ├── auth.php
│ ├── pagination.php
│ ├── passwords.php
│ └── validation.php
└── pt_BR
│ ├── auth.php
│ ├── pagination.php
│ ├── passwords.php
│ └── validation.php
├── module-stub
└── modules
│ ├── Database
│ ├── Factories
│ │ └── ModelFactory.stub
│ ├── Migrations
│ │ └── create_table.stub
│ └── Seeders
│ │ └── ModuleSeeder.stub
│ ├── Http
│ ├── Controllers
│ │ └── ModuleController.stub
│ └── Requests
│ │ └── ModuleValidate.stub
│ ├── Models
│ └── Model.stub
│ ├── ModuleServiceProvider.stub
│ ├── Services
│ └── Service.stub
│ ├── Tests
│ └── ModuleTest.stub
│ └── routes
│ └── app.stub
├── modules
├── Acl
│ ├── AclServiceProvider.php
│ ├── Database
│ │ └── Seeders
│ │ │ ├── AclPermissionSeeder.php
│ │ │ └── AclRoleSeeder.php
│ ├── Http
│ │ ├── Controllers
│ │ │ ├── PermissionController.php
│ │ │ ├── RoleController.php
│ │ │ ├── RolePermissionController.php
│ │ │ ├── UserController.php
│ │ │ ├── UserPermissionController.php
│ │ │ └── UserRoleController.php
│ │ └── Requests
│ │ │ ├── PermissionValidate.php
│ │ │ └── RoleValidate.php
│ ├── Services
│ │ ├── GetUserPermissions.php
│ │ └── ListUserPermissions.php
│ ├── Tests
│ │ ├── Permission
│ │ │ ├── GetUserPermissionTest.php
│ │ │ ├── PermissionTest.php
│ │ │ └── UserPermissionTest.php
│ │ └── Role
│ │ │ ├── RolePermissionTest.php
│ │ │ ├── RoleTest.php
│ │ │ └── UserRoleTest.php
│ ├── config
│ │ └── config.php
│ └── routes
│ │ └── app.php
├── AdminAuth
│ ├── AdminAuthServiceProvider.php
│ ├── Http
│ │ ├── Controllers
│ │ │ ├── AuthenticatedSessionController.php
│ │ │ ├── NewPasswordController.php
│ │ │ └── PasswordResetLinkController.php
│ │ ├── Middleware
│ │ │ └── UserAuth.php
│ │ └── Requests
│ │ │ └── LoginRequest.php
│ ├── Notifications
│ │ └── ResetPassword.php
│ ├── Tests
│ │ ├── AuthenticationTest.php
│ │ └── PasswordResetTest.php
│ ├── routes
│ │ └── site.php
│ └── views
│ │ ├── emails
│ │ └── reset-password.blade.php
│ │ ├── forgot-password-form.blade.php
│ │ ├── layouts
│ │ └── master.blade.php
│ │ ├── login-form.blade.php
│ │ └── reset-password-form.blade.php
├── Dashboard
│ ├── DashboardServiceProvider.php
│ ├── Http
│ │ └── Controllers
│ │ │ └── DashboardController.php
│ ├── Tests
│ │ └── DashboardTest.php
│ └── routes
│ │ └── app.php
├── Support
│ ├── BaseServiceProvider.php
│ ├── Http
│ │ ├── Controllers
│ │ │ ├── AppController.php
│ │ │ └── BackendController.php
│ │ └── Requests
│ │ │ ├── JsonRequest.php
│ │ │ └── Request.php
│ ├── Models
│ │ └── BaseModel.php
│ ├── SupportServiceProvider.php
│ ├── Tests
│ │ ├── ActivityLogTraitTest.php
│ │ ├── EditorImageTraitTest.php
│ │ ├── FileNameGeneratorTraitTest.php
│ │ ├── SearchableTraitTest.php
│ │ ├── UpdateOrderTraitTest.php
│ │ └── UploadFileTraitTest.php
│ ├── Traits
│ │ ├── ActivityLog.php
│ │ ├── EditorImage.php
│ │ ├── FileNameGenerator.php
│ │ ├── Searchable.php
│ │ ├── UpdateOrder.php
│ │ └── UploadFile.php
│ ├── Validators
│ │ ├── recaptcha.php
│ │ └── required_editor.php
│ └── helpers.php
└── User
│ ├── Console
│ └── Commands
│ │ └── CreateUserCommand.php
│ ├── Database
│ ├── Factories
│ │ └── UserFactory.php
│ └── Migrations
│ │ └── 2024_01_25_000000_add_custom_fields_to_users_table.php
│ ├── Http
│ ├── Controllers
│ │ └── UserController.php
│ └── Requests
│ │ └── UserValidate.php
│ ├── Models
│ └── User.php
│ ├── Observers
│ └── UserObserver.php
│ ├── Tests
│ ├── CreateUserCommandTest.php
│ └── UserTest.php
│ ├── UserServiceProvider.php
│ └── routes
│ └── app.php
├── page-stub
├── Components
│ └── Component.stub
├── Composables
│ └── Composable.stub
├── Form.stub
└── Index.stub
├── public
└── favicon.svg
├── resources
├── css
│ └── app.css
├── images
│ └── logo.svg
├── js
│ ├── Components
│ │ ├── Auth
│ │ │ ├── AppAuthLogo.vue
│ │ │ └── AppAuthShell.vue
│ │ ├── DataTable
│ │ │ ├── AppDataSearch.vue
│ │ │ ├── AppDataTable.vue
│ │ │ ├── AppDataTableData.vue
│ │ │ ├── AppDataTableHead.vue
│ │ │ ├── AppDataTableRow.vue
│ │ │ └── AppPaginator.vue
│ │ ├── Form
│ │ │ ├── AppCheckbox.vue
│ │ │ ├── AppCombobox.vue
│ │ │ ├── AppFormErrors.vue
│ │ │ ├── AppInputDate.vue
│ │ │ ├── AppInputFile.vue
│ │ │ ├── AppInputPassword.vue
│ │ │ ├── AppInputText.vue
│ │ │ ├── AppLabel.vue
│ │ │ ├── AppRadioButton.vue
│ │ │ ├── AppTextArea.vue
│ │ │ ├── AppTipTapEditor.vue
│ │ │ └── TipTap
│ │ │ │ ├── TipTapButton.vue
│ │ │ │ ├── TipTapDivider.vue
│ │ │ │ └── extension-file-upload.js
│ │ ├── Menu
│ │ │ ├── AppBreadCrumb.vue
│ │ │ ├── AppBreadCrumbItem.vue
│ │ │ ├── AppMenu.vue
│ │ │ ├── AppMenuItem.vue
│ │ │ └── AppMenuSection.vue
│ │ ├── Message
│ │ │ ├── AppAlert.vue
│ │ │ ├── AppFlashMessage.vue
│ │ │ ├── AppToast.vue
│ │ │ └── AppTooltip.vue
│ │ ├── Misc
│ │ │ ├── AppButton.vue
│ │ │ ├── AppCard.vue
│ │ │ ├── AppImageNotAvailable.vue
│ │ │ ├── AppLink.vue
│ │ │ ├── AppSectionHeader.vue
│ │ │ └── AppTopBar.vue
│ │ └── Overlay
│ │ │ ├── AppConfirmDialog.vue
│ │ │ ├── AppModal.vue
│ │ │ └── AppSideBar.vue
│ ├── Composables
│ │ ├── useAuthCan.js
│ │ ├── useClickOutside.js
│ │ ├── useDataSearch.js
│ │ ├── useFormContext.js
│ │ ├── useFormErrors.js
│ │ ├── useIsMobile.js
│ │ └── useTitle.js
│ ├── Configs
│ │ └── menu.js
│ ├── Layouts
│ │ ├── AuthenticatedLayout.vue
│ │ └── GuestLayout.vue
│ ├── Pages
│ │ ├── AclPermission
│ │ │ ├── PermissionForm.vue
│ │ │ └── PermissionIndex.vue
│ │ ├── AclRole
│ │ │ ├── RoleForm.vue
│ │ │ └── RoleIndex.vue
│ │ ├── AclRolePermission
│ │ │ └── RolePermissionForm.vue
│ │ ├── AclUserPermission
│ │ │ └── UserPermissionForm.vue
│ │ ├── AclUserRole
│ │ │ └── UserRoleForm.vue
│ │ ├── AdminAuth
│ │ │ ├── ForgotPage.vue
│ │ │ ├── LoginForm.vue
│ │ │ └── ResetPassword.vue
│ │ ├── Dashboard
│ │ │ ├── Components
│ │ │ │ └── DashboardCard.vue
│ │ │ └── DashboardIndex.vue
│ │ └── User
│ │ │ ├── UserForm.vue
│ │ │ └── UserIndex.vue
│ ├── Plugins
│ │ └── Translations.js
│ ├── Resolvers
│ │ └── AppComponentsResolver.js
│ ├── Utils
│ │ ├── chunk.js
│ │ ├── debounce.js
│ │ ├── slug.js
│ │ └── truncate.js
│ └── app.js
└── views
│ ├── app.blade.php
│ └── components
│ └── translations.blade.php
├── routes
└── web.php
├── site
├── modules
│ ├── Index
│ │ ├── Http
│ │ │ ├── Controllers
│ │ │ │ └── IndexController.php
│ │ │ └── Requests
│ │ │ │ └── IndexValidate.php
│ │ ├── IndexServiceProvider.php
│ │ ├── Models
│ │ │ └── Index.php
│ │ ├── Tests
│ │ │ └── IndexTest.php
│ │ ├── routes
│ │ │ └── site.php
│ │ └── views
│ │ │ └── index.blade.php
│ └── Support
│ │ ├── SiteController.php
│ │ └── SiteModel.php
└── resources-site
│ ├── css
│ └── site.css
│ ├── images
│ └── home-img.png
│ ├── js
│ ├── Components
│ │ └── IndexExampleComponent.vue
│ ├── create-vue-app.js
│ └── index-app.js
│ └── views
│ ├── pagination
│ ├── simple-tailwind.blade.php
│ └── tailwind.blade.php
│ └── site-layout.blade.php
├── stack-configs
├── .prettierrc.json
├── .vscode
│ ├── settings.json
│ └── tailwind.json
├── eslint.config.js
├── jsconfig.json
├── postcss.config.mjs
├── tailwind.config.mjs
└── vite.config.js
└── tests
├── CreatesApplication.php
├── Pest.php
└── TestCase.php
/.prettierignore:
--------------------------------------------------------------------------------
1 | stubs/stack-configs/*
2 | stubs/site/stack-configs/*
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "semi": false,
4 | "trailingComma": "none",
5 | "arrowParens": "always"
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "css.customData": [".vscode/tailwind.json"],
3 |
4 | "[php]": {
5 | "editor.defaultFormatter": "open-southeners.laravel-pint",
6 | "editor.formatOnSave": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to `modular` will be documented in this file.
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Daniel Coimbra Cintra
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 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | If you discover a security vulnerability within Modular, please send an e-mail to Daniel Cintra via [modular@visualcom.com.br](mailto:modular@visualcom.com.br). All security vulnerabilities will be promptly addressed.
4 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "daniel-cintra/modular",
3 | "description": "A fast way to develop web apps using Laravel, Vue and Inertia.",
4 | "keywords": [
5 | "modular",
6 | "laravel",
7 | "modular"
8 | ],
9 | "homepage": "https://ismodular.com/",
10 | "license": "MIT",
11 | "authors": [
12 | {
13 | "name": "Daniel Coimbra Cintra",
14 | "email": "danic10@gmail.com",
15 | "role": "Developer"
16 | }
17 | ],
18 | "require": {
19 | "php": "^8.2",
20 | "illuminate/contracts": "^12.0",
21 | "inertiajs/inertia-laravel": "^2.0",
22 | "laravel/prompts": "^0.3.5",
23 | "spatie/laravel-activitylog": "^4.10",
24 | "spatie/laravel-package-tools": "^1.19",
25 | "spatie/laravel-permission": "^6.15",
26 | "tightenco/ziggy": "^2.5"
27 | },
28 | "require-dev": {
29 | "laravel/pint": "^1.13",
30 | "nunomaduro/collision": "^8.6",
31 | "orchestra/testbench": "^10.0",
32 | "pestphp/pest": "^3.7",
33 | "pestphp/pest-plugin-laravel": "^3.1"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "Modular\\Modular\\": "src",
38 | "Modular\\Modular\\Database\\Factories\\": "database/factories"
39 | }
40 | },
41 | "autoload-dev": {
42 | "psr-4": {
43 | "Modular\\Modular\\Tests\\": "tests"
44 | }
45 | },
46 | "scripts": {
47 | "analyse": "vendor/bin/phpstan analyse",
48 | "test": "vendor/bin/pest",
49 | "test-coverage": "vendor/bin/pest --coverage",
50 | "format": "vendor/bin/pint"
51 | },
52 | "config": {
53 | "sort-packages": true,
54 | "allow-plugins": {
55 | "pestphp/pest-plugin": true,
56 | "phpstan/extension-installer": true
57 | }
58 | },
59 | "extra": {
60 | "laravel": {
61 | "providers": [
62 | "Modular\\Modular\\ModularServiceProvider"
63 | ]
64 | }
65 | },
66 | "minimum-stability": "dev",
67 | "prefer-stable": true
68 | }
69 |
--------------------------------------------------------------------------------
/config/modular.php:
--------------------------------------------------------------------------------
1 | '/',
5 | 'default-logged-route' => 'dashboard.index',
6 | ];
7 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import vue from 'eslint-plugin-vue'
3 | import vueParser from 'vue-eslint-parser'
4 | import prettierConfig from 'eslint-config-prettier'
5 | import globals from 'globals'
6 |
7 | export default [
8 | // Base ESLint recommended rules
9 | js.configs.recommended,
10 |
11 | // Prettier configuration to disable conflicting rules
12 | {
13 | rules: {
14 | ...prettierConfig.rules,
15 | 'unicode-bom': 'off'
16 | }
17 | },
18 |
19 | // Vue plugin configuration
20 | {
21 | files: ['**/*.vue'],
22 | languageOptions: {
23 | parser: vueParser,
24 | parserOptions: {
25 | ecmaVersion: 'latest',
26 | sourceType: 'module'
27 | }
28 | },
29 | plugins: {
30 | vue
31 | },
32 | rules: {
33 | // Combine base and recommended Vue rules
34 | ...vue.configs.base.rules,
35 | ...vue.configs['vue3-recommended'].rules,
36 |
37 | '@stylistic/js/indent': 'off',
38 | '@stylistic/js/quotes': 'off',
39 |
40 | // Disable specific Vue rules
41 | 'vue/no-v-html': 'off',
42 | 'vue/comment-directive': 'off'
43 |
44 | // You can add other Vue-specific rules here
45 | }
46 | },
47 |
48 | // General JavaScript rules (for .js and .vue files)
49 | {
50 | files: ['**/*.{js,vue}'],
51 | rules: {
52 | // Disable general ESLint rules
53 | // 'no-undef': 'off'
54 | }
55 | },
56 |
57 | // Custom rules (if any)
58 | {
59 | languageOptions: {
60 | globals: {
61 | ...globals.browser,
62 | route: 'readonly'
63 | }
64 | },
65 | rules: {
66 | // Add your custom rules here
67 | }
68 | },
69 |
70 | // Ignore patterns
71 | {
72 | ignores: ['node_modules/*', 'vendor/*']
73 | }
74 | ]
75 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "type": "module",
4 | "scripts": {
5 | "lint": "eslint \"**/*.{js,vue}\"",
6 | "format": "prettier --write ."
7 | },
8 | "devDependencies": {
9 | "eslint": "^9.5.0",
10 | "eslint-config-prettier": "^10.0.2",
11 | "eslint-plugin-vue": "^9.32.0",
12 | "prettier": "^3.4.2",
13 | "vue-eslint-parser": "^9.4.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pint.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "laravel"
3 | }
4 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
11 | // __DIR__.'/config',
12 | // __DIR__.'/resources',
13 | // __DIR__.'/src',
14 | // __DIR__.'/tests',
15 | // __DIR__.'/stubs/app',
16 | // __DIR__.'/stubs/config',
17 | // __DIR__.'/stubs/database',
18 | // __DIR__.'/stubs/lang',
19 | // __DIR__.'/stubs/routes',
20 | ]);
21 |
22 | // register a single rule
23 | // $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
24 |
25 | // define sets of rules
26 | $rectorConfig->sets([
27 | LevelSetList::UP_TO_PHP_82,
28 | ]);
29 | };
30 |
--------------------------------------------------------------------------------
/resources/lang/en/validation.php:
--------------------------------------------------------------------------------
1 | 'The :attribute field is required.',
5 | ];
6 |
--------------------------------------------------------------------------------
/resources/views/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ModularThink/modular/62510efcd923a313442b9288b83ab0d1d22c778d/resources/views/.gitkeep
--------------------------------------------------------------------------------
/src/Components/Translations.php:
--------------------------------------------------------------------------------
1 | getLocale();
16 |
17 | if (app()->environment('production')) {
18 | $translations = cache()->rememberForever("translations_$locale", fn () => $this->getTranslations($locale));
19 | } else {
20 | $translations = $this->getTranslations($locale);
21 | }
22 |
23 | return view('components.translations', [
24 | 'translations' => $translations,
25 | ]);
26 | }
27 |
28 | private function getTranslations(string $locale): array
29 | {
30 | $appPHPTranslations = $this->getPHPTranslations(lang_path($locale));
31 |
32 | $appJsonTranslations = $this->getJsonTranslations(lang_path("$locale.json"));
33 | $modularJsonTranslations = $this->getJsonTranslations(lang_path("vendor/modular/$locale/$locale.json"));
34 |
35 | return array_merge($appPHPTranslations, $appJsonTranslations, $modularJsonTranslations);
36 | }
37 |
38 | private function getPHPTranslations(string $directory): array
39 | {
40 | if (! is_dir($directory)) {
41 | return [];
42 | }
43 |
44 | return collect(File::allFiles($directory))
45 | ->filter(fn ($file) => $file->getExtension() === 'php')->flatMap(fn ($file) => Arr::dot(File::getRequire($file->getRealPath())))->toArray();
46 | }
47 |
48 | private function getJsonTranslations(string $filePath): array
49 | {
50 | if (File::exists($filePath)) {
51 | return json_decode(File::get($filePath), true, 512, JSON_THROW_ON_ERROR);
52 | } else {
53 | return [];
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Console/InstallCommand.php:
--------------------------------------------------------------------------------
1 | components->info('Installing required stacks...');
23 |
24 | $this->setupBackendPackages();
25 |
26 | $this->installFrontendPackages();
27 |
28 | $this->components->info('Required stacks installed!');
29 |
30 | $this->configureCoreModules();
31 |
32 | $this->setupPestTests();
33 |
34 | $this->info('🎉 Modular successfully installed!');
35 |
36 | $this->info('🚀 To create your first user, please run: php artisan modular:create-user');
37 |
38 | return self::SUCCESS;
39 | }
40 |
41 | protected function phpBinary(): string
42 | {
43 | return (new PhpExecutableFinder)->find(false) ?: 'php';
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Console/InstallerTraits/BackendPackages.php:
--------------------------------------------------------------------------------
1 | components->info('Publishing vendor files...');
12 | $this->publishVendorFiles();
13 | }
14 |
15 | protected function publishVendorFiles(): void
16 | {
17 | (new Process([$this->phpBinary(), 'artisan', 'vendor:publish', '--provider=Spatie\Permission\PermissionServiceProvider'], base_path()))
18 | ->setTimeout(null)
19 | ->run(function ($type, $output) {
20 | $this->output->write($output);
21 | });
22 |
23 | (new Process([$this->phpBinary(), 'artisan', 'config:clear'], base_path()))
24 | ->setTimeout(null)
25 | ->run(function ($type, $output) {
26 | $this->output->write($output);
27 | });
28 |
29 | (new Process([$this->phpBinary(), 'artisan', 'vendor:publish', '--provider=Spatie\Activitylog\ActivitylogServiceProvider', '--tag=activitylog-migrations'], base_path()))
30 | ->setTimeout(null)
31 | ->run(function ($type, $output) {
32 | $this->output->write($output);
33 | });
34 |
35 | (new Process([$this->phpBinary(), 'artisan', 'vendor:publish', '--provider=Spatie\Activitylog\ActivitylogServiceProvider', '--tag=activitylog-config'], base_path()))
36 | ->setTimeout(null)
37 | ->run(function ($type, $output) {
38 | $this->output->write($output);
39 | });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Console/InstallerTraits/ModuleExists.php:
--------------------------------------------------------------------------------
1 | moduleName}"))) {
10 | $this->components->error("Module {$this->moduleName} does not exist.");
11 |
12 | return false;
13 | }
14 |
15 | return true;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Console/MakeComponentCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
25 | $this->componentName = Str::studly($this->argument('componentName'));
26 |
27 | if (! $this->moduleExists()) {
28 | return self::FAILURE;
29 | }
30 |
31 | $this->comment('Module '.$this->moduleName.' found, creating Component...');
32 | $this->createComponent();
33 |
34 | $this->generateComments();
35 |
36 | return self::SUCCESS;
37 | }
38 |
39 | private function createComponent(): void
40 | {
41 | $stub = file_get_contents(__DIR__.'/../../stubs/page-stub/Components/Component.stub');
42 |
43 | $stub = str_replace('{{ componentName }}', Str::camel($this->componentName), $stub);
44 |
45 | (new Filesystem)->ensureDirectoryExists(resource_path("js/Pages/{$this->moduleName}/Components/"));
46 |
47 | $path = resource_path("js/Pages/{$this->moduleName}/Components/{$this->componentName}.vue");
48 |
49 | file_put_contents($path, $stub);
50 | }
51 |
52 | private function generateComments(): void
53 | {
54 | $this->comment('In your Vue Component, import the new created Component as:');
55 | $this->info("import {$this->componentName} from './Components/{$this->componentName}.vue'");
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Console/MakeComposableCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
25 | $this->composableName = Str::studly($this->argument('composableName'));
26 |
27 | if (! $this->moduleExists()) {
28 | return self::FAILURE;
29 | }
30 |
31 | $this->comment('Module '.$this->moduleName.' found, creating Composable...');
32 | $this->createComposable();
33 |
34 | $this->generateComments();
35 |
36 | return self::SUCCESS;
37 | }
38 |
39 | private function createComposable(): void
40 | {
41 | $stub = file_get_contents(__DIR__.'/../../stubs/page-stub/Composables/Composable.stub');
42 |
43 | $stub = str_replace('{{ ComposableName }}', $this->composableName, $stub);
44 | $stub = str_replace('{{ composableName }}', Str::camel($this->composableName), $stub);
45 |
46 | (new Filesystem)->ensureDirectoryExists(resource_path("js/Pages/{$this->moduleName}/Composables/"));
47 |
48 | $path = resource_path("js/Pages/{$this->moduleName}/Composables/use{$this->composableName}.js");
49 |
50 | file_put_contents($path, $stub);
51 | }
52 |
53 | private function generateComments(): void
54 | {
55 | $camelCaseComposableName = Str::camel($this->composableName);
56 |
57 | $this->comment('In your Vue Component, import the composable:');
58 | $this->info("import use{$this->composableName} from './Composables/use{$this->composableName}'");
59 |
60 | $this->comment('And use it like:');
61 | $this->info("const { {$camelCaseComposableName} } = use{$this->composableName}()");
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Console/MakeFactoryCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
25 | $this->resourceName = Str::studly($this->argument('resourceName'));
26 |
27 | if (! $this->moduleExists()) {
28 | return self::FAILURE;
29 | }
30 |
31 | $this->comment('Module '.$this->moduleName.' found, creating Factory...');
32 | $this->createModuleFactory();
33 |
34 | return self::SUCCESS;
35 | }
36 |
37 | private function createModuleFactory(): void
38 | {
39 | (new Filesystem)->ensureDirectoryExists(base_path("modules/{$this->moduleName}/Database/Factories"));
40 |
41 | $stub = file_get_contents(__DIR__.'/../../stubs/module-stub/modules/Database/Factories/ModelFactory.stub');
42 |
43 | $stub = str_replace('{{ ModuleName }}', $this->moduleName, $stub);
44 | $stub = str_replace('{{ ResourceName }}', $this->resourceName, $stub);
45 | $stub = str_replace('{{ resourceName }}', Str::camel($this->resourceName), $stub);
46 |
47 | $path = base_path("modules/{$this->moduleName}/Database/Factories/{$this->resourceName}Factory.php");
48 |
49 | file_put_contents($path, $stub);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Console/MakeMigrationCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
25 | $this->migrationName = $this->argument('migrationName');
26 |
27 | if (! $this->moduleExists()) {
28 | return self::FAILURE;
29 | }
30 |
31 | $this->comment('Module '.$this->moduleName.' found, creating migration file...');
32 | $this->createMigrationFile();
33 |
34 | return self::SUCCESS;
35 | }
36 |
37 | private function createMigrationFile(): void
38 | {
39 | (new Filesystem)->ensureDirectoryExists(base_path("modules/{$this->moduleName}/Database/Migrations"));
40 |
41 | $this->call('make:migration', [
42 | 'name' => $this->migrationName,
43 | '--path' => "modules/{$this->moduleName}/Database/Migrations",
44 | ]);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Console/MakeModelCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
24 | $this->resourceName = Str::studly($this->argument('resourceName'));
25 |
26 | if (! $this->moduleExists()) {
27 | return self::FAILURE;
28 | }
29 |
30 | $this->comment('Module '.$this->moduleName.' found, creating Model...');
31 | $this->createModuleModel();
32 |
33 | return self::SUCCESS;
34 | }
35 |
36 | private function createModuleModel(): void
37 | {
38 | $stub = file_get_contents(__DIR__.'/../../stubs/module-stub/modules/Models/Model.stub');
39 |
40 | $stub = str_replace('{{ ModuleName }}', $this->moduleName, $stub);
41 | $stub = str_replace('{{ ResourceName }}', $this->resourceName, $stub);
42 | $stub = str_replace('{{ resourceName }}', Str::camel(Str::plural($this->resourceName)), $stub);
43 |
44 | $path = base_path("modules/{$this->moduleName}/Models/{$this->resourceName}.php");
45 |
46 | file_put_contents($path, $stub);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Console/MakeRouteCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
24 | $this->resourceName = Str::studly($this->argument('resourceName'));
25 |
26 | if (! $this->moduleExists()) {
27 | return self::FAILURE;
28 | }
29 |
30 | $this->comment('Module '.$this->moduleName.' found, creating Route...');
31 | $this->createModuleRoute();
32 |
33 | return self::SUCCESS;
34 | }
35 |
36 | private function createModuleRoute(): void
37 | {
38 | $stub = file_get_contents(__DIR__.'/../../stubs/module-stub/modules/routes/app.stub');
39 |
40 | $stub = str_replace('{{ resource-name }}', Str::snake($this->resourceName, '-'), $stub);
41 | $stub = str_replace('{{ ResourceName }}', $this->resourceName, $stub);
42 | $stub = str_replace('{{ resourceName }}', Str::camel($this->resourceName), $stub);
43 |
44 | $path = base_path("modules/{$this->moduleName}/routes/app.php");
45 |
46 | if (! file_exists($path)) {
47 | file_put_contents($path, $stub);
48 | } else {
49 | $stub = str_replace('resourceName).' routes', $stub);
51 |
52 | file_put_contents($path, $stub, FILE_APPEND);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Console/MakeSeederCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
25 | $this->resourceName = Str::studly($this->argument('resourceName'));
26 |
27 | if (! $this->moduleExists()) {
28 | return self::FAILURE;
29 | }
30 |
31 | $this->comment('Module '.$this->moduleName.' found, creating seeder file...');
32 | $this->createModuleSeeder();
33 |
34 | return self::SUCCESS;
35 | }
36 |
37 | private function createModuleSeeder(): void
38 | {
39 | (new Filesystem)->ensureDirectoryExists(base_path("modules/{$this->moduleName}/Database/Seeders"));
40 |
41 | $stub = file_get_contents(__DIR__.'/../../stubs/module-stub/modules/Database/Seeders/ModuleSeeder.stub');
42 |
43 | $stub = str_replace('{{ ModuleName }}', $this->moduleName, $stub);
44 | $stub = str_replace('{{ ResourceName }}', $this->resourceName, $stub);
45 |
46 | $path = base_path("modules/{$this->moduleName}/Database/Seeders/{$this->resourceName}.php");
47 |
48 | file_put_contents($path, $stub);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Console/MakeServiceCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
25 | $this->serviceName = Str::studly($this->argument('serviceName'));
26 |
27 | if (! $this->moduleExists()) {
28 | return self::FAILURE;
29 | }
30 |
31 | $this->comment('Module '.$this->moduleName.' found, creating Service...');
32 | $this->createModuleService();
33 |
34 | return self::SUCCESS;
35 | }
36 |
37 | private function createModuleService(): void
38 | {
39 | (new Filesystem)->ensureDirectoryExists(base_path("modules/{$this->moduleName}/Services/"));
40 |
41 | $stub = file_get_contents(__DIR__.'/../../stubs/module-stub/modules/Services/Service.stub');
42 |
43 | $stub = str_replace('{{ ModuleName }}', $this->moduleName, $stub);
44 | $stub = str_replace('{{ ServiceName }}', $this->serviceName, $stub);
45 |
46 | $path = base_path("modules/{$this->moduleName}/Services/{$this->serviceName}.php");
47 |
48 | file_put_contents($path, $stub);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Console/MakeTestCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
25 | $this->testFileName = $this->argument('resourceName');
26 |
27 | if (! $this->moduleExists()) {
28 | return self::FAILURE;
29 | }
30 |
31 | $this->comment('Module '.$this->moduleName.' found, creating test file...');
32 | $this->createTestFile();
33 |
34 | return self::SUCCESS;
35 | }
36 |
37 | private function createTestFile(): void
38 | {
39 | (new Filesystem)->ensureDirectoryExists(base_path("modules/{$this->moduleName}/Tests/"));
40 |
41 | $stub = file_get_contents(__DIR__.'/../../stubs/module-stub/modules/Tests/ModuleTest.stub');
42 |
43 | $path = base_path("modules/{$this->moduleName}/Tests/{$this->testFileName}Test.php");
44 |
45 | file_put_contents($path, $stub);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Console/MakeValidateCommand.php:
--------------------------------------------------------------------------------
1 | moduleName = Str::studly($this->argument('moduleName'));
24 | $this->resourceName = Str::studly($this->argument('resourceName'));
25 |
26 | if (! $this->moduleExists()) {
27 | return self::FAILURE;
28 | }
29 |
30 | $this->comment('Module '.$this->moduleName.' found, creating HTTP Request Validate...');
31 | $this->createValidateFile();
32 |
33 | return self::SUCCESS;
34 | }
35 |
36 | private function createValidateFile(): void
37 | {
38 | $stub = file_get_contents(__DIR__.'/../../stubs/module-stub/modules/Http/Requests/ModuleValidate.stub');
39 |
40 | $stub = str_replace('{{ ModuleName }}', $this->moduleName, $stub);
41 | $stub = str_replace('{{ ResourceName }}', $this->resourceName, $stub);
42 |
43 | $path = base_path("modules/{$this->moduleName}/Http/Requests/{$this->resourceName}Validate.php");
44 |
45 | file_put_contents($path, $stub);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Console/PublishLaravelTranslationsCommand.php:
--------------------------------------------------------------------------------
1 | option('lang');
19 |
20 | $availableLangs = ['pt_BR'];
21 |
22 | if (! in_array($lang, $availableLangs)) {
23 | $this->error("The language '{$lang}' is not available. Available languages: ".implode(', ', $availableLangs));
24 |
25 | return self::FAILURE;
26 | }
27 |
28 | $this->publishLang($lang);
29 |
30 | return self::SUCCESS;
31 | }
32 |
33 | private function publishLang(string $lang): void
34 | {
35 | $filesystem = new Filesystem;
36 |
37 | $filesystem->copyDirectory(
38 | __DIR__."/../../stubs/lang/{$lang}",
39 | lang_path("{$lang}")
40 | );
41 |
42 | $this->info("The language files for '{$lang}' were published successfully.");
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Console/PublishSiteFilesCommand.php:
--------------------------------------------------------------------------------
1 | publishModularConfigFile();
21 |
22 | $this->copySupportModuleFiles();
23 | $this->copyIndexModuleDir();
24 |
25 | $this->copyResourcesSiteDir();
26 |
27 | $this->configureViews();
28 |
29 | return self::SUCCESS;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Console/RegisterServiceProviderCommand.php:
--------------------------------------------------------------------------------
1 | argument('name');
16 |
17 | if ($this->registerServiceProvider($moduleName)) {
18 | $this->info("Service provider for module {$moduleName} registered successfully.");
19 |
20 | return self::SUCCESS;
21 | }
22 |
23 | $this->error("Service provider for module {$moduleName} is already registered.");
24 |
25 | return self::FAILURE;
26 | }
27 |
28 | public function registerServiceProvider(string $moduleName): bool
29 | {
30 | $providerPath = base_path('bootstrap/providers.php');
31 | $content = file_get_contents($providerPath);
32 |
33 | $providerClass = "Modules\\{$moduleName}\\{$moduleName}ServiceProvider::class";
34 |
35 | // Check if provider already exists
36 | if (strpos($content, $providerClass) !== false) {
37 | return false;
38 | }
39 |
40 | // Split content into lines
41 | $lines = explode("\n", $content);
42 |
43 | // Find the position of the closing bracket
44 | $returnArrayIndex = -1;
45 | foreach ($lines as $index => $line) {
46 | if (trim($line) === '];') {
47 | $returnArrayIndex = $index;
48 | break;
49 | }
50 | }
51 |
52 | if ($returnArrayIndex === -1) {
53 | return false;
54 | }
55 |
56 | // Insert the new provider before the closing bracket
57 | array_splice($lines, $returnArrayIndex, 0, " {$providerClass},");
58 |
59 | // Join the lines back together
60 | $updatedContent = implode("\n", $lines);
61 |
62 | return (bool) file_put_contents($providerPath, $updatedContent);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Console/SiteTraits/ConfigureViews.php:
--------------------------------------------------------------------------------
1 | call('config:publish', [
13 | 'name' => 'view',
14 | ]);
15 | }
16 |
17 | $this->info('Setting config/view.php...');
18 |
19 | $viewConfig = file_get_contents($configViewFilePath);
20 |
21 | $strSearch = "resource_path('views'),";
22 | $strReplace = "resource_path('views'),".PHP_EOL." base_path('resources-site/views'),";
23 |
24 | $updatedViewConfig = str_replace(
25 | $strSearch,
26 | $strReplace,
27 | $viewConfig
28 | );
29 |
30 | file_put_contents($configViewFilePath, $updatedViewConfig);
31 |
32 | $this->info('The config/view.php file was updated.');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Console/SiteTraits/CopySiteFiles.php:
--------------------------------------------------------------------------------
1 | info('Copying Support Module files...');
12 |
13 | copy(__DIR__.'/../../../stubs/site/modules/Support/SiteController.php', base_path('modules/Support/Http/Controllers/SiteController.php'));
14 | copy(__DIR__.'/../../../stubs/site/modules/Support/SiteModel.php', base_path('modules/Support/Models/SiteModel.php'));
15 |
16 | $this->info('Support Module files copied.');
17 | }
18 |
19 | private function copyResourcesSiteDir(): void
20 | {
21 | $this->info('Copying resources-site directory...');
22 |
23 | (new Filesystem)->ensureDirectoryExists(base_path('resources-site'));
24 | (new Filesystem)->copyDirectory(__DIR__.'/../../../stubs/site/resources-site', base_path('resources-site'));
25 |
26 | $this->info('The resources-site directory was copied.');
27 | }
28 |
29 | private function copyIndexModuleDir(): void
30 | {
31 | $this->info('Copying Index Module directory...');
32 |
33 | (new Filesystem)->ensureDirectoryExists(base_path('modules/Index'));
34 | (new Filesystem)->copyDirectory(__DIR__.'/../../../stubs/site/modules/Index', base_path('modules/Index'));
35 |
36 | $this->info('The Index Module directory was copied.');
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Console/SiteTraits/PublishConfigFile.php:
--------------------------------------------------------------------------------
1 | info('The Modular config file already exists. Moving on...');
11 | } else {
12 | $this->call('vendor:publish', [
13 | '--tag' => 'modular-config',
14 | ]);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/ModularServiceProvider.php:
--------------------------------------------------------------------------------
1 | name('modular')
37 | ->hasConfigFile()
38 | ->hasTranslations()
39 | ->hasViewComponents('modular', Translations::class)
40 | ->hasCommand(InstallCommand::class)
41 | ->hasCommand(MakeModuleCommand::class)
42 | ->hasCommand(MakeControllerCommand::class)
43 | ->hasCommand(MakeValidateCommand::class)
44 | ->hasCommand(MakeModelCommand::class)
45 | ->hasCommand(MakeRouteCommand::class)
46 | ->hasCommand(MakeServiceCommand::class)
47 | ->hasCommand(MakePageCommand::class)
48 | ->hasCommand(MakeComposableCommand::class)
49 | ->hasCommand(MakeComponentCommand::class)
50 | ->hasCommand(MakeTestCommand::class)
51 | ->hasCommand(MakeMigrationCommand::class)
52 | ->hasCommand(MakeSeederCommand::class)
53 | ->hasCommand(PublishLaravelTranslationsCommand::class)
54 | ->hasCommand(PublishSiteFilesCommand::class)
55 | ->hasCommand(MakeFactoryCommand::class)
56 | ->hasCommand(RegisterServiceProviderCommand::class);
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/stubs/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "semi": false,
4 | "trailingComma": "none",
5 | "arrowParens": "always"
6 | }
7 |
--------------------------------------------------------------------------------
/stubs/app/Http/Middleware/HandleInertiaRequests.php:
--------------------------------------------------------------------------------
1 | user();
37 |
38 | return array_merge(parent::share($request), [
39 | 'auth' => [
40 | 'user' => $user,
41 | 'permissions' => $user ? (new ListUserPermissions)->run($user->id) : [],
42 | 'isRootUser' => $user ? ($user->hasRole('root') ? true : false) : false,
43 | ],
44 | 'ziggy' => fn () => array_merge((new Ziggy)->toArray(), [
45 | 'location' => $request->url(),
46 | ]),
47 | 'flash' => fn () => [
48 | 'success' => $request->session()->get('success'),
49 | 'error' => $request->session()->get('error'),
50 | ],
51 | ]);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/stubs/database/seeders/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | call([
14 | AclRoleSeeder::class,
15 | AclPermissionSeeder::class,
16 | ]);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/stubs/lang/bn/auth.php:
--------------------------------------------------------------------------------
1 | 'এই পরিচয়পত্র আমাদের রেকর্ডের সাথে মেলে না।',
17 | 'password' => 'পাসওয়ার্ড ভুল।',
18 | 'throttle' => 'লগইন করার জন্য অনেকবার চেষ্টা করেছেন, :seconds সেকেন্ড পরে পুনরায় চেষ্টা করুন।',
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/stubs/lang/bn/pagination.php:
--------------------------------------------------------------------------------
1 | '« পূর্বে',
17 | 'next' => 'পরবর্তি »',
18 |
19 | 'Showing' => 'দেখাচ্ছে',
20 | 'to' => 'পর্যন্ত',
21 | 'of' => 'এর মধ্যে',
22 | 'results' => 'ফলাফলসমুহ',
23 |
24 | ];
25 |
--------------------------------------------------------------------------------
/stubs/lang/bn/passwords.php:
--------------------------------------------------------------------------------
1 | 'আপনার পাসওয়ার্ড পুনরায় সেট করা হয়েছে!',
17 | 'sent' => 'আমরা আপনার পাসওয়ার্ড পুনরায় সেট করার লিঙ্ক ই-মেইল করেছি!',
18 | 'throttle' => 'লগইন করার জন্য অনেকবার চেষ্টা করেছেন, :seconds সেকেন্ড পরে পুনরায় চেষ্টা করুন।',
19 | 'token' => 'এই পাসওয়ার্ড রিসেট টোকেনটি সঠিক নয়।',
20 | 'user' => 'এই ই-মেইল দিয়ে কোন ব্যবহারকারী খুঁজে পাওয়া যাচ্ছে না',
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/stubs/lang/pt_BR/auth.php:
--------------------------------------------------------------------------------
1 | 'Credenciais incorretas.',
17 | 'password' => 'A senha informada está incorreta.',
18 | 'throttle' => 'Tentativas de login excedidas. Por favor tente novamente em :seconds segundos.',
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/stubs/lang/pt_BR/pagination.php:
--------------------------------------------------------------------------------
1 | '« Anterior',
17 | 'next' => 'Próxima »',
18 |
19 | 'Showing' => 'Mostrando',
20 | 'to' => 'até',
21 | 'of' => 'de',
22 | 'results' => 'resultados',
23 |
24 | ];
25 |
--------------------------------------------------------------------------------
/stubs/lang/pt_BR/passwords.php:
--------------------------------------------------------------------------------
1 | 'Sua senha foi redefinida!',
17 | 'sent' => 'Enviamos um email para redefinição da sua senha!',
18 | 'throttled' => 'Por favor aguarde para tentar novamente.',
19 | 'token' => 'O token para redefinição da senha é inválido.',
20 | 'user' => 'Nós não encontramos um usuário com este email definido.',
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/stubs/module-stub/modules/Database/Factories/ModelFactory.stub:
--------------------------------------------------------------------------------
1 | faker->unique()->sentence(4);
15 |
16 | return [
17 | 'name' => $name,
18 |
19 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
20 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
21 | ];
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/stubs/module-stub/modules/Database/Migrations/create_table.stub:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('name');
17 | $table->timestamps();
18 | $table->softDeletes();
19 | });
20 | }
21 |
22 | /**
23 | * Reverse the migrations.
24 | */
25 | public function down(): void
26 | {
27 | Schema::dropIfExists('${{ resourceName }}');
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/stubs/module-stub/modules/Database/Seeders/ModuleSeeder.stub:
--------------------------------------------------------------------------------
1 | search(request('searchContext'), request('searchTerm'))
17 | ->paginate(request('rowsPerPage', 10))
18 | ->withQueryString()
19 | ->through(fn (${{ resourceName }}) => [
20 | 'id' => ${{ resourceName }}->id,
21 | 'name' => ${{ resourceName }}->name,
22 | 'created_at' => ${{ resourceName }}->created_at->format('d/m/Y H:i') . 'h'
23 | ]);
24 |
25 | return inertia('{{ ModuleName }}/{{ ResourceName }}Index', [
26 | '{{ resourceNameCamelPlural }}' => ${{ resourceNameCamelPlural }}
27 | ]);
28 | }
29 |
30 | public function create(): Response
31 | {
32 | return inertia('{{ ModuleName }}/{{ ResourceName }}Form');
33 | }
34 |
35 | public function store({{ ResourceName }}Validate $request): RedirectResponse
36 | {
37 | {{ ResourceName }}::create($request->validated());
38 |
39 | return redirect()->route('{{ resourceName }}.index')
40 | ->with('success', '{{ ResourceName }} created.');
41 | }
42 |
43 | public function edit(int $id): Response
44 | {
45 | ${{ resourceName }} = {{ ResourceName }}::find($id);
46 |
47 | return inertia('{{ ModuleName }}/{{ ResourceName }}Form', [
48 | '{{ resourceName }}' => ${{ resourceName }}
49 | ]);
50 | }
51 |
52 | public function update({{ ResourceName }}Validate $request, int $id): RedirectResponse
53 | {
54 | ${{ resourceName }} = {{ ResourceName }}::findOrFail($id);
55 |
56 | ${{ resourceName }}->update($request->validated());
57 |
58 | return redirect()->route('{{ resourceName }}.index')
59 | ->with('success', '{{ ResourceName }} updated.');
60 | }
61 |
62 | public function destroy(int $id): RedirectResponse
63 | {
64 | {{ ResourceName }}::findOrFail($id)->delete();
65 |
66 | return redirect()->route('{{ resourceName }}.index')
67 | ->with('success', '{{ ResourceName }} deleted.');
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/stubs/module-stub/modules/Http/Requests/ModuleValidate.stub:
--------------------------------------------------------------------------------
1 | 'required',
13 | ];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/stubs/module-stub/modules/Models/Model.stub:
--------------------------------------------------------------------------------
1 | loadMigrationsFrom(__DIR__.'/Database/Migrations');
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/stubs/module-stub/modules/Services/Service.stub:
--------------------------------------------------------------------------------
1 | assertTrue(true);
10 | // });
11 |
--------------------------------------------------------------------------------
/stubs/module-stub/modules/routes/app.stub:
--------------------------------------------------------------------------------
1 | name('{{ resourceName }}.index');
9 |
10 | Route::get('{{ resource-name }}/create', [
11 | {{ ResourceName }}Controller::class, 'create',
12 | ])->name('{{ resourceName }}.create');
13 |
14 | Route::post('{{ resource-name }}', [
15 | {{ ResourceName }}Controller::class, 'store',
16 | ])->name('{{ resourceName }}.store');
17 |
18 | Route::get('{{ resource-name }}/{id}/edit', [
19 | {{ ResourceName }}Controller::class, 'edit',
20 | ])->name('{{ resourceName }}.edit');
21 |
22 | Route::put('{{ resource-name }}/{id}', [
23 | {{ ResourceName }}Controller::class, 'update',
24 | ])->name('{{ resourceName }}.update');
25 |
26 | Route::delete('{{ resource-name }}/{id}', [
27 | {{ ResourceName }}Controller::class, 'destroy',
28 | ])->name('{{ resourceName }}.destroy');
29 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/AclServiceProvider.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom(__DIR__.'/config/config.php', 'acl');
25 |
26 | Gate::before(function ($user, $ability) {
27 | return $user->hasRole('root') ? true : null; // Must be null, not false. (From Spatie Permission Package documentation.)
28 | });
29 |
30 | parent::boot();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Database/Seeders/AclPermissionSeeder.php:
--------------------------------------------------------------------------------
1 | getPermissions();
18 |
19 | foreach ($permissions as $permission) {
20 | Permission::create([
21 | 'name' => $permission,
22 | 'guard_name' => 'user',
23 | ]);
24 | }
25 |
26 | Schema::enableForeignKeyConstraints();
27 | }
28 |
29 | private function getPermissions(): array
30 | {
31 | return [
32 | // Main Menu
33 | 'Dashboard',
34 |
35 | // Acl: Access Control List
36 | 'Acl',
37 | 'Acl: User - List',
38 | 'Acl: Permission - List',
39 | 'Acl: Role - List',
40 |
41 | // User/UserIndex.vue
42 | 'Acl: User: Role - Edit',
43 | 'Acl: User: Permission - Edit',
44 | 'Acl: User - Create',
45 | 'Acl: User - Edit',
46 | 'Acl: User - Delete',
47 |
48 | // AclPermission/PermissionIndex.vue
49 | 'Acl: Permission - Create',
50 | 'Acl: Permission - Edit',
51 | 'Acl: Permission - Delete',
52 |
53 | // AclRole/RoleIndex.vue
54 | 'Acl: Role - Create',
55 | 'Acl: Role - Edit',
56 | 'Acl: Role - Delete',
57 |
58 | // AclRolePermission/RolePermissionForm.vue
59 | 'Acl: Role: Permission - Edit',
60 | ];
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Database/Seeders/AclRoleSeeder.php:
--------------------------------------------------------------------------------
1 | 'root',
19 | 'guard_name' => 'user',
20 | ]);
21 |
22 | Schema::enableForeignKeyConstraints();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Controllers/PermissionController.php:
--------------------------------------------------------------------------------
1 | where(request('searchContext'), 'like', "%{$searchTerm}%");
16 | })
17 | ->orderBy('guard_name')
18 | ->orderBy('name')
19 | ->paginate(request('rowsPerPage', 10))
20 | ->withQueryString()
21 | ->through(fn ($permission) => [
22 | 'id' => $permission->id,
23 | 'name' => $permission->name,
24 | 'guard' => $permission->guard,
25 |
26 | ]);
27 |
28 | return inertia('AclPermission/PermissionIndex', [
29 | 'permissions' => $permissions,
30 | ]);
31 | }
32 |
33 | public function create()
34 | {
35 | return inertia('AclPermission/PermissionForm');
36 | }
37 |
38 | public function store(PermissionValidate $request)
39 | {
40 | $params = $request->validated();
41 | $params['guard_name'] = 'user';
42 | Permission::create($params);
43 |
44 | return redirect()->route('aclPermission.index')
45 | ->with('success', 'Permission created');
46 | }
47 |
48 | public function edit($id)
49 | {
50 | $permission = Permission::find($id);
51 |
52 | return inertia('AclPermission/PermissionForm', [
53 | 'permission' => $permission,
54 | ]);
55 | }
56 |
57 | public function update(PermissionValidate $request, $id)
58 | {
59 | $permission = Permission::findOrFail($id);
60 |
61 | $permission->update($request->validated());
62 |
63 | return redirect()->route('aclPermission.index')
64 | ->with('success', 'Permission updated');
65 | }
66 |
67 | public function destroy($id)
68 | {
69 | Permission::findOrFail($id)->delete();
70 |
71 | return redirect()->route('aclPermission.index')
72 | ->with('success', 'Permission deleted');
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Controllers/RoleController.php:
--------------------------------------------------------------------------------
1 | where(request('searchContext'), 'like', "%{$searchTerm}%");
16 | })
17 | ->orderBy('name')
18 | ->paginate(request('rowsPerPage', 10))
19 | ->withQueryString()
20 | ->through(fn ($role) => [
21 | 'id' => $role->id,
22 | 'name' => $role->name,
23 | 'guard_name' => $role->guard_name,
24 | ]);
25 |
26 | return inertia('AclRole/RoleIndex', [
27 | 'roles' => $roles,
28 | ]);
29 | }
30 |
31 | public function create()
32 | {
33 | return inertia('AclRole/RoleForm');
34 | }
35 |
36 | public function store(RoleValidate $request)
37 | {
38 | $params = $request->validated();
39 | $params['guard_name'] = 'user';
40 | Role::create($params);
41 |
42 | return redirect()->route('aclRole.index')
43 | ->with('success', 'Role created');
44 | }
45 |
46 | public function edit($id)
47 | {
48 | $role = Role::find($id);
49 |
50 | return inertia('AclRole/RoleForm', [
51 | 'role' => $role,
52 | ]);
53 | }
54 |
55 | public function update(RoleValidate $request, $id)
56 | {
57 | $role = Role::findOrFail($id);
58 |
59 | $role->update($request->validated());
60 |
61 | return redirect()->route('aclRole.index')
62 | ->with('success', 'Role updated');
63 | }
64 |
65 | public function destroy($id)
66 | {
67 | Role::findOrFail($id)->delete();
68 |
69 | return redirect()->route('aclRole.index')
70 | ->with('success', 'Role deleted');
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Controllers/RolePermissionController.php:
--------------------------------------------------------------------------------
1 | function ($q) {
14 | $q->get(['id', 'name']);
15 | }])->findOrFail($id);
16 |
17 | $role->permissions->map(function ($permission) {
18 | unset($permission->pivot);
19 |
20 | return $permission;
21 | });
22 |
23 | $permissions = Permission::orderBy('name')->get(['id', 'name']);
24 |
25 | return inertia('AclRolePermission/RolePermissionForm', [
26 | 'role' => $role,
27 | 'permissions' => $permissions,
28 | ]);
29 | }
30 |
31 | public function update($id)
32 | {
33 | $role = Role::findOrFail($id);
34 |
35 | $role->syncPermissions(request('rolePermissions'));
36 |
37 | return redirect()->route('aclRole.index')
38 | ->with('success', 'Role permissions updated');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Controllers/UserController.php:
--------------------------------------------------------------------------------
1 | orderBy('name')->get();
14 |
15 | return compact('users');
16 | }
17 |
18 | public function getUserRolesAndPermissions(Request $request)
19 | {
20 | $user = auth()->guard('user')->user();
21 |
22 | return [
23 | 'userRoles' => $user->getRoleNames(),
24 | 'userPermissions' => $user->getPermissionsViaRoles()->pluck('name'),
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Controllers/UserPermissionController.php:
--------------------------------------------------------------------------------
1 | run($id);
16 |
17 | $permissions = Permission::orderBy('name')->get(['id', 'name']);
18 |
19 | return inertia('AclUserPermission/UserPermissionForm', [
20 | 'user' => $user,
21 | 'userPermissions' => $userPermissions,
22 | 'permissions' => $permissions,
23 | ]);
24 | }
25 |
26 | public function update($id)
27 | {
28 | $user = User::findOrFail($id);
29 |
30 | $user->syncPermissions(request('userPermissions'));
31 |
32 | return redirect()->route('user.index')
33 | ->with('success', 'User permissions updated');
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Controllers/UserRoleController.php:
--------------------------------------------------------------------------------
1 | function ($q) {
14 | $q->get(['id', 'name']);
15 | }])->findOrFail($id);
16 |
17 | $user->roles->map(function ($role) {
18 | unset($role->pivot);
19 |
20 | return $role;
21 | });
22 |
23 | $roles = Role::orderBy('name')->get(['id', 'name']);
24 |
25 | return inertia('AclUserRole/UserRoleForm', [
26 | 'user' => $user,
27 | 'roles' => $roles,
28 | ]);
29 | }
30 |
31 | public function update($id)
32 | {
33 | $user = User::findOrFail($id);
34 |
35 | $user->syncRoles(request('userRoles'));
36 |
37 | return redirect()->route('user.index')
38 | ->with('success', 'User roles updated');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Requests/PermissionValidate.php:
--------------------------------------------------------------------------------
1 | [
21 | 'required',
22 | 'string',
23 | 'min:3',
24 | 'max:255',
25 | Rule::unique(Permission::class)->ignore($this->id),
26 | ],
27 |
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Http/Requests/RoleValidate.php:
--------------------------------------------------------------------------------
1 | [
21 | 'required',
22 | 'string',
23 | 'min:2',
24 | 'max:255',
25 | Rule::unique(Role::class)->ignore($this->id),
26 | ],
27 |
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Services/GetUserPermissions.php:
--------------------------------------------------------------------------------
1 | function ($query) {
13 | $query->get(['id', 'name']);
14 | }])->findOrFail($userId);
15 |
16 | // if has direct permissions use it
17 | if ($user->permissions->count()) {
18 | return $this->mapPermissions($user->permissions);
19 | }
20 |
21 | // get the permissions via roles
22 | return $this->mapPermissions($user->getAllPermissions());
23 | }
24 |
25 | private function mapPermissions(Collection $permissions): array
26 | {
27 | return $permissions->map(fn ($permission) => [
28 | 'id' => $permission->id,
29 | 'name' => $permission->name,
30 | ])->toArray();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Services/ListUserPermissions.php:
--------------------------------------------------------------------------------
1 | run($userId);
12 |
13 | return Arr::pluck($userPermissions, 'name');
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Tests/Permission/GetUserPermissionTest.php:
--------------------------------------------------------------------------------
1 | user = User::factory()->create();
14 |
15 | $this->role = Role::create(['name' => 'role 1', 'guard_name' => 'user']);
16 | $this->role2 = Role::create(['name' => 'role 2', 'guard_name' => 'user']);
17 |
18 | $this->permission = Permission::create(['name' => 'permission 1', 'guard_name' => 'user']);
19 | $this->permission2 = Permission::create(['name' => 'permission 2', 'guard_name' => 'user']);
20 | });
21 |
22 | test('user permission service returns correct direct permissions for the user', function () {
23 | $this->user->syncPermissions([$this->permission->id]);
24 |
25 | $userPermissions = (new GetUserPermissions)->run($this->user->id);
26 |
27 | $this->assertCount(1, $userPermissions);
28 | $this->assertEquals($this->permission->id, $userPermissions[0]['id']);
29 | });
30 |
31 | test('user permission service returns correct empty permissions for the user', function () {
32 | $userPermissions = (new GetUserPermissions)->run($this->user->id);
33 |
34 | $this->assertTrue(is_array($userPermissions));
35 | $this->assertCount(0, $userPermissions);
36 | });
37 |
38 | test('user permission service returns correct role permissions for the user', function () {
39 | $this->role2->syncPermissions([$this->permission2->id]);
40 | $this->user->syncRoles([$this->role2->id]);
41 |
42 | $userPermissions = (new GetUserPermissions)->run($this->user->id);
43 |
44 | $this->assertCount(1, $userPermissions);
45 | $this->assertEquals($this->permission2->id, $userPermissions[0]['id']);
46 | });
47 |
48 | test('user permission service returns correct direct and role permissions for the user', function () {
49 | $this->user->syncPermissions([$this->permission->id]);
50 | $this->role2->syncPermissions([$this->permission2->id]);
51 | $this->user->syncRoles([$this->role2->id]);
52 |
53 | $userPermissions = (new GetUserPermissions)->run($this->user->id);
54 |
55 | // granular user permissions, will always override role permissions for the user
56 | $this->assertCount(1, $userPermissions);
57 | $this->assertEquals($this->permission->id, $userPermissions[0]['id']);
58 | });
59 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Tests/Permission/UserPermissionTest.php:
--------------------------------------------------------------------------------
1 | 'root']);
15 | $this->user = User::factory()->create();
16 | $this->user->assignRole($role);
17 |
18 | $this->loggedRequest = $this->actingAs($this->user);
19 |
20 | $this->permission = Permission::create(['name' => 'first', 'guard_name' => 'user']);
21 | $this->permission2 = Permission::create(['name' => 'second', 'guard_name' => 'user']);
22 |
23 | $this->user->syncPermissions([$this->permission->id]);
24 | });
25 |
26 | test('user permissions can be rendered', function () {
27 | $response = $this->loggedRequest->get('/admin/acl-user-permission/'.$this->user->id.'/edit');
28 |
29 | $response->assertStatus(200);
30 |
31 | $response->assertInertia(
32 | fn (Assert $page) => $page
33 | ->component('AclUserPermission/UserPermissionForm')
34 | ->has(
35 | 'user',
36 | fn (Assert $page) => $page
37 | ->where('id', $this->user->id)
38 | ->etc()
39 | )
40 | ->has(
41 | 'userPermissions',
42 | 1,
43 | fn (Assert $page) => $page
44 | ->where('id', $this->permission->id)
45 | ->where('name', $this->permission->name)
46 | )
47 | ->has(
48 | 'permissions',
49 | 2
50 | )
51 | );
52 | });
53 |
54 | test('user permissions can be updated', function () {
55 | $response = $this->loggedRequest->put('/admin/acl-user-permission/'.$this->user->id, [
56 | 'userPermissions' => [$this->permission2->id],
57 | ]);
58 |
59 | $response->assertRedirect('/admin/user');
60 |
61 | $userPermissions = (new GetUserPermissions)->run($this->user->id);
62 |
63 | $this->assertCount(1, $userPermissions);
64 | $this->assertEquals($this->permission2->id, $userPermissions[0]['id']);
65 | });
66 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Tests/Role/RolePermissionTest.php:
--------------------------------------------------------------------------------
1 | role = Role::create(['name' => 'root']);
14 | $this->user = User::factory()->create();
15 |
16 | $this->permission = Permission::create(['name' => 'first']);
17 | $this->permission2 = Permission::create(['name' => 'second']);
18 |
19 | $this->role->syncPermissions([$this->permission->id]);
20 | $this->user->assignRole($this->role);
21 |
22 | $this->loggedRequest = $this->actingAs($this->user);
23 | });
24 |
25 | test('role permissions can be rendered', function () {
26 | $response = $this->loggedRequest->get('/admin/acl-role-permission/'.$this->role->id.'/edit');
27 |
28 | $response->assertStatus(200);
29 |
30 | $response->assertInertia(
31 | fn (Assert $page) => $page
32 | ->component('AclRolePermission/RolePermissionForm')
33 | ->has(
34 | 'role',
35 | fn (Assert $page) => $page
36 | ->where('id', $this->role->id)
37 | ->etc()
38 | )
39 | ->has(
40 | 'role.permissions',
41 | 1,
42 | fn (Assert $page) => $page
43 | ->where('id', $this->permission->id)
44 | ->where('name', $this->permission->name)
45 | )
46 | ->has(
47 | 'permissions',
48 | 2
49 | )
50 | );
51 | });
52 |
53 | test('role permissions can be updated', function () {
54 | $response = $this->loggedRequest->put('/admin/acl-role-permission/'.$this->role->id, [
55 | 'rolePermissions' => [$this->permission2->id],
56 | ]);
57 |
58 | $response->assertRedirect('/admin/acl-role');
59 |
60 | $role = Role::with(['permissions' => function ($q) {
61 | $q->get(['id', 'name']);
62 | }])->findOrFail($this->role->id);
63 |
64 | $this->assertCount(1, $role->permissions);
65 | $this->assertEquals($this->permission2->id, $role->permissions->first()->id);
66 | });
67 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/Tests/Role/UserRoleTest.php:
--------------------------------------------------------------------------------
1 | user = User::factory()->create();
13 | $this->loggedRequest = $this->actingAs($this->user);
14 |
15 | $this->role = Role::create(['name' => 'root', 'guard_name' => 'user']);
16 | $this->role2 = Role::create(['name' => 'second', 'guard_name' => 'user']);
17 |
18 | $this->user->syncRoles([$this->role->id]);
19 | });
20 |
21 | test('user roles can be rendered', function () {
22 | $response = $this->loggedRequest->get('/admin/acl-user-role/'.$this->user->id.'/edit');
23 |
24 | $response->assertStatus(200);
25 |
26 | $response->assertInertia(
27 | fn (Assert $page) => $page
28 | ->component('AclUserRole/UserRoleForm')
29 | ->has(
30 | 'user',
31 | fn (Assert $page) => $page
32 | ->where('id', $this->user->id)
33 | ->etc()
34 | )
35 | ->has(
36 | 'user.roles',
37 | 1,
38 | fn (Assert $page) => $page
39 | ->where('id', $this->role->id)
40 | ->where('name', $this->role->name)
41 | )
42 | ->has(
43 | 'roles',
44 | 2
45 | )
46 | );
47 | });
48 |
49 | test('user roles can be updated', function () {
50 | $response = $this->loggedRequest->put('/admin/acl-user-role/'.$this->user->id, [
51 | 'userRoles' => [$this->role2->id],
52 | ]);
53 |
54 | $response->assertRedirect('/admin/user');
55 |
56 | $user = User::with(['roles' => function ($q) {
57 | $q->get(['id', 'name']);
58 | }])->findOrFail($this->user->id);
59 |
60 | $this->assertCount(1, $user->roles);
61 | $this->assertFalse($user->hasRole($this->role->name));
62 | $this->assertTrue($user->hasRole($this->role2->name));
63 | });
64 |
--------------------------------------------------------------------------------
/stubs/modules/Acl/config/config.php:
--------------------------------------------------------------------------------
1 | [
6 | // Menu Principal: Dashboard
7 | '1' => 'Menu Principal: Dashboard',
8 |
9 | // Menu Principal: Controles de Acesso
10 | '2' => 'Menu Principal: Controles de Acesso',
11 | '3' => 'Menu Principal: Controles de Acesso: Usuários - Listar',
12 | '4' => 'Menu Principal: Controles de Acesso: Permissões de Acesso - Listar',
13 | '5' => 'Menu Principal: Controles de Acesso: Perfis de Acesso - Listar',
14 | ],
15 |
16 | 'adminPermissions' => [
17 | '1', '2', '3', '4', '5',
18 | ],
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/AdminAuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadViewsFrom(__DIR__.'/views', 'admin-auth');
26 | parent::boot();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/Http/Controllers/AuthenticatedSessionController.php:
--------------------------------------------------------------------------------
1 | authenticate();
32 |
33 | $request->session()->regenerate();
34 |
35 | return redirect()->intended(route(config('modular.default-logged-route')));
36 | }
37 |
38 | /**
39 | * Destroy an authenticated session.
40 | *
41 | * @return \Illuminate\Http\RedirectResponse
42 | */
43 | public function logout(Request $request)
44 | {
45 | Auth::guard('user')->logout();
46 |
47 | $request->session()->invalidate();
48 |
49 | $request->session()->regenerateToken();
50 |
51 | return Inertia::location(config('modular.login-url'));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/Http/Controllers/NewPasswordController.php:
--------------------------------------------------------------------------------
1 | $request->email,
24 | 'token' => $request->route('token'),
25 | ]);
26 | }
27 |
28 | /**
29 | * Handle an incoming new password request.
30 | *
31 | * @return \Illuminate\Http\RedirectResponse
32 | *
33 | * @throws \Illuminate\Validation\ValidationException
34 | */
35 | public function store(Request $request)
36 | {
37 | $request->validate([
38 | 'token' => 'required',
39 | 'email' => 'required|email',
40 | 'password' => ['required', 'confirmed', Rules\Password::defaults()],
41 | ]);
42 |
43 | // Here we will attempt to reset the user's password. If it is successful we
44 | // will update the password on an actual user model and persist it to the
45 | // database. Otherwise we will parse the error and return the response.
46 | $status = Password::broker('usersModule')->reset(
47 | $request->only('email', 'password', 'password_confirmation', 'token'),
48 | function ($user) use ($request) {
49 | $user->forceFill([
50 | 'password' => Hash::make($request->password),
51 | 'remember_token' => Str::random(60),
52 | ])->save();
53 |
54 | event(new PasswordReset($user));
55 | }
56 | );
57 |
58 | // If the password was successfully reset, we will redirect the user back to
59 | // the application's home authenticated view. If there is an error we can
60 | // redirect them back to where they came from with their error message.
61 | return $status == Password::broker('usersModule')::PASSWORD_RESET
62 | ? redirect()->route('adminAuth.loginForm')->with('success', 'Password updated')
63 | : back()->withInput($request->only('email'))
64 | ->withErrors(['email' => __($status)]);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/Http/Controllers/PasswordResetLinkController.php:
--------------------------------------------------------------------------------
1 | validate([
31 | 'email' => 'required|email',
32 | ]);
33 |
34 | // We will send the password reset link to this user. Once we have attempted
35 | // to send the link, we will examine the response then see the message we
36 | // need to show to the user. Finally, we'll send out a proper response.
37 | $status = Password::broker('usersModule')->sendResetLink(
38 | $request->only('email')
39 | );
40 |
41 | return $status == Password::broker('usersModule')::RESET_LINK_SENT
42 | ? back()->with('success', __($status))
43 | : back()->withInput($request->only('email'))
44 | ->withErrors(['email' => __($status)]);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/Http/Middleware/UserAuth.php:
--------------------------------------------------------------------------------
1 | guest()) {
20 | return redirect()->route('adminAuth.loginForm')
21 | ->withErrors(['email' => 'Session expired, please login again.']);
22 | }
23 |
24 | return $next($request);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/Notifications/ResetPassword.php:
--------------------------------------------------------------------------------
1 | $this->token,
36 | 'email' => $notifiable->getEmailForPasswordReset(),
37 | ];
38 |
39 | return (new MailMessage)->markdown(
40 | 'admin-auth::emails.reset-password',
41 | [
42 | 'url' => url(route('adminAuth.resetPasswordForm', $params, false)),
43 | ]
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/Tests/AuthenticationTest.php:
--------------------------------------------------------------------------------
1 | get($loginRoute);
13 |
14 | $response->assertStatus(200);
15 | });
16 |
17 | test('users can authenticate using the login screen', function () {
18 | $user = User::factory()->create();
19 |
20 | $response = $this->post('/admin-auth/login', [
21 | 'email' => $user->email,
22 | 'password' => 'password',
23 | ]);
24 |
25 | $this->assertAuthenticated();
26 | $response->assertRedirect('/admin/dashboard');
27 | });
28 |
29 | test('users can not authenticate with invalid password', function () {
30 | $user = User::factory()->create();
31 |
32 | $this->post('/login', [
33 | 'email' => $user->email,
34 | 'password' => 'wrong-password',
35 | ]);
36 |
37 | $this->assertGuest();
38 | });
39 |
40 | test('users can logout', function () {
41 | $loginRoute = config('modular.login-url');
42 | $user = User::factory()->create();
43 |
44 | $response = $this->actingAs($user)->get('/admin-auth/logout');
45 |
46 | $this->assertGuest();
47 |
48 | $response->assertRedirect($loginRoute);
49 | });
50 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/Tests/PasswordResetTest.php:
--------------------------------------------------------------------------------
1 | get('/admin-auth/forgot-password');
13 |
14 | $response->assertStatus(200);
15 | });
16 |
17 | test('reset password link can be requested', function () {
18 | Notification::fake();
19 |
20 | $user = User::factory()->create();
21 |
22 | $this->post('/admin-auth/send-reset-link-email', ['email' => $user->email]);
23 |
24 | Notification::assertSentTo($user, AdminAuthResetPassword::class);
25 | });
26 |
27 | test('reset password screen can be rendered', function () {
28 | Notification::fake();
29 |
30 | $user = User::factory()->create();
31 |
32 | $this->post('/admin-auth/send-reset-link-email', ['email' => $user->email]);
33 |
34 | Notification::assertSentTo($user, AdminAuthResetPassword::class, function ($notification) {
35 | $response = $this->get('/admin-auth/reset-password/'.$notification->token);
36 |
37 | $response->assertStatus(200);
38 |
39 | return true;
40 | });
41 | });
42 |
43 | test('password can be reset with valid token', function () {
44 | Notification::fake();
45 |
46 | $user = User::factory()->create();
47 |
48 | $this->post('/admin-auth/send-reset-link-email', ['email' => $user->email]);
49 |
50 | Notification::assertSentTo($user, AdminAuthResetPassword::class, function ($notification) use ($user) {
51 | $response = $this->post('/admin-auth/reset-password/', [
52 | 'token' => $notification->token,
53 | 'email' => $user->email,
54 | 'password' => 'password',
55 | 'password_confirmation' => 'password',
56 | ]);
57 |
58 | $response->assertSessionHasNoErrors();
59 |
60 | return true;
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/routes/site.php:
--------------------------------------------------------------------------------
1 | name('adminAuth.loginForm');
11 |
12 | Route::post('/admin-auth/login', [
13 | AuthenticatedSessionController::class, 'login',
14 | ])->name('adminAuth.login');
15 |
16 | Route::get('/admin-auth/logout', [
17 | AuthenticatedSessionController::class, 'logout',
18 | ])->name('adminAuth.logout');
19 |
20 | // form to receive the email that contains the link to reset password
21 | Route::get('/admin-auth/forgot-password', [
22 | PasswordResetLinkController::class, 'forgotPasswordForm',
23 | ])->name('adminAuth.forgotPassword');
24 |
25 | Route::post('/admin-auth/send-reset-link-email', [
26 | PasswordResetLinkController::class, 'sendResetLinkEmail',
27 | ])->name('adminAuth.sendResetLinkEmail');
28 |
29 | // password reset form
30 | Route::get('/admin-auth/reset-password/{token}', [
31 | NewPasswordController::class, 'resetPasswordForm',
32 | ])->name('adminAuth.resetPasswordForm');
33 |
34 | Route::post('/admin-auth/reset-password', [
35 | NewPasswordController::class, 'store',
36 | ])->name('adminAuth.resetPassword');
37 |
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/views/emails/reset-password.blade.php:
--------------------------------------------------------------------------------
1 | @component('mail::message')
2 | # Forgot your password?
3 |
4 | Here is your password reset link.
5 |
6 | @component('mail::button', ['url' => $url])
7 | Reset Password
8 | @endcomponent
9 |
10 | Thanks,
11 | {{ config('app.name') }}
12 | @endcomponent
--------------------------------------------------------------------------------
/stubs/modules/AdminAuth/views/forgot-password-form.blade.php:
--------------------------------------------------------------------------------
1 | @extends('admin-auth::layouts.master')
2 |
3 | @section('content')
4 |
5 |
21 | Ooops, algo deu errado 22 |
23 |21 | {{ __('Are you sure you want to proceed?') }} 22 |
23 |11 | {{ label }} 12 |
13 |14 | {{ count }} 15 |
16 |3 | From ExampleComponent.vue: Let's do it! 4 |
5 | 6 | -------------------------------------------------------------------------------- /stubs/site/resources-site/js/create-vue-app.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue/dist/vue.esm-bundler.js' 2 | //import commonComponent from './Components/common-component.vue' 3 | 4 | export const createVueApp = (additionalComponents = {}) => { 5 | const app = createApp({ 6 | components: { 7 | //commonComponent, 8 | ...additionalComponents 9 | } 10 | }) 11 | 12 | import.meta.glob(['../images/**']) 13 | 14 | return app 15 | } 16 | -------------------------------------------------------------------------------- /stubs/site/resources-site/js/index-app.js: -------------------------------------------------------------------------------- 1 | import { createVueApp } from './create-vue-app.js' 2 | import IndexExampleComponent from './Components/IndexExampleComponent.vue' 3 | 4 | createVueApp({ 5 | IndexExampleComponent 6 | }).mount('#app') 7 | -------------------------------------------------------------------------------- /stubs/site/resources-site/views/pagination/simple-tailwind.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 25 | @endif 26 | -------------------------------------------------------------------------------- /stubs/site/resources-site/views/site-layout.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |