From ca7e23e6deed7f108ccb614cdf50d043bcc6237e Mon Sep 17 00:00:00 2001 From: kroutony Date: Sat, 22 Feb 2020 14:45:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E6=AC=8A=E9=99=90=E6=A9=9F?= =?UTF-8?q?=E5=88=B6,=20Auth=E7=9B=B8=E9=97=9Cviews,=20=E5=BE=8C=E5=8F=B0v?= =?UTF-8?q?iews=E5=8F=8A=E7=9B=B8=E9=97=9C=E6=A9=9F=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + app/Console/Commands/CreateAdminAccount.php | 50 +++++ app/Http/Controllers/AdminPageController.php | 24 +++ app/Http/Kernel.php | 1 + app/Http/Middleware/AdminAreaGuard.php | 35 +++ app/Providers/RouteServiceProvider.php | 2 +- app/Reposotories/BaseRepository.php | 95 +++++++++ app/Reposotories/UserRepository.php | 44 ++++ app/User.php | 2 + composer.json | 5 +- composer.lock | 124 ++++++++++- config/admin.php | 9 + config/app.php | 1 + config/data-presets.php | 17 ++ config/permission.php | 129 +++++++++++ ..._02_21_174407_create_permission_tables.php | 104 +++++++++ database/seeds/DatabaseSeeder.php | 2 +- .../Preset/RolesAndPermissionsSeeder.php | 45 ++++ package-lock.json | 201 +++++++++++++++++- package.json | 26 ++- resources/js/admin/app.js | 1 + resources/js/admin/lib.js | 36 ++++ resources/js/bootstrap.js | 29 +-- resources/sass/_variables.scss | 19 ++ resources/sass/admin/_bootstrap.scss | 3 + resources/sass/admin/_variables.scss | 19 ++ resources/sass/admin/app.scss | 10 + resources/sass/admin/components/_blockui.scss | 5 + .../sass/admin/components/_datatables.scss | 14 ++ resources/sass/admin/lib.scss | 37 ++++ resources/sass/app.scss | 9 +- resources/views/admin/index.blade.php | 3 + resources/views/admin/layouts/app.blade.php | 74 +++++++ resources/views/auth/login.blade.php | 73 +++++++ .../views/auth/passwords/confirm.blade.php | 49 +++++ .../views/auth/passwords/email.blade.php | 47 ++++ .../views/auth/passwords/reset.blade.php | 65 ++++++ resources/views/auth/register.blade.php | 77 +++++++ resources/views/auth/verify.blade.php | 28 +++ resources/views/components/nav.blade.php | 50 +++++ resources/views/home.blade.php | 22 ++ resources/views/layouts/app.blade.php | 7 +- routes/web.php | 18 +- webpack.mix.js | 15 +- 44 files changed, 1584 insertions(+), 45 deletions(-) create mode 100644 app/Console/Commands/CreateAdminAccount.php create mode 100644 app/Http/Controllers/AdminPageController.php create mode 100644 app/Http/Middleware/AdminAreaGuard.php create mode 100644 app/Reposotories/BaseRepository.php create mode 100644 app/Reposotories/UserRepository.php create mode 100644 config/admin.php create mode 100644 config/data-presets.php create mode 100644 config/permission.php create mode 100644 database/migrations/2020_02_21_174407_create_permission_tables.php create mode 100644 database/seeds/Preset/RolesAndPermissionsSeeder.php create mode 100644 resources/js/admin/app.js create mode 100644 resources/js/admin/lib.js create mode 100644 resources/sass/_variables.scss create mode 100644 resources/sass/admin/_bootstrap.scss create mode 100644 resources/sass/admin/_variables.scss create mode 100644 resources/sass/admin/app.scss create mode 100644 resources/sass/admin/components/_blockui.scss create mode 100644 resources/sass/admin/components/_datatables.scss create mode 100644 resources/sass/admin/lib.scss create mode 100644 resources/views/admin/index.blade.php create mode 100644 resources/views/admin/layouts/app.blade.php create mode 100644 resources/views/auth/login.blade.php create mode 100644 resources/views/auth/passwords/confirm.blade.php create mode 100644 resources/views/auth/passwords/email.blade.php create mode 100644 resources/views/auth/passwords/reset.blade.php create mode 100644 resources/views/auth/register.blade.php create mode 100644 resources/views/auth/verify.blade.php create mode 100644 resources/views/components/nav.blade.php diff --git a/.gitignore b/.gitignore index ff9cfdb..4b30275 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ /public/storage /public/js /public/css +/public/fonts/vendor +/public/images/vendor +/public/mix-manifest.json /storage/*.key /vendor .env diff --git a/app/Console/Commands/CreateAdminAccount.php b/app/Console/Commands/CreateAdminAccount.php new file mode 100644 index 0000000..0509f51 --- /dev/null +++ b/app/Console/Commands/CreateAdminAccount.php @@ -0,0 +1,50 @@ +argument('email'); + $password = $this->argument('password'); + + $userRepo = app(UserRepository::class); + + $userRepo->createAdminAccount($email, $password); + + $this->info("Account: $email has been created"); + } +} diff --git a/app/Http/Controllers/AdminPageController.php b/app/Http/Controllers/AdminPageController.php new file mode 100644 index 0000000..bd6442d --- /dev/null +++ b/app/Http/Controllers/AdminPageController.php @@ -0,0 +1,24 @@ +mainManuPagePrefix = config('admin.route_name_prefix') . config('admin.menu.route_name_prefix'); + } + + public function index() + { + return view('admin.index'); + } +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index deb65e8..c4500c7 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -61,6 +61,7 @@ class Kernel extends HttpKernel 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'admin.area' => \App\Http\Middleware\AdminAreaGuard::class, ]; /** diff --git a/app/Http/Middleware/AdminAreaGuard.php b/app/Http/Middleware/AdminAreaGuard.php new file mode 100644 index 0000000..af70a9b --- /dev/null +++ b/app/Http/Middleware/AdminAreaGuard.php @@ -0,0 +1,35 @@ +user(); + if($user) { + if($user->can('admin area')) { + return $next($request); + } else { + abort(403); // 有登入但無權限 + } + } else { + abort(404); // 無登入回應404,顯示找不到頁面 + } + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 527eee3..269fabb 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -21,7 +21,7 @@ class RouteServiceProvider extends ServiceProvider * * @var string */ - public const HOME = '/home'; + public const HOME = '/'; /** * Define your route model bindings, pattern filters, etc. diff --git a/app/Reposotories/BaseRepository.php b/app/Reposotories/BaseRepository.php new file mode 100644 index 0000000..140808c --- /dev/null +++ b/app/Reposotories/BaseRepository.php @@ -0,0 +1,95 @@ +model; + } + + /** + * @param Model $model + * @return void + */ + public function setModel(Model $model) + { + $this->model = $model; + } + + /** + * 取得Model的class name + * + * @return string + */ + public function getModelClass() + { + return get_class($this->model); + } + + /** + * 新建一個Model的instance + * + * @return Model + */ + public function createModel() + { + return app($this->getModelClass()); + } + + /** + * 查詢所有 + * + * @return \Illuminate\Database\Eloquent\Collection + */ + public function queryAll() + { + $modelClass = $this->getModelClass(); + return $modelClass::all(); + } + + /** + * 查詢特定ID + * + * @param $id + * @return Model|null + */ + public function findModel($id) + { + return $this->getModel()->find($id); + } + + /** + * 刪除特定id + * + * @param $id + * @return bool|null + * @throws \Exception + */ + public function deleteModel($id) + { + return $this->findModel($id)->delete(); + } + + /** + * @param $id + * @return bool + */ + public function hasModel($id) + { + return $this->getModel()->where('id', $id)->exists(); + } +} diff --git a/app/Reposotories/UserRepository.php b/app/Reposotories/UserRepository.php new file mode 100644 index 0000000..2a9bca2 --- /dev/null +++ b/app/Reposotories/UserRepository.php @@ -0,0 +1,44 @@ +setModel($user); + } + + /** + * 建立管理員帳號 + * + * @param string $email + * @param string $password + * @return void + */ + public function createAdminAccount($email, $password) + { + if($email && $password) { + $user = $this->model->create([ + 'email' => $email, + 'password' => Hash::make($password), + 'api_token' => Str::random(36) + ]); + + $user->assignRole('administrator'); + + return $user->id; + } + } +} diff --git a/app/User.php b/app/User.php index 1243978..111ff4c 100644 --- a/app/User.php +++ b/app/User.php @@ -5,6 +5,7 @@ namespace App; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Spatie\Permission\Traits\HasRoles; /** * App\User @@ -34,6 +35,7 @@ class User extends Authenticatable { use Notifiable; + use HasRoles; /** * The attributes that are mass assignable. * diff --git a/composer.json b/composer.json index e5d0cdc..5fb28bf 100644 --- a/composer.json +++ b/composer.json @@ -11,12 +11,14 @@ "php": "^7.2", "fideloper/proxy": "^4.0", "laravel/framework": "^6.2", - "laravel/tinker": "^2.0" + "laravel/tinker": "^2.0", + "spatie/laravel-permission": "^3.8" }, "require-dev": { "barryvdh/laravel-ide-helper": "^2.6", "facade/ignition": "^1.4", "fzaninotto/faker": "^1.9.1", + "laravel/ui": "^1.2", "mockery/mockery": "^1.0", "nunomaduro/collision": "^3.0", "phpunit/phpunit": "^8.0" @@ -37,6 +39,7 @@ }, "classmap": [ "database/seeds", + "database/seeds/Preset", "database/factories" ] }, diff --git a/composer.lock b/composer.lock index c1bbaaf..51c79ef 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "d823097c9a46903a75367077995590b2", + "content-hash": "7315d6229270be366528dbffc0c6864c", "packages": [ { "name": "dnoegel/php-xdg-base-dir", @@ -1459,6 +1459,74 @@ ], "time": "2020-02-21T04:36:14+00:00" }, + { + "name": "spatie/laravel-permission", + "version": "3.8.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-permission.git", + "reference": "2339b6fae8f8aa5af047e1a0c54b6fb37546058a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/2339b6fae8f8aa5af047e1a0c54b6fb37546058a", + "reference": "2339b6fae8f8aa5af047e1a0c54b6fb37546058a", + "shasum": "" + }, + "require": { + "illuminate/auth": "^5.8|^6.0|^7.0", + "illuminate/container": "^5.8|^6.0|^7.0", + "illuminate/contracts": "^5.8|^6.0|^7.0", + "illuminate/database": "^5.8|^6.0|^7.0", + "php": "^7.2" + }, + "require-dev": { + "orchestra/testbench": "^3.8|^4.0|^5.0", + "phpunit/phpunit": "^8.0", + "predis/predis": "^1.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Permission\\PermissionServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\Permission\\": "src" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Permission handling for Laravel 5.8 and up", + "homepage": "https://github.com/spatie/laravel-permission", + "keywords": [ + "acl", + "laravel", + "permission", + "permissions", + "rbac", + "roles", + "security", + "spatie" + ], + "time": "2020-02-18T21:16:24+00:00" + }, { "name": "swiftmailer/swiftmailer", "version": "v6.2.3", @@ -4118,6 +4186,60 @@ ], "time": "2019-09-25T14:49:45+00:00" }, + { + "name": "laravel/ui", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/ui.git", + "reference": "bb64fca681566ca94457d490a00f899516e75664" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/ui/zipball/bb64fca681566ca94457d490a00f899516e75664", + "reference": "bb64fca681566ca94457d490a00f899516e75664", + "shasum": "" + }, + "require": { + "illuminate/console": "~5.8|^6.0", + "illuminate/filesystem": "~5.8|^6.0", + "illuminate/support": "~5.8|^6.0", + "php": "^7.1.3" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Ui\\UiServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Ui\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel UI utilities and presets.", + "keywords": [ + "laravel", + "ui" + ], + "time": "2020-02-13T21:12:28+00:00" + }, { "name": "mockery/mockery", "version": "1.3.1", diff --git a/config/admin.php b/config/admin.php new file mode 100644 index 0000000..4d986e4 --- /dev/null +++ b/config/admin.php @@ -0,0 +1,9 @@ + 'adm', + // 後台的Route Name前綴 + 'route_name_prefix' => 'admin.', +]; diff --git a/config/app.php b/config/app.php index 4ab5bda..1e682e8 100644 --- a/config/app.php +++ b/config/app.php @@ -166,6 +166,7 @@ return [ * Package Service Providers... */ Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, + Spatie\Permission\PermissionServiceProvider::class, /* * Application Service Providers... */ diff --git a/config/data-presets.php b/config/data-presets.php new file mode 100644 index 0000000..f3ea333 --- /dev/null +++ b/config/data-presets.php @@ -0,0 +1,17 @@ + [ + ['name' => 'administrator', 'displayName' => 'administrator'], + ['name' => 'editor', 'displayName' => 'editor'], + ], + 'permissions' => [ + [ + //進入後台 + 'name' => 'admin area', + 'displayName' => 'adminArea', + 'assignTo' => [ + 'administrator', 'editor' + ] + ], + ] +]; diff --git a/config/permission.php b/config/permission.php new file mode 100644 index 0000000..1a0b35a --- /dev/null +++ b/config/permission.php @@ -0,0 +1,129 @@ + [ + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * Eloquent model should be used to retrieve your permissions. Of course, it + * is often just the "Permission" model but you may use whatever you like. + * + * The model you want to use as a Permission model needs to implement the + * `Spatie\Permission\Contracts\Permission` contract. + */ + + 'permission' => Spatie\Permission\Models\Permission::class, + + /* + * When using the "HasRoles" trait from this package, we need to know which + * Eloquent model should be used to retrieve your roles. Of course, it + * is often just the "Role" model but you may use whatever you like. + * + * The model you want to use as a Role model needs to implement the + * `Spatie\Permission\Contracts\Role` contract. + */ + + 'role' => Spatie\Permission\Models\Role::class, + + ], + + 'table_names' => [ + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'roles' => 'roles', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your permissions. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'permissions' => 'permissions', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your models permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_permissions' => 'model_has_permissions', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your models roles. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_roles' => 'model_has_roles', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'role_has_permissions' => 'role_has_permissions', + ], + + 'column_names' => [ + + /* + * Change this if you want to name the related model primary key other than + * `model_id`. + * + * For example, this would be nice if your primary keys are all UUIDs. In + * that case, name this `model_uuid`. + */ + + 'model_morph_key' => 'model_id', + ], + + /* + * When set to true, the required permission/role names are added to the exception + * message. This could be considered an information leak in some contexts, so + * the default setting is false here for optimum safety. + */ + + 'display_permission_in_exception' => false, + + 'cache' => [ + + /* + * By default all permissions are cached for 24 hours to speed up performance. + * When permissions or roles are updated the cache is flushed automatically. + */ + + 'expiration_time' => \DateInterval::createFromDateString('24 hours'), + + /* + * The cache key used to store all permissions. + */ + + 'key' => 'spatie.permission.cache', + + /* + * When checking for a permission against a model by passing a Permission + * instance to the check, this key determines what attribute on the + * Permissions model is used to cache against. + * + * Ideally, this should match your preferred way of checking permissions, eg: + * `$user->can('view-posts')` would be 'name'. + */ + + 'model_key' => 'name', + + /* + * You may optionally indicate a specific cache driver to use for permission and + * role caching using any of the `store` drivers listed in the cache.php config + * file. Using 'default' here means to use the `default` set in cache.php. + */ + + 'store' => 'default', + ], +]; diff --git a/database/migrations/2020_02_21_174407_create_permission_tables.php b/database/migrations/2020_02_21_174407_create_permission_tables.php new file mode 100644 index 0000000..223f56b --- /dev/null +++ b/database/migrations/2020_02_21_174407_create_permission_tables.php @@ -0,0 +1,104 @@ +increments('id'); + $table->string('name')->comment('名稱'); + $table->string('display_name')->comment('用來顯示的名稱'); + $table->string('guard_name'); + $table->timestamps(); + }); + + Schema::create($tableNames['roles'], function (Blueprint $table) { + $table->increments('id'); + $table->string('name')->comment('名稱'); + $table->string('display_name')->comment('用來顯示的名稱'); + $table->string('guard_name'); + $table->timestamps(); + }); + + Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames) { + $table->unsignedInteger('permission_id'); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type', ]); + + $table->foreign('permission_id') + ->references('id') + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->primary(['permission_id', $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + }); + + Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames) { + $table->unsignedInteger('role_id'); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type', ]); + + $table->foreign('role_id') + ->references('id') + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary(['role_id', $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + }); + + Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) { + $table->unsignedInteger('permission_id'); + $table->unsignedInteger('role_id'); + + $table->foreign('permission_id') + ->references('id') + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->foreign('role_id') + ->references('id') + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary(['permission_id', 'role_id']); + }); + + app('cache') + ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) + ->forget(config('permission.cache.key')); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + $tableNames = config('permission.table_names'); + + Schema::drop($tableNames['role_has_permissions']); + Schema::drop($tableNames['model_has_roles']); + Schema::drop($tableNames['model_has_permissions']); + Schema::drop($tableNames['roles']); + Schema::drop($tableNames['permissions']); + } +} diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 91cb6d1..6d8eef7 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -11,6 +11,6 @@ class DatabaseSeeder extends Seeder */ public function run() { - // $this->call(UsersTableSeeder::class); + $this->call(RolesAndPermissionsSeeder::class); } } diff --git a/database/seeds/Preset/RolesAndPermissionsSeeder.php b/database/seeds/Preset/RolesAndPermissionsSeeder.php new file mode 100644 index 0000000..5d4ae18 --- /dev/null +++ b/database/seeds/Preset/RolesAndPermissionsSeeder.php @@ -0,0 +1,45 @@ +forgetCachedPermissions(); + + //讀取Config檔案 + $presetConfig = config('data-presets'); + $roles = $presetConfig['roles']; + $permissions = $presetConfig['permissions']; + + //建立Roles + foreach ($roles as $role) { + $_role = Role::create([ + 'name' => $role['name'], + 'display_name' => $role['displayName'] + ]); + } + + //建立Permissions + foreach ($permissions as $permission) { + $_permission = Permission::create([ + 'name' => $permission['name'], + 'display_name' => $permission['displayName'] + ]); + if(!empty($permission['assignTo'])) { + foreach ($permission['assignTo'] as $roleName) { + $_permission->assignRole($roleName); + } + } + } + } +} diff --git a/package-lock.json b/package-lock.json index d756ec9..ee1b041 100644 --- a/package-lock.json +++ b/package-lock.json @@ -919,6 +919,47 @@ "to-fast-properties": "2.0.0" } }, + "@coreui/coreui": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@coreui/coreui/-/coreui-2.1.16.tgz", + "integrity": "sha512-1YOnQAlcX2bIgnaX3k9GKaN4lD+wKam7tdDfFj7/ZQTN1XG3dwDELHp4aagWQs78ix2CCO1LyeLrzGpsMcLW3Q==", + "dev": true, + "requires": { + "@coreui/coreui-plugin-npm-postinstall": "1.0.2", + "bootstrap": "4.4.1", + "core-js": "3.6.4", + "regenerator-runtime": "0.13.3" + } + }, + "@coreui/coreui-plugin-chartjs-custom-tooltips": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@coreui/coreui-plugin-chartjs-custom-tooltips/-/coreui-plugin-chartjs-custom-tooltips-1.3.1.tgz", + "integrity": "sha512-ovNE9QygRdB7IkE7gZNRx79lSk77STtNOFS4NRpjljoRcAseR156ZYV0i/dSoiwZwRJ+dHzWeXy1IMcXcdnAww==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@coreui/coreui-plugin-npm-postinstall": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@coreui/coreui-plugin-npm-postinstall/-/coreui-plugin-npm-postinstall-1.0.2.tgz", + "integrity": "sha512-yeeoWp+bNS84nP1977Y8UCiQ9pssO+f4QuVj3i0/gYZFjjvOgxx0dnyWhtowD5sLYnCRMPlPpqyjwXze3SlkYg==", + "dev": true + }, + "@coreui/icons": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@coreui/icons/-/icons-0.4.1.tgz", + "integrity": "sha512-k3U1zzJwwKIH+LgSBD1y+GTbB3Rqen567GyNHNKxHHTke6r67n/s3K2nxgD8vX53WirfXzsz+8+mRipnzkplCA==", + "dev": true + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -1718,6 +1759,15 @@ "file-uri-to-path": "1.0.0" } }, + "block-ui": { + "version": "2.70.1", + "resolved": "https://registry.npmjs.org/block-ui/-/block-ui-2.70.1.tgz", + "integrity": "sha1-yGLWTuYoj7eBIzd8ZoC8erJiED8=", + "dev": true, + "requires": { + "jquery": "3.4.1" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1785,6 +1835,12 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "bootstrap": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz", + "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1914,8 +1970,8 @@ "dev": true, "requires": { "caniuse-lite": "1.0.30001028", - "electron-to-chromium": "1.3.356", - "node-releases": "1.1.49" + "electron-to-chromium": "1.3.358", + "node-releases": "1.1.50" } }, "buffer": { @@ -2083,6 +2139,35 @@ "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", "dev": true }, + "chart.js": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", + "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", + "dev": true, + "requires": { + "chartjs-color": "2.4.1", + "moment": "2.24.0" + } + }, + "chartjs-color": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "dev": true, + "requires": { + "chartjs-color-string": "0.6.0", + "color-convert": "1.9.3" + } + }, + "chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -2464,6 +2549,12 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "core-js": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", + "dev": true + }, "core-js-compat": { "version": "3.6.4", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", @@ -2858,6 +2949,46 @@ "type": "1.2.0" } }, + "datatables.net": { + "version": "1.10.20", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.20.tgz", + "integrity": "sha512-4E4S7tTU607N3h0fZPkGmAtr9mwy462u+VJ6gxYZ8MxcRIjZqHy3Dv1GNry7i3zQCktTdWbULVKBbkAJkuHEnQ==", + "dev": true, + "requires": { + "jquery": "3.4.1" + } + }, + "datatables.net-bs4": { + "version": "1.10.20", + "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.20.tgz", + "integrity": "sha512-kQmMUMsHMOlAW96ztdoFqjSbLnlGZQ63iIM82kHbmldsfYdzuyhbb4hTx6YNBi481WCO3iPSvI6YodNec46ZAw==", + "dev": true, + "requires": { + "datatables.net": "1.10.20", + "jquery": "3.4.1" + } + }, + "datatables.net-responsive": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/datatables.net-responsive/-/datatables.net-responsive-2.2.3.tgz", + "integrity": "sha512-8D6VtZcyuH3FG0Hn5A4LPZQEOX3+HrRFM7HjpmsQc/nQDBbdeBLkJX4Sh/o1nzFTSneuT1Wh/lYZHVPpjcN+Sw==", + "dev": true, + "requires": { + "datatables.net": "1.10.20", + "jquery": "3.4.1" + } + }, + "datatables.net-responsive-bs4": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/datatables.net-responsive-bs4/-/datatables.net-responsive-bs4-2.2.3.tgz", + "integrity": "sha512-SQaWI0uLuPcaiBBin9zX+MuQfTSIkK1bYxbXqUV6NLkHCVa6PMQK7Rvftj0ywG4R7uOtjbzY8nSVqxEKvQI0Vg==", + "dev": true, + "requires": { + "datatables.net-bs4": "1.10.20", + "datatables.net-responsive": "2.2.3", + "jquery": "3.4.1" + } + }, "de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -3169,9 +3300,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.356", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.356.tgz", - "integrity": "sha512-qW4YHMfOFjvx0jkSK2vjaHoLjk1+uJIV5tqtLDo7P5y3/kM8KQP23YBU0Y5fCSW4jIbDvEzeHDaY4+4vEaqqOw==", + "version": "1.3.358", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.358.tgz", + "integrity": "sha512-y9xvv+9PplXSUkOSxgtOfwNrqD/948VIScyWURnY27PXprg3PmRl7e8ekRJhnksDNjxLVyBYY6I2nQmNBzdi6g==", "dev": true }, "elliptic": { @@ -3884,6 +4015,12 @@ "resolve-dir": "1.0.1" } }, + "flag-icon-css": { + "version": "3.4.6", + "resolved": "https://registry.npmjs.org/flag-icon-css/-/flag-icon-css-3.4.6.tgz", + "integrity": "sha512-rF69rt19Hr63SRQTiPBzQABaYB20LAgZhDkr/AxqSdgmCIN+tC5PRMz56Y0gxehFXJmdRwv55+GMi7R1fCRTwg==", + "dev": true + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -3903,6 +4040,12 @@ "debug": "3.1.0" } }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -5531,6 +5674,12 @@ } } }, + "jquery": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6085,6 +6234,12 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "dev": true + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -6250,9 +6405,9 @@ } }, "node-releases": { - "version": "1.1.49", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.49.tgz", - "integrity": "sha512-xH8t0LS0disN0mtRCh+eByxFPie+msJUBL/lJDBuap53QGiYPa9joh83K4pCZgWJ+2L4b9h88vCVdXQ60NO2bg==", + "version": "1.1.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", + "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", "dev": true, "requires": { "semver": "6.3.0" @@ -6731,6 +6886,12 @@ "sha.js": "2.4.11" } }, + "perfect-scrollbar": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz", + "integrity": "sha512-NrNHJn5mUGupSiheBTy6x+6SXCFbLlm8fVZh9moIzw/LgqElN5q4ncR4pbCBCYuCJ8Kcl9mYM0NgDxvW+b4LxA==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -6761,6 +6922,12 @@ "find-up": "3.0.0" } }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "dev": true + }, "portfinder": { "version": "1.0.25", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", @@ -8104,6 +8271,12 @@ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "dev": true }, + "select2": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/select2/-/select2-4.0.13.tgz", + "integrity": "sha512-1JeB87s6oN/TDxQQYCvS5EFoQyvV6eYMZZ0AeA4tdFDYWN3BAGZ8npr17UBFddU0lgAt3H0yjX3X6/ekOj1yjw==", + "dev": true + }, "selfsigned": { "version": "1.10.7", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", @@ -8326,6 +8499,12 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-line-icons": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/simple-line-icons/-/simple-line-icons-2.4.1.tgz", + "integrity": "sha1-t1vFoNh+UwkowszaVzUnS7JW8jQ=", + "dev": true + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -9468,6 +9647,12 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, + "vue": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", + "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==", + "dev": true + }, "vue-hot-reload-api": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", diff --git a/package.json b/package.json index bb631ab..0ca6d35 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,29 @@ "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" }, "devDependencies": { + "@coreui/coreui": "^2.1.16", + "@coreui/coreui-plugin-chartjs-custom-tooltips": "^1.3.1", + "@coreui/icons": "^0.4.1", "axios": "^0.19", - "cross-env": "^7.0", + "block-ui": "^2.70.1", + "bootstrap": "^4.0.0", + "chart.js": "^2.9.3", + "cross-env": "^7.0.0", + "datatables.net-bs4": "^1.10.20", + "datatables.net-responsive-bs4": "^2.2.3", + "flag-icon-css": "^3.4.6", + "font-awesome": "^4.7.0", + "jquery": "^3.2", "laravel-mix": "^5.0.1", "lodash": "^4.17.13", - "resolve-url-loader": "^3.1.0", - "sass": "^1.15.2", - "sass-loader": "^8.0.0", - "vue-template-compiler": "^2.6.11" + "perfect-scrollbar": "^1.5.0", + "popper.js": "^1.12", + "resolve-url-loader": "^3.1.1", + "sass": "^1.20.1", + "sass-loader": "^8.0.2", + "select2": "^4.0.13", + "simple-line-icons": "^2.4.1", + "vue": "^2.6.11", + "vue-template-compiler": "^2.6.10" } } diff --git a/resources/js/admin/app.js b/resources/js/admin/app.js new file mode 100644 index 0000000..cf327e4 --- /dev/null +++ b/resources/js/admin/app.js @@ -0,0 +1 @@ +import './lib'; diff --git a/resources/js/admin/lib.js b/resources/js/admin/lib.js new file mode 100644 index 0000000..25c35b9 --- /dev/null +++ b/resources/js/admin/lib.js @@ -0,0 +1,36 @@ +import $ from 'jquery'; +window.$ = window.jQuery = $; + +import Popper from 'popper.js'; +window.Popper = Popper; + +import 'select2'; + +import 'bootstrap'; + +import _ from 'lodash' +window._ = _; + +import Axios from 'axios'; +window.axios = Axios; + +import Vue from 'vue'; +window.Vue = Vue; + +import 'perfect-scrollbar'; +import '@coreui/coreui'; +import 'chart.js'; +import '@coreui/coreui-plugin-chartjs-custom-tooltips'; + +try { + + window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; + + let token = document.head.querySelector('meta[name="csrf-token"]'); + if (token) { + window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; + } else { + console.error('Meta CSRF token not found'); + } + +} catch(e) {} diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js index 6922577..d2ef007 100644 --- a/resources/js/bootstrap.js +++ b/resources/js/bootstrap.js @@ -1,28 +1,13 @@ window._ = require('lodash'); -/** - * We'll load the axios HTTP library which allows us to easily issue requests - * to our Laravel back-end. This library automatically handles sending the - * CSRF token as a header based on the value of the "XSRF" token cookie. - */ +try { + window.Popper = require('popper.js').default; + window.$ = window.jQuery = require('jquery'); + + require('bootstrap'); +} catch (e) {} + window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; - -/** - * Echo exposes an expressive API for subscribing to channels and listening - * for events that are broadcast by Laravel. Echo and event broadcasting - * allows your team to easily build robust real-time web applications. - */ - -// import Echo from 'laravel-echo'; - -// window.Pusher = require('pusher-js'); - -// window.Echo = new Echo({ -// broadcaster: 'pusher', -// key: process.env.MIX_PUSHER_APP_KEY, -// cluster: process.env.MIX_PUSHER_APP_CLUSTER, -// forceTLS: true -// }); diff --git a/resources/sass/_variables.scss b/resources/sass/_variables.scss new file mode 100644 index 0000000..0407ab5 --- /dev/null +++ b/resources/sass/_variables.scss @@ -0,0 +1,19 @@ +// Body +$body-bg: #f8fafc; + +// Typography +$font-family-sans-serif: 'Nunito', sans-serif; +$font-size-base: 0.9rem; +$line-height-base: 1.6; + +// Colors +$blue: #3490dc; +$indigo: #6574cd; +$purple: #9561e2; +$pink: #f66d9b; +$red: #e3342f; +$orange: #f6993f; +$yellow: #ffed4a; +$green: #38c172; +$teal: #4dc0b5; +$cyan: #6cb2eb; diff --git a/resources/sass/admin/_bootstrap.scss b/resources/sass/admin/_bootstrap.scss new file mode 100644 index 0000000..6090760 --- /dev/null +++ b/resources/sass/admin/_bootstrap.scss @@ -0,0 +1,3 @@ +@import "~bootstrap/scss/functions"; +@import "~bootstrap/scss/variables"; +@import "~bootstrap/scss/mixins"; diff --git a/resources/sass/admin/_variables.scss b/resources/sass/admin/_variables.scss new file mode 100644 index 0000000..0407ab5 --- /dev/null +++ b/resources/sass/admin/_variables.scss @@ -0,0 +1,19 @@ +// Body +$body-bg: #f8fafc; + +// Typography +$font-family-sans-serif: 'Nunito', sans-serif; +$font-size-base: 0.9rem; +$line-height-base: 1.6; + +// Colors +$blue: #3490dc; +$indigo: #6574cd; +$purple: #9561e2; +$pink: #f66d9b; +$red: #e3342f; +$orange: #f6993f; +$yellow: #ffed4a; +$green: #38c172; +$teal: #4dc0b5; +$cyan: #6cb2eb; diff --git a/resources/sass/admin/app.scss b/resources/sass/admin/app.scss new file mode 100644 index 0000000..cb2a187 --- /dev/null +++ b/resources/sass/admin/app.scss @@ -0,0 +1,10 @@ +//Boostrap函式庫 +@import "bootstrap"; +//元件 +@import "components/datatables"; +@import "components/blockui"; + + + + + diff --git a/resources/sass/admin/components/_blockui.scss b/resources/sass/admin/components/_blockui.scss new file mode 100644 index 0000000..e7681cc --- /dev/null +++ b/resources/sass/admin/components/_blockui.scss @@ -0,0 +1,5 @@ +.blockUI { + &.blockOverlay { + z-index: 1098 !important; + } +} diff --git a/resources/sass/admin/components/_datatables.scss b/resources/sass/admin/components/_datatables.scss new file mode 100644 index 0000000..dff1865 --- /dev/null +++ b/resources/sass/admin/components/_datatables.scss @@ -0,0 +1,14 @@ +//結果數目 +div.dataTables_wrapper { + div.dataTables_info { + white-space: unset; + } +} +//搜尋框 +div.dataTables_wrapper { + div.dataTables_filter { + @include media-breakpoint-down(sm) { + text-align: right; + } + } +} diff --git a/resources/sass/admin/lib.scss b/resources/sass/admin/lib.scss new file mode 100644 index 0000000..e76ea5d --- /dev/null +++ b/resources/sass/admin/lib.scss @@ -0,0 +1,37 @@ +/** +Fonts + */ +@import url('https://fonts.googleapis.com/css?family=Nunito'); +/** +Variables + */ +@import 'variables'; +/** +Bootstrap + */ +@import '~bootstrap/scss/bootstrap'; +/** +Core UI + */ +@import "~@coreui/coreui/scss/coreui-standalone"; +/** +CoreUI Icons +*/ +@import "~@coreui/icons/scss/free"; +/** +Flag Icons + */ +@import "~flag-icon-css/sass/flag-icon"; +/** +Font Awesome + */ +@import "~font-awesome/scss/font-awesome"; +/** +Simple Line Icons + */ +@import "~simple-line-icons/scss/simple-line-icons"; +/** +DataTables + */ +@import "~datatables.net-bs4/css/dataTables.bootstrap4.min.css"; +@import "~datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css"; diff --git a/resources/sass/app.scss b/resources/sass/app.scss index 8337712..3193ffa 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -1 +1,8 @@ -// +// Fonts +@import url('https://fonts.googleapis.com/css?family=Nunito'); + +// Variables +@import 'variables'; + +// Bootstrap +@import '~bootstrap/scss/bootstrap'; diff --git a/resources/views/admin/index.blade.php b/resources/views/admin/index.blade.php new file mode 100644 index 0000000..f923744 --- /dev/null +++ b/resources/views/admin/index.blade.php @@ -0,0 +1,3 @@ +@extends('admin.layouts.app') + +@section('title', 'Admin Area') diff --git a/resources/views/admin/layouts/app.blade.php b/resources/views/admin/layouts/app.blade.php new file mode 100644 index 0000000..5c54257 --- /dev/null +++ b/resources/views/admin/layouts/app.blade.php @@ -0,0 +1,74 @@ + + + + + + + + + @stack('admin-app-head-scripts') + + @section('admin-app-styles') + + + + + @show + @stack('admin-app-styles') + + + +
+ {{-- 左側主選單--}} + + {{-- 主頁面 --}} +
+
+ @yield('admin-page-content') +
+
+ {{-- 右側選單 --}} + +
+ +@section('admin-app-scripts') + +@show +@stack('admin-app-scripts') + + diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 0000000..ca56d89 --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,73 @@ +@extends('layouts.app') + +@section('content-body') +
+
+
+
+
{{ __('Login') }}
+ +
+
+ @csrf + +
+ + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+
+ +
+ + +
+ + + @error('password') + + {{ $message }} + + @enderror +
+
+ +
+
+
+ + + +
+
+
+ +
+
+ + + @if (Route::has('password.request')) + + {{ __('Forgot Your Password?') }} + + @endif +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/passwords/confirm.blade.php b/resources/views/auth/passwords/confirm.blade.php new file mode 100644 index 0000000..4503545 --- /dev/null +++ b/resources/views/auth/passwords/confirm.blade.php @@ -0,0 +1,49 @@ +@extends('layouts.app') + +@section('content-body') +
+
+
+
+
{{ __('Confirm Password') }}
+ +
+ {{ __('Please confirm your password before continuing.') }} + +
+ @csrf + +
+ + +
+ + + @error('password') + + {{ $message }} + + @enderror +
+
+ +
+
+ + + @if (Route::has('password.request')) + + {{ __('Forgot Your Password?') }} + + @endif +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/passwords/email.blade.php b/resources/views/auth/passwords/email.blade.php new file mode 100644 index 0000000..7a706c1 --- /dev/null +++ b/resources/views/auth/passwords/email.blade.php @@ -0,0 +1,47 @@ +@extends('layouts.app') + +@section('content-body') +
+
+
+
+
{{ __('Reset Password') }}
+ +
+ @if (session('status')) + + @endif + +
+ @csrf + +
+ + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/passwords/reset.blade.php b/resources/views/auth/passwords/reset.blade.php new file mode 100644 index 0000000..252661b --- /dev/null +++ b/resources/views/auth/passwords/reset.blade.php @@ -0,0 +1,65 @@ +@extends('layouts.app') + +@section('content-body') +
+
+
+
+
{{ __('Reset Password') }}
+ +
+
+ @csrf + + + +
+ + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+
+ +
+ + +
+ + + @error('password') + + {{ $message }} + + @enderror +
+
+ +
+ + +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php new file mode 100644 index 0000000..52d6480 --- /dev/null +++ b/resources/views/auth/register.blade.php @@ -0,0 +1,77 @@ +@extends('layouts.app') + +@section('content-body') +
+
+
+
+
{{ __('Register') }}
+ +
+
+ @csrf + +
+ + +
+ + + @error('name') + + {{ $message }} + + @enderror +
+
+ +
+ + +
+ + + @error('email') + + {{ $message }} + + @enderror +
+
+ +
+ + +
+ + + @error('password') + + {{ $message }} + + @enderror +
+
+ +
+ + +
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/verify.blade.php b/resources/views/auth/verify.blade.php new file mode 100644 index 0000000..0c74c46 --- /dev/null +++ b/resources/views/auth/verify.blade.php @@ -0,0 +1,28 @@ +@extends('layouts.app') + +@section('content-body') +
+
+
+
+
{{ __('Verify Your Email Address') }}
+ +
+ @if (session('resent')) + + @endif + + {{ __('Before proceeding, please check your email for a verification link.') }} + {{ __('If you did not receive the email') }}, +
+ @csrf + . +
+
+
+
+
+
+@endsection diff --git a/resources/views/components/nav.blade.php b/resources/views/components/nav.blade.php new file mode 100644 index 0000000..0ad863b --- /dev/null +++ b/resources/views/components/nav.blade.php @@ -0,0 +1,50 @@ + diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index f16b07f..753f84a 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -1 +1,23 @@ @extends('layouts.app') + +@section('content-body') +
+
+
+
+
Dashboard
+ +
+ @if (session('status')) + + @endif + + You are logged in! +
+
+
+
+
+@endsection diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index b1799a1..bb177c8 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -19,8 +19,13 @@ @section('content-header') + @include('components.nav') @show -@yield('content-body') + +
+ @yield('content-body') +
+ @section('content-footer') @show diff --git a/routes/web.php b/routes/web.php index 9bdaea4..41d5dbc 100644 --- a/routes/web.php +++ b/routes/web.php @@ -11,6 +11,20 @@ | */ -Route::get('/', function () { - return view('home'); +/** + * Authenticate routes + */ +Auth::routes(['verify' => true]); + +/** + * Static page routes + */ +Route::view('/', 'home')->name('index'); + +/** + * Admin routes + */ +Route::group(['prefix' => config('admin.route'), 'middleware' => ['admin.area'], 'as' => config('admin.route_name_prefix')], function() { + Route::get('/', 'AdminPageController@index')->name('index'); }); + diff --git a/webpack.mix.js b/webpack.mix.js index 8a923cb..abd5d0a 100644 --- a/webpack.mix.js +++ b/webpack.mix.js @@ -11,5 +11,16 @@ const mix = require('laravel-mix'); | */ -mix.js('resources/js/app.js', 'public/js') - .sass('resources/sass/app.scss', 'public/css'); +let publicJsDir = 'public/js', + publicCssDir = 'public/css', + publicAdminJsDir = 'public/js/admin', + publicAdminCssDir = 'public/css/admin' + +mix.js('resources/js/app.js', publicJsDir) + +mix.sass('resources/sass/app.scss', publicCssDir); + +mix.js('resources/js/admin/app.js', publicAdminJsDir) + +mix.sass('resources/sass/admin/lib.scss', publicAdminCssDir) + .sass('resources/sass/admin/app.scss', publicAdminCssDir)