17 | */
18 | public function definition()
19 | {
20 | return [
21 | 'name' => fake()->name(),
22 | 'email' => fake()->safeEmail(),
23 | 'email_verified_at' => now(),
24 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
25 | 'remember_token' => Str::random(10),
26 | ];
27 | }
28 |
29 | /**
30 | * Indicate that the model's email address should be unverified.
31 | *
32 | * @return static
33 | */
34 | public function unverified()
35 | {
36 | return $this->state(function (array $attributes) {
37 | return [
38 | 'email_verified_at' => null,
39 | ];
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->timestamp('email_verified_at')->nullable();
21 | $table->string('password');
22 | $table->rememberToken();
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('users');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_100000_create_password_resets_table.php:
--------------------------------------------------------------------------------
1 | string('email')->index();
18 | $table->string('token');
19 | $table->timestamp('created_at')->nullable();
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | *
26 | * @return void
27 | */
28 | public function down()
29 | {
30 | Schema::dropIfExists('password_resets');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/database/migrations/2019_08_19_000000_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('uuid')->unique();
19 | $table->text('connection');
20 | $table->text('queue');
21 | $table->longText('payload');
22 | $table->longText('exception');
23 | $table->timestamp('failed_at')->useCurrent();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('failed_jobs');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->morphs('tokenable');
19 | $table->string('name');
20 | $table->string('token', 64)->unique();
21 | $table->text('abilities')->nullable();
22 | $table->timestamp('last_used_at')->nullable();
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('personal_access_tokens');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/database/migrations/2022_07_10_074501_create_news_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('title');
19 | $table->string('description');
20 | $table->string('category');
21 | $table->string('author');
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | *
29 | * @return void
30 | */
31 | public function down()
32 | {
33 | Schema::dropIfExists('news');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/database/seeders/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | create();
18 |
19 | // \App\Models\User::factory()->create([
20 | // 'name' => 'Test User',
21 | // 'email' => 'test@example.com',
22 | // ]);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/database/seeders/NewsSeeder.php:
--------------------------------------------------------------------------------
1 | count(50)->create();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["resources/js/*"]
6 | }
7 | },
8 | "exclude": ["node_modules", "public"]
9 | }
10 |
--------------------------------------------------------------------------------
/lang/en/auth.php:
--------------------------------------------------------------------------------
1 | 'These credentials do not match our records.',
17 | 'password' => 'The provided password is incorrect.',
18 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/lang/en/pagination.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 | 'next' => 'Next »',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/lang/en/passwords.php:
--------------------------------------------------------------------------------
1 | 'Your password has been reset!',
17 | 'sent' => 'We have emailed your password reset link!',
18 | 'throttled' => 'Please wait before retrying.',
19 | 'token' => 'This password reset token is invalid.',
20 | 'user' => "We can't find a user with that email address.",
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "vite",
5 | "build": "vite build"
6 | },
7 | "devDependencies": {
8 | "@headlessui/react": "^1.4.2",
9 | "@inertiajs/inertia": "^0.11.0",
10 | "@inertiajs/inertia-react": "^0.8.0",
11 | "@inertiajs/progress": "^0.2.6",
12 | "@tailwindcss/forms": "^0.5.2",
13 | "@vitejs/plugin-react": "^1.3.2",
14 | "alpinejs": "^3.4.2",
15 | "autoprefixer": "^10.4.2",
16 | "axios": "^0.25",
17 | "laravel-vite-plugin": "^0.2.1",
18 | "lodash": "^4.17.19",
19 | "postcss": "^8.4.6",
20 | "react": "^17.0.2",
21 | "react-dom": "^17.0.2",
22 | "tailwindcss": "^3.1.0",
23 | "vite": "^2.9.11"
24 | },
25 | "dependencies": {
26 | "avataaars": "^2.0.0",
27 | "daisyui": "^2.19.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests/Unit
10 |
11 |
12 | ./tests/Feature
13 |
14 |
15 |
16 |
17 | ./app
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Send Requests To Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/deaafrizal/laract9-crud-basic-portal-berita/f19ae66f739ba111ce1cd440d862890b06a144c5/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class);
50 |
51 | $response = $kernel->handle(
52 | $request = Request::capture()
53 | )->send();
54 |
55 | $kernel->terminate($request, $response);
56 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/resources/css/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/resources/js/Components/ApplicationLogo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function ApplicationLogo({ className }) {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/resources/js/Components/Button.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function Button({ type = 'submit', className = '', processing, children }) {
4 | return (
5 |
14 | {children}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/resources/js/Components/Checkbox.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function Checkbox({ name, value, handleChange }) {
4 | return (
5 | handleChange(e)}
11 | />
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/resources/js/Components/Dropdown.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, Fragment } from 'react';
2 | import { Link } from '@inertiajs/inertia-react';
3 | import { Transition } from '@headlessui/react';
4 |
5 | const DropDownContext = React.createContext();
6 |
7 | const Dropdown = ({ children }) => {
8 | const [open, setOpen] = useState(false);
9 |
10 | const toggleOpen = () => {
11 | setOpen((previousState) => !previousState);
12 | };
13 |
14 | return (
15 |
16 | {children}
17 |
18 | );
19 | };
20 |
21 | const Trigger = ({ children }) => {
22 | const { open, setOpen, toggleOpen } = useContext(DropDownContext);
23 |
24 | return (
25 | <>
26 | {children}
27 |
28 | {open && setOpen(false)}>
}
29 | >
30 | );
31 | };
32 |
33 | const Content = ({ align = 'right', width = '48', contentClasses = 'py-1 bg-white', children }) => {
34 | const { open, setOpen } = useContext(DropDownContext);
35 |
36 | let alignmentClasses = 'origin-top';
37 |
38 | if (align === 'left') {
39 | alignmentClasses = 'origin-top-left left-0';
40 | } else if (align === 'right') {
41 | alignmentClasses = 'origin-top-right right-0';
42 | }
43 |
44 | let widthClasses = '';
45 |
46 | if (width === '48') {
47 | widthClasses = 'w-48';
48 | }
49 |
50 | return (
51 | <>
52 |
62 | setOpen(false)}
65 | >
66 |
{children}
67 |
68 |
69 | >
70 | );
71 | };
72 |
73 | const DropdownLink = ({ href, method = 'post', as = 'a', children }) => {
74 | return (
75 |
81 | {children}
82 |
83 | );
84 | };
85 |
86 | Dropdown.Trigger = Trigger;
87 | Dropdown.Content = Content;
88 | Dropdown.Link = DropdownLink;
89 |
90 | export default Dropdown;
91 |
--------------------------------------------------------------------------------
/resources/js/Components/Homepage/NewsLists.jsx:
--------------------------------------------------------------------------------
1 | const isNews = (news) => {
2 | return news.map((data, i) => {
3 | return
4 |
5 |
6 |
7 |
8 |
9 | {data.title}
10 | NEW
11 |
12 |
{data.description}
13 |
14 |
{data.category}
15 |
{data.author}
16 |
17 |
18 |
19 | })
20 | }
21 |
22 | const noNews = () => {
23 | return (
24 | Saat ini belum ada berita tersedia
25 | )
26 | }
27 |
28 | const NewsLists = ({ news }) => {
29 | return !news ? noNews() : isNews(news)
30 | }
31 |
32 | export default NewsLists
--------------------------------------------------------------------------------
/resources/js/Components/Homepage/Paginator.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "@inertiajs/inertia-react";
2 |
3 | const Paginator = ({ meta }) => {
4 | const prev = meta.links[0].url;
5 | const next = meta.links[meta.links.length - 1].url;
6 | const current = meta.current_page;
7 |
8 | return (
9 |
10 | {prev && «}
11 | {current}
12 | {next && »}
13 |
14 | )
15 | }
16 |
17 | export default Paginator
--------------------------------------------------------------------------------
/resources/js/Components/Input.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | export default function Input({
4 | type = 'text',
5 | name,
6 | value,
7 | className,
8 | autoComplete,
9 | required,
10 | isFocused,
11 | handleChange,
12 | }) {
13 | const input = useRef();
14 |
15 | useEffect(() => {
16 | if (isFocused) {
17 | input.current.focus();
18 | }
19 | }, []);
20 |
21 | return (
22 |
23 | handleChange(e)}
35 | />
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/resources/js/Components/Label.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function Label({ forInput, value, className, children }) {
4 | return (
5 |
6 | {value ? value : children}
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/resources/js/Components/NavLink.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@inertiajs/inertia-react';
3 |
4 | export default function NavLink({ href, active, children }) {
5 | return (
6 |
14 | {children}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/resources/js/Components/Navbar.jsx:
--------------------------------------------------------------------------------
1 |
2 | import { Link } from "@inertiajs/inertia-react"
3 | import Avatar from "avataaars"
4 |
5 | const Navbar = ({ user }) => {
6 | return (
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
33 |
34 |
35 | {!user ?
36 | <>
37 | Login
38 | Register
39 | >
40 | :
41 | <>
42 |
43 |
44 | Dashboard
45 | New
46 |
47 |
48 | Settings
49 | Logout
50 | >
51 | }
52 |
53 |
54 |
55 |
56 | )
57 | }
58 |
59 | export default Navbar
--------------------------------------------------------------------------------
/resources/js/Components/ResponsiveNavLink.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from '@inertiajs/inertia-react';
3 |
4 | export default function ResponsiveNavLink({ method = 'get', as = 'a', href, active = false, children }) {
5 | return (
6 |
16 | {children}
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/resources/js/Components/ValidationErrors.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function ValidationErrors({ errors }) {
4 | return (
5 | Object.keys(errors).length > 0 && (
6 |
7 |
Whoops! Something went wrong.
8 |
9 |
10 | {Object.keys(errors).map(function (key, index) {
11 | return {errors[key]} ;
12 | })}
13 |
14 |
15 | )
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/resources/js/Layouts/Authenticated.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ApplicationLogo from '@/Components/ApplicationLogo';
3 | import Dropdown from '@/Components/Dropdown';
4 | import NavLink from '@/Components/NavLink';
5 | import ResponsiveNavLink from '@/Components/ResponsiveNavLink';
6 | import { Link } from '@inertiajs/inertia-react';
7 |
8 | export default function Authenticated({ auth, header, children }) {
9 | const [showingNavigationDropdown, setShowingNavigationDropdown] = useState(false);
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
22 |
23 |
24 |
25 | Dashboard
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
39 | {auth.user.name}
40 |
41 |
47 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Log Out
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
setShowingNavigationDropdown((previousState) => !previousState)}
69 | className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out"
70 | >
71 |
72 |
79 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Dashboard
96 |
97 |
98 |
99 |
100 |
101 |
{auth.user.name}
102 |
{auth.user.email}
103 |
104 |
105 |
106 |
107 | Log Out
108 |
109 |
110 |
111 |
112 |
113 |
114 | {header && (
115 |
118 | )}
119 |
120 |
{children}
121 |
122 | );
123 | }
124 |
--------------------------------------------------------------------------------
/resources/js/Layouts/Guest.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ApplicationLogo from '@/Components/ApplicationLogo';
3 | import { Link } from '@inertiajs/inertia-react';
4 |
5 | export default function Guest({ children }) {
6 | return (
7 |
8 |
13 |
14 |
15 | {children}
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/ConfirmPassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import Button from '@/Components/Button';
3 | import Guest from '@/Layouts/Guest';
4 | import Input from '@/Components/Input';
5 | import Label from '@/Components/Label';
6 | import ValidationErrors from '@/Components/ValidationErrors';
7 | import { Head, useForm } from '@inertiajs/inertia-react';
8 |
9 | export default function ConfirmPassword() {
10 | const { data, setData, post, processing, errors, reset } = useForm({
11 | password: '',
12 | });
13 |
14 | useEffect(() => {
15 | return () => {
16 | reset('password');
17 | };
18 | }, []);
19 |
20 | const onHandleChange = (event) => {
21 | setData(event.target.name, event.target.value);
22 | };
23 |
24 | const submit = (e) => {
25 | e.preventDefault();
26 |
27 | post(route('password.confirm'));
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 |
35 | This is a secure area of the application. Please confirm your password before continuing.
36 |
37 |
38 |
39 |
40 |
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/ForgotPassword.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@/Components/Button';
3 | import Guest from '@/Layouts/Guest';
4 | import Input from '@/Components/Input';
5 | import ValidationErrors from '@/Components/ValidationErrors';
6 | import { Head, useForm } from '@inertiajs/inertia-react';
7 |
8 | export default function ForgotPassword({ status }) {
9 | const { data, setData, post, processing, errors } = useForm({
10 | email: '',
11 | });
12 |
13 | const onHandleChange = (event) => {
14 | setData(event.target.name, event.target.value);
15 | };
16 |
17 | const submit = (e) => {
18 | e.preventDefault();
19 |
20 | post(route('password.email'));
21 | };
22 |
23 | return (
24 |
25 |
26 |
27 |
28 | Forgot your password? No problem. Just let us know your email address and we will email you a password
29 | reset link that will allow you to choose a new one.
30 |
31 |
32 | {status && {status}
}
33 |
34 |
35 |
36 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import Button from '@/Components/Button';
3 | import Checkbox from '@/Components/Checkbox';
4 | import Guest from '@/Layouts/Guest';
5 | import Input from '@/Components/Input';
6 | import Label from '@/Components/Label';
7 | import ValidationErrors from '@/Components/ValidationErrors';
8 | import { Head, Link, useForm } from '@inertiajs/inertia-react';
9 |
10 | export default function Login({ status, canResetPassword }) {
11 | const { data, setData, post, processing, errors, reset } = useForm({
12 | email: '',
13 | password: '',
14 | remember: '',
15 | });
16 |
17 | useEffect(() => {
18 | return () => {
19 | reset('password');
20 | };
21 | }, []);
22 |
23 | const onHandleChange = (event) => {
24 | setData(event.target.name, event.target.type === 'checkbox' ? event.target.checked : event.target.value);
25 | };
26 |
27 | const submit = (e) => {
28 | e.preventDefault();
29 |
30 | post(route('login'));
31 | };
32 |
33 | return (
34 |
35 |
36 |
37 | {status && {status}
}
38 |
39 |
40 |
41 |
92 |
93 | );
94 | }
95 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/Register.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import Button from '@/Components/Button';
3 | import Guest from '@/Layouts/Guest';
4 | import Input from '@/Components/Input';
5 | import Label from '@/Components/Label';
6 | import ValidationErrors from '@/Components/ValidationErrors';
7 | import { Head, Link, useForm } from '@inertiajs/inertia-react';
8 |
9 | export default function Register() {
10 | const { data, setData, post, processing, errors, reset } = useForm({
11 | name: '',
12 | email: '',
13 | password: '',
14 | password_confirmation: '',
15 | });
16 |
17 | useEffect(() => {
18 | return () => {
19 | reset('password', 'password_confirmation');
20 | };
21 | }, []);
22 |
23 | const onHandleChange = (event) => {
24 | setData(event.target.name, event.target.type === 'checkbox' ? event.target.checked : event.target.value);
25 | };
26 |
27 | const submit = (e) => {
28 | e.preventDefault();
29 |
30 | post(route('register'));
31 | };
32 |
33 | return (
34 |
35 |
36 |
37 |
38 |
39 |
106 |
107 | );
108 | }
109 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/ResetPassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import Button from '@/Components/Button';
3 | import Guest from '@/Layouts/Guest';
4 | import Input from '@/Components/Input';
5 | import Label from '@/Components/Label';
6 | import ValidationErrors from '@/Components/ValidationErrors';
7 | import { Head, useForm } from '@inertiajs/inertia-react';
8 |
9 | export default function ResetPassword({ token, email }) {
10 | const { data, setData, post, processing, errors, reset } = useForm({
11 | token: token,
12 | email: email,
13 | password: '',
14 | password_confirmation: '',
15 | });
16 |
17 | useEffect(() => {
18 | return () => {
19 | reset('password', 'password_confirmation');
20 | };
21 | }, []);
22 |
23 | const onHandleChange = (event) => {
24 | setData(event.target.name, event.target.value);
25 | };
26 |
27 | const submit = (e) => {
28 | e.preventDefault();
29 |
30 | post(route('password.update'));
31 | };
32 |
33 | return (
34 |
35 |
36 |
37 |
38 |
39 |
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/resources/js/Pages/Auth/VerifyEmail.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@/Components/Button';
3 | import Guest from '@/Layouts/Guest';
4 | import { Head, Link, useForm } from '@inertiajs/inertia-react';
5 |
6 | export default function VerifyEmail({ status }) {
7 | const { post, processing } = useForm();
8 |
9 | const submit = (e) => {
10 | e.preventDefault();
11 |
12 | post(route('verification.send'));
13 | };
14 |
15 | return (
16 |
17 |
18 |
19 |
20 | Thanks for signing up! Before getting started, could you verify your email address by clicking on the
21 | link we just emailed to you? If you didn't receive the email, we will gladly send you another.
22 |
23 |
24 | {status === 'verification-link-sent' && (
25 |
26 | A new verification link has been sent to the email address you provided during registration.
27 |
28 | )}
29 |
30 |
44 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/resources/js/Pages/Dashboard.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import Authenticated from '@/Layouts/Authenticated';
3 | import { Link, Head } from '@inertiajs/inertia-react';
4 | import { Inertia } from '@inertiajs/inertia';
5 |
6 | export default function Dashboard(props) {
7 | const [title, setTitle] = useState('');
8 | const [description, setDescription] = useState('');
9 | const [category, setCategory] = useState('');
10 | const [isNotif, setIsNotif] = useState(false)
11 |
12 | const handleSubmit = () => {
13 | const data = {
14 | title, description, category
15 | }
16 | Inertia.post('/news', data)
17 | setIsNotif(true)
18 | setTitle('')
19 | setDescription('')
20 | setCategory('')
21 | }
22 |
23 | useEffect(() => {
24 | if (!props.myNews) {
25 | Inertia.get('/news')
26 | }
27 | return;
28 | }, [])
29 |
30 | return (
31 | Berita Saya}
35 | >
36 |
37 |
38 |
39 |
40 |
41 | {isNotif &&
42 |
43 |
44 |
{props.flash.message}
45 |
46 |
47 | }
48 |
setTitle(title.target.value)} value={title} />
49 |
setDescription(description.target.value)} value={description} />
50 |
setCategory(category.target.value)} value={category} />
51 |
handleSubmit()}>SUBMIT
52 |
53 |
54 |
55 | {props.myNews && props.myNews.length > 0 ? props.myNews.map((news, i) => {
56 | return (
57 |
58 |
59 |
60 | {news.title}
61 | NEW
62 |
63 |
{news.description}
64 |
65 |
{news.category}
66 |
67 |
68 | edit
69 |
70 |
71 |
72 |
73 | delete
74 |
75 |
76 |
77 |
78 |
79 | )
80 | }) :
Anda belum memiliki berita
}
81 |
82 |
83 |
84 | );
85 | }
86 |
--------------------------------------------------------------------------------
/resources/js/Pages/EditNews.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Head } from '@inertiajs/inertia-react';
3 | import Navbar from '@/Components/Navbar';
4 | import { Inertia } from '@inertiajs/inertia';
5 |
6 | export default function EditNews(props) {
7 | const [title, setTitle] = useState('');
8 | const [description, setDescription] = useState('');
9 | const [category, setCategory] = useState('');
10 |
11 | const handleSubmit = () => {
12 | const data = {
13 | id: props.myNews.id, title, description, category
14 | }
15 | Inertia.post('/news/update', data)
16 | setTitle('')
17 | setDescription('')
18 | setCategory('')
19 | }
20 |
21 | return (
22 |
23 |
24 |
25 |
34 |
35 | )
36 | }
--------------------------------------------------------------------------------
/resources/js/Pages/Homepage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Head } from '@inertiajs/inertia-react';
3 | import Navbar from '@/Components/Navbar';
4 | import NewsLists from '@/Components/Homepage/NewsLists';
5 | import Paginator from '@/Components/Homepage/Paginator';
6 |
7 | export default function Homepage(props) {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 | )
20 | }
--------------------------------------------------------------------------------
/resources/js/app.jsx:
--------------------------------------------------------------------------------
1 | import './bootstrap';
2 | import '../css/app.css';
3 |
4 | import React from 'react';
5 | import { render } from 'react-dom';
6 | import { createInertiaApp } from '@inertiajs/inertia-react';
7 | import { InertiaProgress } from '@inertiajs/progress';
8 | import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
9 |
10 | const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
11 |
12 | createInertiaApp({
13 | title: (title) => `${title} - ${appName}`,
14 | resolve: (name) => resolvePageComponent(`./Pages/${name}.jsx`, import.meta.glob('./Pages/**/*.jsx')),
15 | setup({ el, App, props }) {
16 | return render( , el);
17 | },
18 | });
19 |
20 | InertiaProgress.init({ color: '#4B5563' });
21 |
--------------------------------------------------------------------------------
/resources/js/bootstrap.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | window._ = _;
3 |
4 | /**
5 | * We'll load the axios HTTP library which allows us to easily issue requests
6 | * to our Laravel back-end. This library automatically handles sending the
7 | * CSRF token as a header based on the value of the "XSRF" token cookie.
8 | */
9 |
10 | import axios from 'axios';
11 | window.axios = axios;
12 |
13 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
14 |
15 | /**
16 | * Echo exposes an expressive API for subscribing to channels and listening
17 | * for events that are broadcast by Laravel. Echo and event broadcasting
18 | * allows your team to easily build robust real-time web applications.
19 | */
20 |
21 | // import Echo from 'laravel-echo';
22 |
23 | // import Pusher from 'pusher-js';
24 | // window.Pusher = Pusher;
25 |
26 | // window.Echo = new Echo({
27 | // broadcaster: 'pusher',
28 | // key: import.meta.env.VITE_PUSHER_APP_KEY,
29 | // wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_CLUSTER}.pusher.com`,
30 | // wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
31 | // wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
32 | // forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
33 | // enabledTransports: ['ws', 'wss'],
34 | // });
35 |
--------------------------------------------------------------------------------
/resources/views/app.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ config('app.name', 'Laravel') }}
8 |
9 |
10 |
11 |
12 |
13 | @routes
14 | @viteReactRefresh
15 | @vite('resources/js/app.jsx')
16 | @inertiaHead
17 |
18 |
19 | @inertia
20 |
21 |
22 |
--------------------------------------------------------------------------------
/resources/views/auth/confirm-password.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ __('This is a secure area of the application. Please confirm your password before continuing.') }}
11 |
12 |
13 |
14 |
15 |
16 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/resources/views/auth/forgot-password.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/resources/views/auth/login.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/resources/views/auth/register.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/resources/views/auth/reset-password.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/resources/views/auth/verify-email.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
11 |
12 |
13 | @if (session('status') == 'verification-link-sent')
14 |
15 | {{ __('A new verification link has been sent to the email address you provided during registration.') }}
16 |
17 | @endif
18 |
19 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/resources/views/components/application-logo.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/resources/views/components/auth-card.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ $logo }}
4 |
5 |
6 |
7 | {{ $slot }}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/resources/views/components/auth-session-status.blade.php:
--------------------------------------------------------------------------------
1 | @props(['status'])
2 |
3 | @if ($status)
4 | merge(['class' => 'font-medium text-sm text-green-600']) }}>
5 | {{ $status }}
6 |
7 | @endif
8 |
--------------------------------------------------------------------------------
/resources/views/components/auth-validation-errors.blade.php:
--------------------------------------------------------------------------------
1 | @props(['errors'])
2 |
3 | @if ($errors->any())
4 |
5 |
6 | {{ __('Whoops! Something went wrong.') }}
7 |
8 |
9 |
10 | @foreach ($errors->all() as $error)
11 | {{ $error }}
12 | @endforeach
13 |
14 |
15 | @endif
16 |
--------------------------------------------------------------------------------
/resources/views/components/button.blade.php:
--------------------------------------------------------------------------------
1 | merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150']) }}>
2 | {{ $slot }}
3 |
4 |
--------------------------------------------------------------------------------
/resources/views/components/dropdown-link.blade.php:
--------------------------------------------------------------------------------
1 | merge(['class' => 'block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}
2 |
--------------------------------------------------------------------------------
/resources/views/components/dropdown.blade.php:
--------------------------------------------------------------------------------
1 | @props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white'])
2 |
3 | @php
4 | switch ($align) {
5 | case 'left':
6 | $alignmentClasses = 'origin-top-left left-0';
7 | break;
8 | case 'top':
9 | $alignmentClasses = 'origin-top';
10 | break;
11 | case 'right':
12 | default:
13 | $alignmentClasses = 'origin-top-right right-0';
14 | break;
15 | }
16 |
17 | switch ($width) {
18 | case '48':
19 | $width = 'w-48';
20 | break;
21 | }
22 | @endphp
23 |
24 |
25 |
26 | {{ $trigger }}
27 |
28 |
29 |
39 |
40 | {{ $content }}
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/resources/views/components/input.blade.php:
--------------------------------------------------------------------------------
1 | @props(['disabled' => false])
2 |
3 | merge(['class' => 'rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50']) !!}>
4 |
--------------------------------------------------------------------------------
/resources/views/components/label.blade.php:
--------------------------------------------------------------------------------
1 | @props(['value'])
2 |
3 | merge(['class' => 'block font-medium text-sm text-gray-700']) }}>
4 | {{ $value ?? $slot }}
5 |
6 |
--------------------------------------------------------------------------------
/resources/views/components/nav-link.blade.php:
--------------------------------------------------------------------------------
1 | @props(['active'])
2 |
3 | @php
4 | $classes = ($active ?? false)
5 | ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
6 | : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out';
7 | @endphp
8 |
9 | merge(['class' => $classes]) }}>
10 | {{ $slot }}
11 |
12 |
--------------------------------------------------------------------------------
/resources/views/components/responsive-nav-link.blade.php:
--------------------------------------------------------------------------------
1 | @props(['active'])
2 |
3 | @php
4 | $classes = ($active ?? false)
5 | ? 'block pl-3 pr-4 py-2 border-l-4 border-indigo-400 text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
6 | : 'block pl-3 pr-4 py-2 border-l-4 border-transparent text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out';
7 | @endphp
8 |
9 | merge(['class' => $classes]) }}>
10 | {{ $slot }}
11 |
12 |
--------------------------------------------------------------------------------
/resources/views/dashboard.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ __('Dashboard') }}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | You're logged in!
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resources/views/layouts/app.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ config('app.name', 'Laravel') }}
9 |
10 |
11 |
12 |
13 |
14 | @vite(['resources/css/app.css', 'resources/js/app.js'])
15 |
16 |
17 |
18 | @include('layouts.navigation')
19 |
20 |
21 |
22 |
23 | {{ $header }}
24 |
25 |
26 |
27 |
28 |
29 | {{ $slot }}
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/resources/views/layouts/guest.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ config('app.name', 'Laravel') }}
9 |
10 |
11 |
12 |
13 |
14 | @vite(['resources/css/app.css', 'resources/js/app.js'])
15 |
16 |
17 |
18 | {{ $slot }}
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/resources/views/layouts/navigation.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
15 |
16 | {{ __('Dashboard') }}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{ Auth::user()->name }}
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | {{ __('Dashboard') }}
68 |
69 |
70 |
71 |
72 |
73 |
74 |
{{ Auth::user()->name }}
75 |
{{ Auth::user()->email }}
76 |
77 |
78 |
79 |
80 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | get('/user', function (Request $request) {
18 | return $request->user();
19 | });
20 |
--------------------------------------------------------------------------------
/routes/auth.php:
--------------------------------------------------------------------------------
1 | group(function () {
14 | Route::get('register', [RegisteredUserController::class, 'create'])
15 | ->name('register');
16 |
17 | Route::post('register', [RegisteredUserController::class, 'store']);
18 |
19 | Route::get('login', [AuthenticatedSessionController::class, 'create'])
20 | ->name('login');
21 |
22 | Route::post('login', [AuthenticatedSessionController::class, 'store']);
23 |
24 | Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
25 | ->name('password.request');
26 |
27 | Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])
28 | ->name('password.email');
29 |
30 | Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])
31 | ->name('password.reset');
32 |
33 | Route::post('reset-password', [NewPasswordController::class, 'store'])
34 | ->name('password.update');
35 | });
36 |
37 | Route::middleware('auth')->group(function () {
38 | Route::get('verify-email', [EmailVerificationPromptController::class, '__invoke'])
39 | ->name('verification.notice');
40 |
41 | Route::get('verify-email/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
42 | ->middleware(['signed', 'throttle:6,1'])
43 | ->name('verification.verify');
44 |
45 | Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
46 | ->middleware('throttle:6,1')
47 | ->name('verification.send');
48 |
49 | Route::get('confirm-password', [ConfirmablePasswordController::class, 'show'])
50 | ->name('password.confirm');
51 |
52 | Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']);
53 |
54 | Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
55 | ->name('logout');
56 | });
57 |
--------------------------------------------------------------------------------
/routes/channels.php:
--------------------------------------------------------------------------------
1 | id === (int) $id;
18 | });
19 |
--------------------------------------------------------------------------------
/routes/console.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
19 | })->purpose('Display an inspiring quote');
20 |
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 | middleware(['auth', 'verified'])->name('create.news');
21 | Route::get('/news', [NewsController::class, 'show'])->middleware(['auth', 'verified'])->name('my.news');
22 | Route::get('/news/edit', [NewsController::class, 'edit'])->middleware(['auth', 'verified'])->name('edit.news');
23 | Route::post('/news/update', [NewsController::class, 'update'])->middleware(['auth', 'verified'])->name('update.news');
24 | Route::post('/news/delete', [NewsController::class, 'destroy'])->middleware(['auth', 'verified'])->name('delete.news');
25 |
26 | Route::get('/dashboard', function () {
27 | return Inertia::render('Dashboard');
28 | })->middleware(['auth', 'verified'])->name('dashboard');
29 |
30 | require __DIR__ . '/auth.php';
31 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/.gitignore:
--------------------------------------------------------------------------------
1 | compiled.php
2 | config.php
3 | down
4 | events.scanned.php
5 | maintenance.php
6 | routes.php
7 | routes.scanned.php
8 | schedule-*
9 | services.json
10 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const defaultTheme = require('tailwindcss/defaultTheme');
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | module.exports = {
5 | content: [
6 | './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
7 | './storage/framework/views/*.php',
8 | './resources/views/**/*.blade.php',
9 | './resources/js/**/*.jsx',
10 | ],
11 |
12 | theme: {
13 | extend: {
14 | fontFamily: {
15 | sans: ['Nunito', ...defaultTheme.fontFamily.sans],
16 | },
17 | },
18 | },
19 |
20 | plugins: [require('@tailwindcss/forms'), require('daisyui')],
21 | };
22 |
--------------------------------------------------------------------------------
/tests/CreatesApplication.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class)->bootstrap();
19 |
20 | return $app;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/AuthenticationTest.php:
--------------------------------------------------------------------------------
1 | get('/login');
17 |
18 | $response->assertStatus(200);
19 | }
20 |
21 | public function test_users_can_authenticate_using_the_login_screen()
22 | {
23 | $user = User::factory()->create();
24 |
25 | $response = $this->post('/login', [
26 | 'email' => $user->email,
27 | 'password' => 'password',
28 | ]);
29 |
30 | $this->assertAuthenticated();
31 | $response->assertRedirect(RouteServiceProvider::HOME);
32 | }
33 |
34 | public function test_users_can_not_authenticate_with_invalid_password()
35 | {
36 | $user = User::factory()->create();
37 |
38 | $this->post('/login', [
39 | 'email' => $user->email,
40 | 'password' => 'wrong-password',
41 | ]);
42 |
43 | $this->assertGuest();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/EmailVerificationTest.php:
--------------------------------------------------------------------------------
1 | create([
20 | 'email_verified_at' => null,
21 | ]);
22 |
23 | $response = $this->actingAs($user)->get('/verify-email');
24 |
25 | $response->assertStatus(200);
26 | }
27 |
28 | public function test_email_can_be_verified()
29 | {
30 | $user = User::factory()->create([
31 | 'email_verified_at' => null,
32 | ]);
33 |
34 | Event::fake();
35 |
36 | $verificationUrl = URL::temporarySignedRoute(
37 | 'verification.verify',
38 | now()->addMinutes(60),
39 | ['id' => $user->id, 'hash' => sha1($user->email)]
40 | );
41 |
42 | $response = $this->actingAs($user)->get($verificationUrl);
43 |
44 | Event::assertDispatched(Verified::class);
45 | $this->assertTrue($user->fresh()->hasVerifiedEmail());
46 | $response->assertRedirect(RouteServiceProvider::HOME.'?verified=1');
47 | }
48 |
49 | public function test_email_is_not_verified_with_invalid_hash()
50 | {
51 | $user = User::factory()->create([
52 | 'email_verified_at' => null,
53 | ]);
54 |
55 | $verificationUrl = URL::temporarySignedRoute(
56 | 'verification.verify',
57 | now()->addMinutes(60),
58 | ['id' => $user->id, 'hash' => sha1('wrong-email')]
59 | );
60 |
61 | $this->actingAs($user)->get($verificationUrl);
62 |
63 | $this->assertFalse($user->fresh()->hasVerifiedEmail());
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/PasswordConfirmationTest.php:
--------------------------------------------------------------------------------
1 | create();
16 |
17 | $response = $this->actingAs($user)->get('/confirm-password');
18 |
19 | $response->assertStatus(200);
20 | }
21 |
22 | public function test_password_can_be_confirmed()
23 | {
24 | $user = User::factory()->create();
25 |
26 | $response = $this->actingAs($user)->post('/confirm-password', [
27 | 'password' => 'password',
28 | ]);
29 |
30 | $response->assertRedirect();
31 | $response->assertSessionHasNoErrors();
32 | }
33 |
34 | public function test_password_is_not_confirmed_with_invalid_password()
35 | {
36 | $user = User::factory()->create();
37 |
38 | $response = $this->actingAs($user)->post('/confirm-password', [
39 | 'password' => 'wrong-password',
40 | ]);
41 |
42 | $response->assertSessionHasErrors();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/PasswordResetTest.php:
--------------------------------------------------------------------------------
1 | get('/forgot-password');
18 |
19 | $response->assertStatus(200);
20 | }
21 |
22 | public function test_reset_password_link_can_be_requested()
23 | {
24 | Notification::fake();
25 |
26 | $user = User::factory()->create();
27 |
28 | $this->post('/forgot-password', ['email' => $user->email]);
29 |
30 | Notification::assertSentTo($user, ResetPassword::class);
31 | }
32 |
33 | public function test_reset_password_screen_can_be_rendered()
34 | {
35 | Notification::fake();
36 |
37 | $user = User::factory()->create();
38 |
39 | $this->post('/forgot-password', ['email' => $user->email]);
40 |
41 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) {
42 | $response = $this->get('/reset-password/'.$notification->token);
43 |
44 | $response->assertStatus(200);
45 |
46 | return true;
47 | });
48 | }
49 |
50 | public function test_password_can_be_reset_with_valid_token()
51 | {
52 | Notification::fake();
53 |
54 | $user = User::factory()->create();
55 |
56 | $this->post('/forgot-password', ['email' => $user->email]);
57 |
58 | Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) {
59 | $response = $this->post('/reset-password', [
60 | 'token' => $notification->token,
61 | 'email' => $user->email,
62 | 'password' => 'password',
63 | 'password_confirmation' => 'password',
64 | ]);
65 |
66 | $response->assertSessionHasNoErrors();
67 |
68 | return true;
69 | });
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/RegistrationTest.php:
--------------------------------------------------------------------------------
1 | get('/register');
16 |
17 | $response->assertStatus(200);
18 | }
19 |
20 | public function test_new_users_can_register()
21 | {
22 | $response = $this->post('/register', [
23 | 'name' => 'Test User',
24 | 'email' => 'test@example.com',
25 | 'password' => 'password',
26 | 'password_confirmation' => 'password',
27 | ]);
28 |
29 | $this->assertAuthenticated();
30 | $response->assertRedirect(RouteServiceProvider::HOME);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Feature/ExampleTest.php:
--------------------------------------------------------------------------------
1 | get('/');
18 |
19 | $response->assertStatus(200);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import laravel from 'laravel-vite-plugin';
3 | import react from '@vitejs/plugin-react';
4 |
5 | export default defineConfig({
6 | plugins: [
7 | laravel({
8 | input: 'resources/js/app.jsx',
9 | }),
10 | react(),
11 | ],
12 | });
13 |
--------------------------------------------------------------------------------