Để bắt đầu một cách thuận lợi, bạn nên tập trung vào một lộ trình học. Ví dụ: Để đi làm với vị trí “Lập trình viên Front-end” bạn nên tập trung vào lộ trình “Front-end”.
53 |
54 |
--------------------------------------------------------------------------------
/config/sanctum.php:
--------------------------------------------------------------------------------
1 | explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
19 | '%s%s',
20 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
21 | Sanctum::currentApplicationUrlWithPort()
22 | ))),
23 |
24 | /*
25 | |--------------------------------------------------------------------------
26 | | Sanctum Guards
27 | |--------------------------------------------------------------------------
28 | |
29 | | This array contains the authentication guards that will be checked when
30 | | Sanctum is trying to authenticate a request. If none of these guards
31 | | are able to authenticate the request, Sanctum will use the bearer
32 | | token that's present on an incoming request for authentication.
33 | |
34 | */
35 |
36 | 'guard' => ['web'],
37 |
38 | /*
39 | |--------------------------------------------------------------------------
40 | | Expiration Minutes
41 | |--------------------------------------------------------------------------
42 | |
43 | | This value controls the number of minutes until an issued token will be
44 | | considered expired. If this value is null, personal access tokens do
45 | | not expire. This won't tweak the lifetime of first-party sessions.
46 | |
47 | */
48 |
49 | 'expiration' => null,
50 |
51 | /*
52 | |--------------------------------------------------------------------------
53 | | Sanctum Middleware
54 | |--------------------------------------------------------------------------
55 | |
56 | | When authenticating your first-party SPA with Sanctum you may need to
57 | | customize some of the middleware Sanctum uses while processing the
58 | | request. You may change the middleware listed below as required.
59 | |
60 | */
61 |
62 | 'middleware' => [
63 | 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
64 | 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
65 | ],
66 |
67 | ];
68 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laravel/laravel",
3 | "type": "project",
4 | "description": "The Laravel Framework.",
5 | "keywords": ["framework", "laravel"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.1",
9 | "filament/filament": "^2.16.55",
10 | "filament/spatie-laravel-media-library-plugin": "^2.16.55",
11 | "guzzlehttp/guzzle": "^7.5",
12 | "inertiajs/inertia-laravel": "^0.6.4",
13 | "laravel/framework": "^10.14.1",
14 | "laravel/sanctum": "^3.0.1",
15 | "laravel/socialite": "^5.5.6",
16 | "laravel/tinker": "^2.7.3",
17 | "spatie/laravel-medialibrary": "^10.7.4",
18 | "tightenco/ziggy": "^1.5"
19 | },
20 | "require-dev": {
21 | "barryvdh/laravel-debugbar": "^3.7",
22 | "fakerphp/faker": "^1.20",
23 | "laravel-lang/attributes": "^2.0.9",
24 | "laravel-lang/lang": "^12.6",
25 | "laravel-lang/publisher": "^14.4",
26 | "laravel/pint": "^1.2.1",
27 | "laravel/sail": "^1.16.3",
28 | "mockery/mockery": "^1.5.1",
29 | "nunomaduro/collision": "^7.7.0",
30 | "phpunit/phpunit": "^10.2.4",
31 | "spatie/laravel-ignition": "^2.2.0"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "App\\": "app/",
36 | "Database\\Factories\\": "database/factories/",
37 | "Database\\Seeders\\": "database/seeders/"
38 | }
39 | },
40 | "autoload-dev": {
41 | "psr-4": {
42 | "Tests\\": "tests/"
43 | }
44 | },
45 | "scripts": {
46 | "post-autoload-dump": [
47 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
48 | "@php artisan package:discover --ansi"
49 | ],
50 | "post-update-cmd": [
51 | "@php artisan vendor:publish --tag=laravel-assets --ansi --force",
52 | "@php artisan filament:upgrade"
53 | ],
54 | "post-root-package-install": [
55 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
56 | ],
57 | "post-create-project-cmd": [
58 | "@php artisan key:generate --ansi"
59 | ]
60 | },
61 | "extra": {
62 | "laravel": {
63 | "dont-discover": []
64 | }
65 | },
66 | "config": {
67 | "optimize-autoloader": true,
68 | "preferred-install": "dist",
69 | "sort-packages": true,
70 | "allow-plugins": {
71 | "pestphp/pest-plugin": true
72 | }
73 | },
74 | "minimum-stability": "dev",
75 | "prefer-stable": true
76 | }
77 |
--------------------------------------------------------------------------------
/config/filesystems.php:
--------------------------------------------------------------------------------
1 | env('FILESYSTEM_DISK', 'local'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Filesystem Disks
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure as many filesystem "disks" as you wish, and you
24 | | may even configure multiple disks of the same driver. Defaults have
25 | | been set up for each driver as an example of the required values.
26 | |
27 | | Supported Drivers: "local", "ftp", "sftp", "s3"
28 | |
29 | */
30 |
31 | 'disks' => [
32 |
33 | 'local' => [
34 | 'driver' => 'local',
35 | 'root' => storage_path('app'),
36 | 'throw' => false,
37 | ],
38 |
39 | 'public' => [
40 | 'driver' => 'local',
41 | 'root' => storage_path('app/public'),
42 | 'url' => env('APP_URL').'/storage',
43 | 'visibility' => 'public',
44 | 'throw' => false,
45 | ],
46 |
47 | 's3' => [
48 | 'driver' => 's3',
49 | 'key' => env('AWS_ACCESS_KEY_ID'),
50 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
51 | 'region' => env('AWS_DEFAULT_REGION'),
52 | 'bucket' => env('AWS_BUCKET'),
53 | 'url' => env('AWS_URL'),
54 | 'endpoint' => env('AWS_ENDPOINT'),
55 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
56 | 'throw' => false,
57 | ],
58 |
59 | ],
60 |
61 | /*
62 | |--------------------------------------------------------------------------
63 | | Symbolic Links
64 | |--------------------------------------------------------------------------
65 | |
66 | | Here you may configure the symbolic links that will be created when the
67 | | `storage:link` Artisan command is executed. The array keys should be
68 | | the locations of the links and the values should be their targets.
69 | |
70 | */
71 |
72 | 'links' => [
73 | public_path('storage') => storage_path('app/public'),
74 | ],
75 |
76 | ];
77 |
--------------------------------------------------------------------------------
/lang/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "(and :count more error)": "(and :count more error)",
3 | "(and :count more errors)": "(and :count more errors)",
4 | "All rights reserved.": "All rights reserved.",
5 | "Forbidden": "Forbidden",
6 | "Go to page :page": "Go to page :page",
7 | "Hello!": "Hello!",
8 | "If you did not create an account, no further action is required.": "If you did not create an account, no further action is required.",
9 | "If you did not request a password reset, no further action is required.": "If you did not request a password reset, no further action is required.",
10 | "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:",
11 | "Login": "Login",
12 | "Logout": "Logout",
13 | "Not Found": "Not Found",
14 | "of": "of",
15 | "Page Expired": "Page Expired",
16 | "Pagination Navigation": "Pagination Navigation",
17 | "Please click the button below to verify your email address.": "Please click the button below to verify your email address.",
18 | "Regards": "Regards",
19 | "Register": "Register",
20 | "Reset Password": "Reset Password",
21 | "Reset Password Notification": "Reset Password Notification",
22 | "results": "results",
23 | "Server Error": "Server Error",
24 | "Service Unavailable": "Service Unavailable",
25 | "Showing": "Showing",
26 | "The :attribute must contain at least one letter.": "The :attribute must contain at least one letter.",
27 | "The :attribute must contain at least one number.": "The :attribute must contain at least one number.",
28 | "The :attribute must contain at least one symbol.": "The :attribute must contain at least one symbol.",
29 | "The :attribute must contain at least one uppercase and one lowercase letter.": "The :attribute must contain at least one uppercase and one lowercase letter.",
30 | "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "The given :attribute has appeared in a data leak. Please choose a different :attribute.",
31 | "The given data was invalid.": "The given data was invalid.",
32 | "This password reset link will expire in :count minutes.": "This password reset link will expire in :count minutes.",
33 | "to": "to",
34 | "Toggle navigation": "Toggle navigation",
35 | "Too Many Requests": "Too Many Requests",
36 | "Unauthorized": "Unauthorized",
37 | "Verify Email Address": "Verify Email Address",
38 | "Whoops!": "Whoops!",
39 | "You are receiving this email because we received a password reset request for your account.": "You are receiving this email because we received a password reset request for your account."
40 | }
--------------------------------------------------------------------------------
/lang/vi.json:
--------------------------------------------------------------------------------
1 | {
2 | "(and :count more error)": "(và :count lỗi khác)",
3 | "(and :count more errors)": "(và :count lỗi khác)",
4 | "All rights reserved.": "Đã đăng kí bản quyền",
5 | "Forbidden": "Cấm Truy Cập",
6 | "Go to page :page": "Tới trang :page",
7 | "Hello!": "Xin chào!",
8 | "If you did not create an account, no further action is required.": "Nếu bạn không đăng ký tài khoản này, bạn không cần thực hiện thêm hành động nào.",
9 | "If you did not request a password reset, no further action is required.": "Nếu bạn không yêu cầu đặt lại mật khẩu, bạn không cần thực hiện thêm hành động nào.",
10 | "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Nếu bạn gặp vấn đề khi click vào nút \":actionText\", hãy sao chép dán địa chỉ bên dưới\nvào trình duyệt web của bạn:",
11 | "Login": "Đăng nhập",
12 | "Logout": "Đăng xuất",
13 | "Not Found": "Không Tìm Thấy",
14 | "of": "trong",
15 | "Page Expired": "Trang Đã Hết Hạn",
16 | "Pagination Navigation": "Điều hướng phân trang",
17 | "Please click the button below to verify your email address.": "Vui lòng click vào nút bên dưới để xác minh địa chỉ email của bạn.",
18 | "Regards": "Trân trọng",
19 | "Register": "Đăng ký",
20 | "Reset Password": "Đặt Lại Mật Khẩu",
21 | "Reset Password Notification": "Thông Báo Đặt Lại Mật Khẩu",
22 | "results": "kết quả",
23 | "Server Error": "Máy Chủ Gặp Sự Cố",
24 | "Service Unavailable": "Dịch Vụ Không Khả Dụng",
25 | "Showing": "Đang hiển thị",
26 | "The :attribute must contain at least one letter.": "Trường :attribute phải chứa ít nhất một chữ cái.",
27 | "The :attribute must contain at least one number.": "Trường :attribute phải chứa ít nhất một số.",
28 | "The :attribute must contain at least one symbol.": "Trường :attribute must phải chứa ít nhất một ký hiệu.",
29 | "The :attribute must contain at least one uppercase and one lowercase letter.": "Trường :attribute phải chứa ít nhất một chữ hoa và một chữ thường.",
30 | "The given :attribute has appeared in a data leak. Please choose a different :attribute.": ":Attribute đã cho đã xuất hiện trong một vụ rò rỉ dữ liệu. Vui lòng chọn :attribute khác.",
31 | "The given data was invalid.": "Dữ liệu nhận được không hợp lệ.",
32 | "This password reset link will expire in :count minutes.": "Đường dẫn lấy lại mật khẩu sẽ hết hạn trong :count phút.",
33 | "to": "tới",
34 | "Toggle navigation": "Chuyển hướng điều hướng",
35 | "Too Many Requests": "Quá Nhiều Yêu Cầu",
36 | "Unauthorized": "Không Được Phép",
37 | "Verify Email Address": "Xác Minh Địa Chỉ Email",
38 | "Whoops!": "Rất tiếc!",
39 | "You are receiving this email because we received a password reset request for your account.": "Bạn nhận được email này vì chúng tôi đã nhận được yêu cầu đặt lại mật khẩu cho tài khoản của bạn."
40 | }
--------------------------------------------------------------------------------
/app/Http/Kernel.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | protected $middleware = [
17 | // \App\Http\Middleware\TrustHosts::class,
18 | \App\Http\Middleware\TrustProxies::class,
19 | \Illuminate\Http\Middleware\HandleCors::class,
20 | \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
21 | \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
22 | \App\Http\Middleware\TrimStrings::class,
23 | \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
24 | ];
25 |
26 | /**
27 | * The application's route middleware groups.
28 | *
29 | * @var array>
30 | */
31 | protected $middlewareGroups = [
32 | 'web' => [
33 | \App\Http\Middleware\EncryptCookies::class,
34 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
35 | \Illuminate\Session\Middleware\StartSession::class,
36 | \Illuminate\View\Middleware\ShareErrorsFromSession::class,
37 | \App\Http\Middleware\VerifyCsrfToken::class,
38 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
39 | \App\Http\Middleware\HandleInertiaRequests::class,
40 | ],
41 |
42 | 'api' => [
43 | // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
44 | 'throttle:api',
45 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
46 | ],
47 | ];
48 |
49 | /**
50 | * The application's route middleware.
51 | *
52 | * These middleware may be assigned to groups or used individually.
53 | *
54 | * @var array
55 | */
56 | protected $routeMiddleware = [
57 | 'auth' => \App\Http\Middleware\Authenticate::class,
58 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
59 | 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
60 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
61 | 'can' => \Illuminate\Auth\Middleware\Authorize::class,
62 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
63 | 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
64 | 'signed' => \App\Http\Middleware\ValidateSignature::class,
65 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
66 | 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
67 | ];
68 | }
69 |
--------------------------------------------------------------------------------
/resources/js/components/ReviewForm.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Để theo ngành IT - Phần mềm cần rèn luyện những kỹ năng nào? Bạn đã có sẵn tố chất phù hợp với ngành chưa? Cùng thăm quan các công ty IT và tìm hiểu về văn hóa, tác phong làm việc của ngành này nhé các bạn.
21 |
22 |
23 |
24 |
25 |
26 |
Kiến thức nhập môn IT
27 | Miễn phí
28 |
Để có cái nhìn tổng quan về ngành IT - Lập trình web các bạn nên xem các videos tại khóa này trước nhé.
29 |
30 |
31 |
32 |
33 |
34 |
2. HTML và CSS
35 |
Để học web Front-end chúng ta luôn bắt đầu với ngôn ngữ HTML và CSS, đây là 2 ngôn ngữ có mặt trong mọi website trên internet. Trong khóa học này F8 sẽ chia sẻ từ những kiến thức cơ bản nhất. Sau khóa học này bạn sẽ tự làm được 2 giao diện websites là The Band và Shopee.
36 |
37 |
38 |
39 |
40 |
41 |
HTML CSS Pro
42 | 1.299.000đ
43 |
Từ cơ bản tới chuyên sâu, thực hành 8 dự án, hàng trăm bài tập, trang hỏi đáp riêng, cấp chứng chỉ sau khóa học và mua một lần học mãi mãi.
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/database/seeders/CourseSeeder.php:
--------------------------------------------------------------------------------
1 | 'Kiến thức nhập môn IT',
25 | 'subtitle' => 'Để có cái nhìn tổng quan về ngành IT - Lập trình web các bạn nên xem các videos tại khóa này trước nhé.',
26 | ],
27 | [
28 | 'name' => 'HTML CSS từ Zero đến Hero',
29 | 'subtitle' => 'Trong khóa này chúng ta sẽ cùng nhau xây dựng giao diện 2 trang web là The Band & Shopee.',
30 | ],
31 | [
32 | 'name' => 'Responsive với Grid System',
33 | 'subtitle' => 'Trong khóa này chúng ta sẽ học về cách xây dựng giao diện web responsive với Grid System, tương tự Bootstrap 4.',
34 | ],
35 | [
36 | 'name' => 'Lập trình JavaScript cơ bản',
37 | 'subtitle' => 'Học Javascript cơ bản phù hợp cho người chưa từng học lập trình. Với hơn 100 bài học và có bài tập thực hành sau mỗi bài học.',
38 | ],
39 | [
40 | 'name' => 'Lập trình JavaScript nâng cao',
41 | 'subtitle' => 'Hiểu sâu hơn về cách Javascript hoạt động, tìm hiểu về IIFE, closure, reference types, this keyword, bind, call, apply, prototype, ...',
42 | ],
43 | [
44 | 'name' => 'Làm việc với Terminal & Ubuntu',
45 | 'subtitle' => 'Sở hữu một Terminal hiện đại, mạnh mẽ trong tùy biến và học cách làm việc với Ubuntu là một bước quan trọng trên con đường trở thành một Web Developer.',
46 | ],
47 | [
48 | 'name' => 'Xây dựng Website với React JS',
49 | 'subtitle' => 'Khóa học ReactJS từ cơ bản tới nâng cao, kết quả của khóa học này là bạn có thể làm hầu hết các dự án thường gặp với ReactJS. Cuối khóa học này bạn sẽ sở hữu một dự án giống Tiktok.com, bạn có thể tự tin đi xin việc khi nắm chắc các kiến thức được chia sẻ trong khóa học này.',
50 | ],
51 | [
52 | 'name' => 'Node & ExpressJS',
53 | 'subtitle' => 'Học Back-end với Node & ExpressJS framework, hiểu các khái niệm khi làm Back-end và xây dựng RESTful API cho trang web.',
54 | ],
55 | ];
56 |
57 | foreach ($courses as $key => $course) {
58 | Course::query()
59 | ->create([
60 | ...$course,
61 | 'category_id' => rand(1, $categoriesCount),
62 | 'slug' => Str::slug($course['name']),
63 | 'level' => InstructionalLevel::Beginner,
64 | ])
65 | ->addMediaFromDisk('courses/'.$key + 1 .'.png', 'public')
66 | ->toMediaCollection('courses');
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/config/queue.php:
--------------------------------------------------------------------------------
1 | env('QUEUE_CONNECTION', 'sync'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Queue Connections
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure the connection information for each server that
24 | | is used by your application. A default configuration has been added
25 | | for each back-end shipped with Laravel. You are free to add more.
26 | |
27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
28 | |
29 | */
30 |
31 | 'connections' => [
32 |
33 | 'sync' => [
34 | 'driver' => 'sync',
35 | ],
36 |
37 | 'database' => [
38 | 'driver' => 'database',
39 | 'table' => 'jobs',
40 | 'queue' => 'default',
41 | 'retry_after' => 90,
42 | 'after_commit' => false,
43 | ],
44 |
45 | 'beanstalkd' => [
46 | 'driver' => 'beanstalkd',
47 | 'host' => 'localhost',
48 | 'queue' => 'default',
49 | 'retry_after' => 90,
50 | 'block_for' => 0,
51 | 'after_commit' => false,
52 | ],
53 |
54 | 'sqs' => [
55 | 'driver' => 'sqs',
56 | 'key' => env('AWS_ACCESS_KEY_ID'),
57 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
58 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
59 | 'queue' => env('SQS_QUEUE', 'default'),
60 | 'suffix' => env('SQS_SUFFIX'),
61 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
62 | 'after_commit' => false,
63 | ],
64 |
65 | 'redis' => [
66 | 'driver' => 'redis',
67 | 'connection' => 'default',
68 | 'queue' => env('REDIS_QUEUE', 'default'),
69 | 'retry_after' => 90,
70 | 'block_for' => null,
71 | 'after_commit' => false,
72 | ],
73 |
74 | ],
75 |
76 | /*
77 | |--------------------------------------------------------------------------
78 | | Failed Queue Jobs
79 | |--------------------------------------------------------------------------
80 | |
81 | | These options configure the behavior of failed queue job logging so you
82 | | can control which database and table are used to store the jobs that
83 | | have failed. You may change them to any database / table you wish.
84 | |
85 | */
86 |
87 | 'failed' => [
88 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
89 | 'database' => env('DB_CONNECTION', 'mysql'),
90 | 'table' => 'failed_jobs',
91 | ],
92 |
93 | ];
94 |
--------------------------------------------------------------------------------
/resources/js/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
78 |
79 |
--------------------------------------------------------------------------------
/resources/js/Pages/Settings.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
Thiết lập
24 |
25 |
Thông tin tài khoản
26 |
76 |
77 |
78 |
Liên kết đăng nhập mạng xã hội
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/database/seeders/LessonSeeder.php:
--------------------------------------------------------------------------------
1 | 'Nhận được gì sau khóa học?',
23 | 'youtube_id' => 'R6plN3FvzFY',
24 | ],
25 | [
26 | 'name' => 'HTML, CSS là gì?',
27 | 'youtube_id' => 'zwsPND378OQ',
28 | ],
29 | [
30 | 'name' => 'Cài đặt babel plugin module resolver',
31 | 'youtube_id' => 'wwGkGi1WFgg',
32 | ],
33 | [
34 | 'name' => 'SSH vào Server Ubuntu Thật ',
35 | 'youtube_id' => 'ScLOfVwezKU',
36 | ],
37 | [
38 | 'name' => 'Cài đặt NodeJS trên WSL',
39 | 'youtube_id' => '9rddrjDkmWo',
40 | ],
41 | [
42 | 'name' => 'Tạo dự án ReactJS trên WSL',
43 | 'youtube_id' => 'aj3HXDfrM2Q',
44 | ],
45 | [
46 | 'name' => 'Chạy dự án ExpressJS trên WSL',
47 | 'youtube_id' => 'MpYEUtbbFSg',
48 | ],
49 | [
50 | 'name' => 'Cài đặt PHP 8 & Composer trên WSL',
51 | 'youtube_id' => '_Le-We25B0k',
52 | ],
53 | [
54 | 'name' => 'Chạy dự án Laravel 8 trên WSL',
55 | 'youtube_id' => 'EHlmj2KDyHU',
56 | ],
57 | [
58 | 'name' => 'Cài Đặt Windows Terminal',
59 | 'youtube_id' => 'egSxAF-Sak4',
60 | ],
61 | [
62 | 'name' => 'Giới thiệu Windows Terminal & WSL',
63 | 'youtube_id' => '7ppRSaGT1uw',
64 | ],
65 | [
66 | 'name' => 'React Router V6',
67 | 'youtube_id' => '5jYlY4y5Dfs',
68 | ],
69 | [
70 | 'name' => 'useImperativeHandle() hook',
71 | 'youtube_id' => 'dSzf0nv6QmM',
72 | ],
73 | [
74 | 'name' => 'useReducer() hook',
75 | 'youtube_id' => 'yn-8PVKBBn0',
76 | ],
77 | [
78 | 'name' => 'useMemo() hook',
79 | 'youtube_id' => 'TGaxZyZzB7E',
80 | ],
81 | [
82 | 'name' => 'useEffect with dependencies',
83 | 'youtube_id' => 'OOt2VRa1Oeg',
84 | ],
85 | [
86 | 'name' => 'Todolist with useState() ',
87 | 'youtube_id' => 'bpVFSiNsFHY',
88 | ],
89 | [
90 | 'name' => 'CRA Folder Structure',
91 | 'youtube_id' => '-Ka_3RkQAvk',
92 | ],
93 | [
94 | 'name' => 'NPM, NPX và YARN là gì?',
95 | 'youtube_id' => '7sX_8lKURqo',
96 | ],
97 | [
98 | 'name' => 'Tạo dự án với React + Webpack',
99 | 'youtube_id' => '1EBe-l1E3pM',
100 | ],
101 | ];
102 |
103 | foreach ($lessons as $lesson) {
104 | Lesson::create([
105 | ...$lesson,
106 | 'course_id' => rand(1, $coursesCount),
107 | 'time_duration' => random_int(10, 600),
108 | ]);
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/config/cache.php:
--------------------------------------------------------------------------------
1 | env('CACHE_DRIVER', 'file'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Cache Stores
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may define all of the cache "stores" for your application as
26 | | well as their drivers. You may even define multiple stores for the
27 | | same cache driver to group types of items stored in your caches.
28 | |
29 | | Supported drivers: "apc", "array", "database", "file",
30 | | "memcached", "redis", "dynamodb", "octane", "null"
31 | |
32 | */
33 |
34 | 'stores' => [
35 |
36 | 'apc' => [
37 | 'driver' => 'apc',
38 | ],
39 |
40 | 'array' => [
41 | 'driver' => 'array',
42 | 'serialize' => false,
43 | ],
44 |
45 | 'database' => [
46 | 'driver' => 'database',
47 | 'table' => 'cache',
48 | 'connection' => null,
49 | 'lock_connection' => null,
50 | ],
51 |
52 | 'file' => [
53 | 'driver' => 'file',
54 | 'path' => storage_path('framework/cache/data'),
55 | ],
56 |
57 | 'memcached' => [
58 | 'driver' => 'memcached',
59 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
60 | 'sasl' => [
61 | env('MEMCACHED_USERNAME'),
62 | env('MEMCACHED_PASSWORD'),
63 | ],
64 | 'options' => [
65 | // Memcached::OPT_CONNECT_TIMEOUT => 2000,
66 | ],
67 | 'servers' => [
68 | [
69 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'),
70 | 'port' => env('MEMCACHED_PORT', 11211),
71 | 'weight' => 100,
72 | ],
73 | ],
74 | ],
75 |
76 | 'redis' => [
77 | 'driver' => 'redis',
78 | 'connection' => 'cache',
79 | 'lock_connection' => 'default',
80 | ],
81 |
82 | 'dynamodb' => [
83 | 'driver' => 'dynamodb',
84 | 'key' => env('AWS_ACCESS_KEY_ID'),
85 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
86 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
87 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
88 | 'endpoint' => env('DYNAMODB_ENDPOINT'),
89 | ],
90 |
91 | 'octane' => [
92 | 'driver' => 'octane',
93 | ],
94 |
95 | ],
96 |
97 | /*
98 | |--------------------------------------------------------------------------
99 | | Cache Key Prefix
100 | |--------------------------------------------------------------------------
101 | |
102 | | When utilizing the APC, database, memcached, Redis, or DynamoDB cache
103 | | stores there might be other applications using the same cache. For
104 | | that reason, you may prefix every cache key to avoid collisions.
105 | |
106 | */
107 |
108 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
109 |
110 | ];
111 |
--------------------------------------------------------------------------------
/app/Filament/Resources/UserResource.php:
--------------------------------------------------------------------------------
1 | schema([
26 | Forms\Components\Card::make()
27 | ->schema([
28 | Forms\Components\TextInput::make('name')
29 | ->label('Họ tên')
30 | ->required(),
31 |
32 | Forms\Components\TextInput::make('username')
33 | ->label('Tên đăng nhập')
34 | ->unique()
35 | ->required(),
36 |
37 | Forms\Components\TextInput::make('email')
38 | ->label('Email')
39 | ->unique()
40 | ->required(),
41 |
42 | Forms\Components\TextInput::make('phone')
43 | ->label('Số điện thoại')
44 | ->unique()
45 | ->required(),
46 |
47 | Forms\Components\TextInput::make('password')
48 | ->label('Mật khẩu')
49 | ->required()
50 | ->minLength(6)
51 | ->password()
52 | ->dehydrateStateUsing(fn (string $state) => Hash::make($state)),
53 |
54 | Forms\Components\TextInput::make('password_confirmation')
55 | ->label('Nhập lại mật khẩu')
56 | ->password(),
57 |
58 | Forms\Components\Textarea::make('bio')
59 | ->label('Giới thiệu')
60 | ->nullable()
61 | ->columnSpanFull(),
62 |
63 | Forms\Components\SpatieMediaLibraryFileUpload::make('avatar')
64 | ->label('Ảnh đại diện')
65 | ->nullable()
66 | ->columnSpanFull(),
67 | ])
68 | ->columns(),
69 | ]);
70 | }
71 |
72 | public static function table(Table $table): Table
73 | {
74 | return $table
75 | ->columns([
76 | Tables\Columns\ImageColumn::make('avatar_url')
77 | ->label('Ảnh đại diện')
78 | ->circular(),
79 |
80 | Tables\Columns\TextColumn::make('name')
81 | ->label('Họ tên')
82 | ->searchable(),
83 |
84 | Tables\Columns\TextColumn::make('email')
85 | ->searchable(),
86 |
87 | Tables\Columns\TextColumn::make('username')
88 | ->label('Tên người dùng')
89 | ->searchable(),
90 | ])
91 | ->filters([
92 | //
93 | ])
94 | ->actions([
95 | Tables\Actions\EditAction::make(),
96 | ])
97 | ->bulkActions([
98 | Tables\Actions\DeleteBulkAction::make(),
99 | ]);
100 | }
101 |
102 | public static function getPages(): array
103 | {
104 | return [
105 | 'index' => Pages\ListUsers::route('/'),
106 | 'create' => Pages\CreateUser::route('/create'),
107 | 'edit' => Pages\EditUser::route('/{record}/edit'),
108 | ];
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/config/mail.php:
--------------------------------------------------------------------------------
1 | env('MAIL_MAILER', 'smtp'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Mailer Configurations
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure all of the mailers used by your application plus
24 | | their respective settings. Several examples have been configured for
25 | | you and you are free to add your own as your application requires.
26 | |
27 | | Laravel supports a variety of mail "transport" drivers to be used while
28 | | sending an e-mail. You will specify which one you are using for your
29 | | mailers below. You are free to add additional mailers as required.
30 | |
31 | | Supported: "smtp", "sendmail", "mailgun", "ses",
32 | | "postmark", "log", "array", "failover"
33 | |
34 | */
35 |
36 | 'mailers' => [
37 | 'smtp' => [
38 | 'transport' => 'smtp',
39 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
40 | 'port' => env('MAIL_PORT', 587),
41 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'),
42 | 'username' => env('MAIL_USERNAME'),
43 | 'password' => env('MAIL_PASSWORD'),
44 | 'timeout' => null,
45 | 'local_domain' => env('MAIL_EHLO_DOMAIN'),
46 | ],
47 |
48 | 'ses' => [
49 | 'transport' => 'ses',
50 | ],
51 |
52 | 'mailgun' => [
53 | 'transport' => 'mailgun',
54 | ],
55 |
56 | 'postmark' => [
57 | 'transport' => 'postmark',
58 | ],
59 |
60 | 'sendmail' => [
61 | 'transport' => 'sendmail',
62 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
63 | ],
64 |
65 | 'log' => [
66 | 'transport' => 'log',
67 | 'channel' => env('MAIL_LOG_CHANNEL'),
68 | ],
69 |
70 | 'array' => [
71 | 'transport' => 'array',
72 | ],
73 |
74 | 'failover' => [
75 | 'transport' => 'failover',
76 | 'mailers' => [
77 | 'smtp',
78 | 'log',
79 | ],
80 | ],
81 | ],
82 |
83 | /*
84 | |--------------------------------------------------------------------------
85 | | Global "From" Address
86 | |--------------------------------------------------------------------------
87 | |
88 | | You may wish for all e-mails sent by your application to be sent from
89 | | the same address. Here, you may specify a name and address that is
90 | | used globally for all e-mails that are sent by your application.
91 | |
92 | */
93 |
94 | 'from' => [
95 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
96 | 'name' => env('MAIL_FROM_NAME', 'Example'),
97 | ],
98 |
99 | /*
100 | |--------------------------------------------------------------------------
101 | | Markdown Mail Settings
102 | |--------------------------------------------------------------------------
103 | |
104 | | If you are using Markdown based email rendering, you may configure your
105 | | theme and component paths here, allowing you to customize the design
106 | | of the emails. Or, you may simply stick with the Laravel defaults!
107 | |
108 | */
109 |
110 | 'markdown' => [
111 | 'theme' => 'default',
112 |
113 | 'paths' => [
114 | resource_path('views/vendor/mail'),
115 | ],
116 | ],
117 |
118 | ];
119 |
--------------------------------------------------------------------------------
/config/auth.php:
--------------------------------------------------------------------------------
1 | [
17 | 'guard' => 'web',
18 | 'passwords' => 'users',
19 | ],
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Authentication Guards
24 | |--------------------------------------------------------------------------
25 | |
26 | | Next, you may define every authentication guard for your application.
27 | | Of course, a great default configuration has been defined for you
28 | | here which uses session storage and the Eloquent user provider.
29 | |
30 | | All authentication drivers have a user provider. This defines how the
31 | | users are actually retrieved out of your database or other storage
32 | | mechanisms used by this application to persist your user's data.
33 | |
34 | | Supported: "session"
35 | |
36 | */
37 |
38 | 'guards' => [
39 | 'web' => [
40 | 'driver' => 'session',
41 | 'provider' => 'users',
42 | ],
43 | ],
44 |
45 | /*
46 | |--------------------------------------------------------------------------
47 | | User Providers
48 | |--------------------------------------------------------------------------
49 | |
50 | | All authentication drivers have a user provider. This defines how the
51 | | users are actually retrieved out of your database or other storage
52 | | mechanisms used by this application to persist your user's data.
53 | |
54 | | If you have multiple user tables or models you may configure multiple
55 | | sources which represent each model / table. These sources may then
56 | | be assigned to any extra authentication guards you have defined.
57 | |
58 | | Supported: "database", "eloquent"
59 | |
60 | */
61 |
62 | 'providers' => [
63 | 'users' => [
64 | 'driver' => 'eloquent',
65 | 'model' => App\Models\User::class,
66 | ],
67 |
68 | // 'users' => [
69 | // 'driver' => 'database',
70 | // 'table' => 'users',
71 | // ],
72 | ],
73 |
74 | /*
75 | |--------------------------------------------------------------------------
76 | | Resetting Passwords
77 | |--------------------------------------------------------------------------
78 | |
79 | | You may specify multiple password reset configurations if you have more
80 | | than one user table or model in the application and you want to have
81 | | separate password reset settings based on the specific user types.
82 | |
83 | | The expire time is the number of minutes that each reset token will be
84 | | considered valid. This security feature keeps tokens short-lived so
85 | | they have less time to be guessed. You may change this as needed.
86 | |
87 | */
88 |
89 | 'passwords' => [
90 | 'users' => [
91 | 'provider' => 'users',
92 | 'table' => 'password_resets',
93 | 'expire' => 60,
94 | 'throttle' => 60,
95 | ],
96 | ],
97 |
98 | /*
99 | |--------------------------------------------------------------------------
100 | | Password Confirmation Timeout
101 | |--------------------------------------------------------------------------
102 | |
103 | | Here you may define the amount of seconds before a password confirmation
104 | | times out and the user is prompted to re-enter their password via the
105 | | confirmation screen. By default, the timeout lasts for three hours.
106 | |
107 | */
108 |
109 | 'password_timeout' => 10800,
110 |
111 | ];
112 |
--------------------------------------------------------------------------------
/app/Filament/Resources/CategoryResource.php:
--------------------------------------------------------------------------------
1 | schema([
35 | Card::make()
36 | ->schema([
37 | TextInput::make('name')
38 | ->label('Tên')
39 | ->required()
40 | ->reactive()
41 | ->afterStateUpdated(fn (string $state, callable $set) => $set('slug', Str::slug($state))),
42 |
43 | TextInput::make('slug')
44 | ->disabled()
45 | ->required()
46 | ->unique(ignoreRecord: true),
47 |
48 | MarkdownEditor::make('description')
49 | ->label('Mô tả')
50 | ->columnSpan('full'),
51 |
52 | SpatieMediaLibraryFileUpload::make('image')
53 | ->label('Hình ảnh')
54 | ->columnSpan('full'),
55 |
56 | Placeholder::make('created_at')
57 | ->label('Tạo lúc')
58 | ->hiddenOn('create')
59 | ->content(fn (?Category $record): string => $record?->created_at?->diffForHumans() ?? '-'),
60 |
61 | Placeholder::make('updated_at')
62 | ->label('Cập nhật lúc')
63 | ->hiddenOn('create')
64 | ->content(fn (?Category $record): string => $record?->updated_at?->diffForHumans() ?? '-'),
65 | ])
66 | ->columns(),
67 | ]);
68 | }
69 |
70 | public static function table(Table $table): Table
71 | {
72 | return $table
73 | ->columns([
74 | SpatieMediaLibraryImageColumn::make('Hình ảnh'),
75 |
76 | TextColumn::make('name')
77 | ->label('Tên')
78 | ->description(fn (Category $record): string => Str::limit($record->description, 50))
79 | ->searchable()
80 | ->sortable(),
81 |
82 | TextColumn::make('created_at')
83 | ->label('Ngày tạo')
84 | ->date()
85 | ->sortable(),
86 | ]);
87 | }
88 |
89 | public static function getRelations(): array
90 | {
91 | return [
92 | CoursesRelationManager::class,
93 | PostsRelationManager::class,
94 | ];
95 | }
96 |
97 | public static function getPages(): array
98 | {
99 | return [
100 | 'index' => Pages\ListCategories::route('/'),
101 | 'create' => Pages\CreateCategory::route('/create'),
102 | 'edit' => Pages\EditCategory::route('/{record}/edit'),
103 | ];
104 | }
105 |
106 | public static function getGloballySearchableAttributes(): array
107 | {
108 | return ['name', 'slug'];
109 | }
110 | }
111 |
--------------------------------------------------------------------------------