diff --git a/TODO.md b/TODO.md index a383b78..42a22d4 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,7 @@ ### Admin - [x] CRUD Info -- [ ] CRUD Banner +- [x] CRUD Banner - [ ] CRUD Rekening / Account - [ ] CRUD Customer - [ ] CRUD Voucher diff --git a/app/Http/Controllers/BannerController.php b/app/Http/Controllers/BannerController.php new file mode 100644 index 0000000..04ba868 --- /dev/null +++ b/app/Http/Controllers/BannerController.php @@ -0,0 +1,83 @@ +paginate(); + + return inertia('Banner/Index', [ + 'query' => $query + ]); + } + + public function create() + { + return inertia('Banner/Form'); + } + + public function store(Request $request) + { + $request->validate([ + 'image' => 'required|image', + 'title' => 'required|string', + 'description' => 'required|string', + ]); + + $file = $request->file('image'); + $file->store('uploads', 'public'); + + Banner::create([ + 'image' => $file->hashName('uploads'), + 'title' => $request->title, + 'description' => $request->description, + ]); + + return redirect()->route('banner.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed saved']); + } + + public function edit(Banner $banner) + { + return inertia('Banner/Form', [ + 'banner' => $banner + ]); + } + + public function update(Request $request, Banner $banner) + { + $request->validate([ + 'image' => 'nullable|image', + 'title' => 'required|string', + 'description' => 'required|string', + ]); + + if ($request->hasFile('image')) { + $file = $request->file('image'); + $file->store('uploads', 'public'); + $banner->image = $file->hashName('uploads'); + } + + $banner->update([ + 'image' => $banner->image, + 'title' => $request->title, + 'description' => $request->description, + ]); + + return redirect()->route('banner.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed updated']); + } + + public function destroy(Banner $banner) + { + $banner->delete(); + + return redirect()->route('banner.index') + ->with('message', ['type' => 'success', 'message' => 'Item has beed deleted']); + } +} diff --git a/app/Http/Controllers/Customer/AuthController.php b/app/Http/Controllers/Customer/AuthController.php index 21c8261..507e671 100644 --- a/app/Http/Controllers/Customer/AuthController.php +++ b/app/Http/Controllers/Customer/AuthController.php @@ -73,7 +73,7 @@ class AuthController extends Controller 'fullname' => $user->name, 'name' => $user->nickname, 'email' => $user->email, - 'username' => Str::random(10), + 'username' => Str::slug($user->name . '_' . Str::random(5), '_'), 'google_id' => $user->id, 'google_oauth_response' => json_encode($user), 'customer_level_id' => $basic->id, diff --git a/app/Http/Controllers/Customer/HomeController.php b/app/Http/Controllers/Customer/HomeController.php index 10756c3..2c60801 100644 --- a/app/Http/Controllers/Customer/HomeController.php +++ b/app/Http/Controllers/Customer/HomeController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Customer; use App\Http\Controllers\Controller; +use App\Models\Banner; use App\Models\Info; class HomeController extends Controller @@ -10,9 +11,18 @@ class HomeController extends Controller public function index() { $infos = Info::where('is_publish', 1)->orderBy('updated_at', 'desc')->get(); + $banners = Banner::orderBy('updated_at', 'desc')->get(); return inertia('Home/Index/Index', [ 'infos' => $infos, + 'banners' => $banners + ]); + } + + public function banner(Banner $banner) + { + return inertia('Home/Index/Banner', [ + 'banner' => $banner, ]); } } diff --git a/app/Http/Controllers/GeneralController.php b/app/Http/Controllers/GeneralController.php index 0559b8b..5e8209c 100644 --- a/app/Http/Controllers/GeneralController.php +++ b/app/Http/Controllers/GeneralController.php @@ -2,6 +2,9 @@ namespace App\Http\Controllers; +use Illuminate\Http\Request; +use Illuminate\Support\Str; + class GeneralController extends Controller { public function index() @@ -13,4 +16,18 @@ class GeneralController extends Controller { return inertia('Maintance'); } + + public function upload(Request $request) + { + $request->validate(['image' => 'required|file']); + $file = $request->file('image'); + $file->store('uploads', 'public'); + + + return response()->json([ + 'id' => Str::ulid(), + 'name' => $file->getClientOriginalName(), + 'url' => asset($file->hashName('uploads')), + ]); + } } diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 6d5b532..592894d 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -37,6 +37,7 @@ class HandleInertiaRequests extends Middleware 'message' => fn () => $request->session()->get('message'), ], 'app_name' => env('APP_NAME', 'App Name'), + 'csrf_token' => csrf_token(), ]); } } diff --git a/app/Models/Banner.php b/app/Models/Banner.php index e643a9f..b895462 100644 --- a/app/Models/Banner.php +++ b/app/Models/Banner.php @@ -2,6 +2,8 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Casts\Attribute; + class Banner extends Model { protected $fillable = [ @@ -11,4 +13,15 @@ class Banner extends Model 'destination', 'type', ]; + + protected $appends = [ + 'image_url' + ]; + + protected function imageUrl(): Attribute + { + return Attribute::make(get: function () { + return asset($this->image); + }); + } } diff --git a/app/Models/Customer.php b/app/Models/Customer.php index bf70725..f896616 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -41,6 +41,7 @@ class Customer extends Authenticatable 'image_url', 'display_deposit', 'display_coin', + 'display_phone' ]; public function imageUrl(): Attribute @@ -62,6 +63,16 @@ class Customer extends Authenticatable ); } + public function displayPhone(): Attribute + { + return Attribute::make(get: function () { + if ($this->phone === null) { + return ' - '; + } + return '+62' . $this->phone; + }); + } + public function displayDeposit(): Attribute { return Attribute::make(get: function () { diff --git a/database/migrations/2023_05_24_130511_create_banners_table.php b/database/migrations/2023_05_24_130511_create_banners_table.php index bdedd5b..8a0aea7 100644 --- a/database/migrations/2023_05_24_130511_create_banners_table.php +++ b/database/migrations/2023_05_24_130511_create_banners_table.php @@ -16,7 +16,7 @@ return new class extends Migration $table->string('image'); $table->string('title'); - $table->string('description')->nullable(); + $table->text('description')->nullable(); $table->string('destination')->nullable(); $table->string('type')->nullable(); $table->smallInteger('is_publish')->nullable(); diff --git a/database/seeders/DummySeeder.php b/database/seeders/DummySeeder.php index 7e028ae..6df9eac 100644 --- a/database/seeders/DummySeeder.php +++ b/database/seeders/DummySeeder.php @@ -2,6 +2,7 @@ namespace Database\Seeders; +use App\Models\Banner; use App\Models\Info; use Illuminate\Database\Seeder; @@ -15,6 +16,7 @@ class DummySeeder extends Seeder public function run() { $this->info(); + $this->banner(); } public function info() @@ -24,4 +26,16 @@ class DummySeeder extends Seeder 'is_publish' => 1, ]); } + + public function banner() + { + $images = ['1.webp', '2.webp', '3.webp']; + foreach ($images as $index => $image) { + Banner::create([ + 'title' => 'Banner ' . $index, + 'image' => 'sample/' . $image, + 'description' => '

Banner

' + ]); + } + } } diff --git a/package-lock.json b/package-lock.json index b494748..9badb38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "@tinymce/tinymce-react": "^4.3.0", "flowbite": "^1.6.3", "flowbite-react": "^0.3.8", "is": "^3.3.0", @@ -15,7 +16,8 @@ "react-icons": "^4.7.1", "react-number-format": "^5.1.2", "react-toastify": "^9.1.1", - "react-use": "^17.4.0" + "react-use": "^17.4.0", + "tinymce": "^6.4.2" }, "devDependencies": { "@headlessui/react": "^1.4.2", @@ -930,6 +932,19 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" } }, + "node_modules/@tinymce/tinymce-react": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-4.3.0.tgz", + "integrity": "sha512-iB4cUsYfcJL4NGuKhqCGYuTmFTje3nPxyPv1HxprTsp/YMGuuiiSNWrv3zwI31QX5Cn8qeq9MrMDnbxuRugHyg==", + "dependencies": { + "prop-types": "^15.6.2", + "tinymce": "^6.3.1" + }, + "peerDependencies": { + "react": "^18.0.0 || ^17.0.1 || ^16.7.0", + "react-dom": "^18.0.0 || ^17.0.1 || ^16.7.0" + } + }, "node_modules/@types/js-cookie": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", @@ -2825,6 +2840,11 @@ "node": ">=10" } }, + "node_modules/tinymce": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.4.2.tgz", + "integrity": "sha512-te+4c8PoAwTKPMBQtMQehnSZlOO9Ay5tDgaRFJKBehYb6SlX2PYZ0v3oe/cmiv5EfqdwZLkEMXk2MNOeApZZLg==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", diff --git a/package.json b/package.json index 0a2f7eb..3f97a79 100644 --- a/package.json +++ b/package.json @@ -20,16 +20,18 @@ "vite": "^4.0.0" }, "dependencies": { + "@tinymce/tinymce-react": "^4.3.0", "flowbite": "^1.6.3", "flowbite-react": "^0.3.8", - "react-icons": "^4.7.1", "is": "^3.3.0", "moment": "^2.29.4", "nprogress": "^0.2.0", "qs": "^6.11.0", "react-datepicker": "^4.8.0", + "react-icons": "^4.7.1", "react-number-format": "^5.1.2", "react-toastify": "^9.1.1", - "react-use": "^17.4.0" + "react-use": "^17.4.0", + "tinymce": "^6.4.2" } -} \ No newline at end of file +} diff --git a/public/uploads/2FlN0wGNWeaxv9kFA9SOoh9JeTwRvUEiV5a8faRx.png b/public/uploads/2FlN0wGNWeaxv9kFA9SOoh9JeTwRvUEiV5a8faRx.png new file mode 100644 index 0000000..3635370 Binary files /dev/null and b/public/uploads/2FlN0wGNWeaxv9kFA9SOoh9JeTwRvUEiV5a8faRx.png differ diff --git a/public/uploads/5Cb26LsXe6x8o2C1NDeqPhpvK305Y05HmzWz6OlX.png b/public/uploads/5Cb26LsXe6x8o2C1NDeqPhpvK305Y05HmzWz6OlX.png new file mode 100644 index 0000000..b36f112 Binary files /dev/null and b/public/uploads/5Cb26LsXe6x8o2C1NDeqPhpvK305Y05HmzWz6OlX.png differ diff --git a/public/uploads/GXhu1GNCg4KpAgAVZvS1Bu4X24GRYhU6VBkWtFe9.png b/public/uploads/GXhu1GNCg4KpAgAVZvS1Bu4X24GRYhU6VBkWtFe9.png new file mode 100644 index 0000000..b36f112 Binary files /dev/null and b/public/uploads/GXhu1GNCg4KpAgAVZvS1Bu4X24GRYhU6VBkWtFe9.png differ diff --git a/public/uploads/kjFUmhXj0s0xAkhf4IRQT470njODmLgMlJyw0x37.png b/public/uploads/kjFUmhXj0s0xAkhf4IRQT470njODmLgMlJyw0x37.png new file mode 100644 index 0000000..80ff33a Binary files /dev/null and b/public/uploads/kjFUmhXj0s0xAkhf4IRQT470njODmLgMlJyw0x37.png differ diff --git a/resources/js/Components/TinyMCE.jsx b/resources/js/Components/TinyMCE.jsx new file mode 100644 index 0000000..c1eba5b1 --- /dev/null +++ b/resources/js/Components/TinyMCE.jsx @@ -0,0 +1,165 @@ +import { Editor } from '@tinymce/tinymce-react' + +// TinyMCE so the global var exists +// eslint-disable-next-line no-unused-vars +import tinymce from 'tinymce/tinymce' +// DOM model +import 'tinymce/models/dom/model' +// Theme +import 'tinymce/themes/silver' +// Toolbar icons +import 'tinymce/icons/default' +// Editor styles +import 'tinymce/skins/ui/oxide/skin.min.css' + +// importing the plugin js. +// if you use a plugin that is not listed here the editor will fail to load +import 'tinymce/plugins/advlist' +import 'tinymce/plugins/anchor' +import 'tinymce/plugins/autolink' +import 'tinymce/plugins/autoresize' +import 'tinymce/plugins/autosave' +import 'tinymce/plugins/charmap' +import 'tinymce/plugins/code' +import 'tinymce/plugins/codesample' +import 'tinymce/plugins/directionality' +import 'tinymce/plugins/emoticons' +import 'tinymce/plugins/fullscreen' +import 'tinymce/plugins/help' +import 'tinymce/plugins/image' +import 'tinymce/plugins/importcss' +import 'tinymce/plugins/insertdatetime' +import 'tinymce/plugins/link' +import 'tinymce/plugins/lists' +import 'tinymce/plugins/media' +import 'tinymce/plugins/nonbreaking' +import 'tinymce/plugins/pagebreak' +import 'tinymce/plugins/preview' +import 'tinymce/plugins/quickbars' +import 'tinymce/plugins/save' +import 'tinymce/plugins/searchreplace' +import 'tinymce/plugins/table' +import 'tinymce/plugins/template' +import 'tinymce/plugins/visualblocks' +import 'tinymce/plugins/visualchars' +import 'tinymce/plugins/wordcount' + +// importing plugin resources +import 'tinymce/plugins/emoticons/js/emojis' + +// Content styles, including inline UI like fake cursors +/* eslint import/no-webpack-loader-syntax: off */ +import contentCss from 'tinymce/skins/content/default/content.min.css?inline' +import contentUiCss from 'tinymce/skins/ui/oxide/content.min.css?inline' + +import generalCss from '../../css/app.css?inline' + +import { usePage } from '@inertiajs/react' + +export default function BundledEditor(props) { + const { init, ...rest } = props + const { csrf_token } = usePage().props + + const FilePickerCallback = async (callback, value, meta) => { + // Provide file and text for the link dialog + const input = document.createElement('input') + input.setAttribute('type', 'file') + input.setAttribute('accept', 'image/*,video/*') + + input.addEventListener('change', async (e) => { + const body = new FormData() + body.append('_token', csrf_token) + body.append('image', e.target.files[0]) + + await fetch(route('post.upload'), { + method: 'post', + body: body, + headers: { + 'accept-content': 'application/json', + 'X-CSSRF-TOKEN': csrf_token, + }, + credentials: 'include', + }) + .then((res) => res.json()) + .then((res) => { + callback(res.url, { alt: 'My alt text' }) + }) + .catch((err) => { + alert(err) + }) + }) + + input.click() + } + + const ImageUploadHandler = (blobInfo, progress) => + new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest() + xhr.withCredentials = true + xhr.open('POST', route('post.upload')) + + xhr.upload.onprogress = (e) => { + progress((e.loaded / e.total) * 100) + } + + xhr.onload = () => { + if (xhr.status === 403) { + reject({ + message: 'HTTP Error: ' + xhr.status, + remove: true, + }) + return + } + + if (xhr.status < 200 || xhr.status >= 300) { + reject('HTTP Error: ' + xhr.status) + return + } + + const json = JSON.parse(xhr.responseText) + + if (!json || typeof json.url != 'string') { + reject('Invalid JSON: ' + xhr.responseText) + return + } + + resolve(json.url) + } + + xhr.onerror = () => { + reject( + 'Image upload failed due to a XHR Transport error. Code: ' + + xhr.status + ) + } + + const formData = new FormData() + formData.append('_token', csrf_token) + formData.append('image', blobInfo.blob(), blobInfo.filename()) + + xhr.send(formData) + }) + + // note that skin and content_css is disabled to avoid the normal + // loading process and is instead loaded as a string via content_style + return ( + + ) +} diff --git a/resources/js/Layouts/Partials/routes.js b/resources/js/Layouts/Partials/routes.js index c393f96..991bbc7 100644 --- a/resources/js/Layouts/Partials/routes.js +++ b/resources/js/Layouts/Partials/routes.js @@ -1,4 +1,10 @@ -import { HiChartPie, HiUser, HiUsers, HiUserGroup } from 'react-icons/hi' +import { + HiChartPie, + HiUser, + HiUsers, + HiUserGroup, + HiInformationCircle, +} from 'react-icons/hi' import { HiQuestionMarkCircle } from 'react-icons/hi2' export default [ @@ -10,6 +16,14 @@ export default [ active: 'dashboard', permission: 'view-dashboard', }, + { + name: 'Banner', + show: true, + icon: HiInformationCircle, + route: route('banner.index'), + active: 'banner.*', + permission: 'view-banner', + }, { name: 'Info', show: true, diff --git a/resources/js/Pages/Banner/Form.jsx b/resources/js/Pages/Banner/Form.jsx new file mode 100644 index 0000000..e9c9ed9 --- /dev/null +++ b/resources/js/Pages/Banner/Form.jsx @@ -0,0 +1,125 @@ +import React, { useEffect, Suspense } from 'react' +import { isEmpty } from 'lodash' + +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import FormInput from '@/Components/FormInput' +import Button from '@/Components/Button' +import { Head, useForm } from '@inertiajs/react' +import FormFile from '@/Components/FormFile' + +const TinyEditor = React.lazy(() => import('@/Components/TinyMCE')) + +export default function Form(props) { + const { banner } = props + + const { data, setData, post, processing, errors } = useForm({ + title: '', + description: '', + image: '', + image_url: '', + }) + + const handleOnChange = (event) => { + setData( + event.target.name, + event.target.type === 'checkbox' + ? event.target.checked + ? 1 + : 0 + : event.target.value + ) + } + const handleSubmit = () => { + if (isEmpty(banner) === false) { + post(route('banner.update', banner)) + return + } + post(route('banner.store')) + } + + useEffect(() => { + if (isEmpty(banner) === false) { + setData({ + title: banner.title, + description: banner.description, + image_url: banner.image_url, + }) + } + }, [banner]) + + return ( + + + +
+
+
+
Banner
+ + + setData('image', e.target.files[0]) + } + error={errors.image} + preview={ + isEmpty(data.image_url) === false && ( + preview + ) + } + /> + +
+ Loading...
}> + { + setData( + 'description', + editor.getContent() + ) + }} + /> + +
+
+ +
+
+
+ +
+ ) +} diff --git a/resources/js/Pages/Banner/Index.jsx b/resources/js/Pages/Banner/Index.jsx new file mode 100644 index 0000000..03f690b --- /dev/null +++ b/resources/js/Pages/Banner/Index.jsx @@ -0,0 +1,143 @@ +import React, { useEffect, useState } from 'react' +import { Link, router } from '@inertiajs/react' +import { usePrevious } from 'react-use' +import { Head } from '@inertiajs/react' +import { Button, Dropdown } from 'flowbite-react' +import { HiPencil, HiTrash } from 'react-icons/hi' +import { useModalState } from '@/hooks' + +import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout' +import Pagination from '@/Components/Pagination' +import ModalConfirm from '@/Components/ModalConfirm' +import { hasPermission } from '@/utils' + +export default function Info(props) { + const { + query: { links, data }, + auth, + } = props + + const confirmModal = useModalState() + + const handleDeleteClick = (banner) => { + confirmModal.setData(banner) + confirmModal.toggle() + } + + const onDelete = () => { + if (confirmModal.data !== null) { + router.delete(route('banner.destroy', confirmModal.data.id)) + } + } + + const canCreate = hasPermission(auth, 'create-banner') + const canUpdate = hasPermission(auth, 'update-banner') + const canDelete = hasPermission(auth, 'delete-banner') + + return ( + + + +
+
+
+
+ {canCreate && ( + + + + )} +
+
+
+ + + + + + + + {data.map((banner) => ( + + + + + ))} + +
+ Judul + +
+ {banner.title} + + + {canUpdate && ( + + + +
+ Ubah +
+ +
+ )} + {canDelete && ( + + handleDeleteClick( + banner + ) + } + > +
+ +
+ Hapus +
+
+
+ )} +
+
+
+
+ +
+
+
+
+
+ +
+ ) +} diff --git a/resources/js/Pages/Home/Base.jsx b/resources/js/Pages/Home/Base.jsx index a4a5ad3..e6b6126 100644 --- a/resources/js/Pages/Home/Base.jsx +++ b/resources/js/Pages/Home/Base.jsx @@ -6,7 +6,7 @@ export default function Index({ status }) { return ( -
+
Login
diff --git a/resources/js/Pages/Home/Index/Banner.jsx b/resources/js/Pages/Home/Index/Banner.jsx new file mode 100644 index 0000000..b2aac39 --- /dev/null +++ b/resources/js/Pages/Home/Index/Banner.jsx @@ -0,0 +1,21 @@ +import React from 'react' +import { Head } from '@inertiajs/react' +import CustomerLayout from '@/Layouts/CustomerLayout' + +export default function Banner({ banner }) { + return ( + + +
+ +
+ {banner.title} +
+
+
+ + ) +} diff --git a/resources/js/Pages/Home/Index/Index.jsx b/resources/js/Pages/Home/Index/Index.jsx index efdd939..8faf39e 100644 --- a/resources/js/Pages/Home/Index/Index.jsx +++ b/resources/js/Pages/Home/Index/Index.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { Head, usePage } from '@inertiajs/react' +import { Head, router, usePage } from '@inertiajs/react' import CustomerLayout from '@/Layouts/CustomerLayout' import { HiOutlineBell } from 'react-icons/hi2' @@ -29,7 +29,11 @@ const GuestBanner = () => { ) } -export default function Index({ auth: { user }, infos }) { +export default function Index({ auth: { user }, infos, banners }) { + const handleBanner = (banner) => { + router.get(route('home.banner', banner)) + } + return ( @@ -39,23 +43,16 @@ export default function Index({ auth: { user }, infos }) { {/* banner */}
- - - - - + {banners.map((banner) => ( + handleBanner(banner)} + key={banner.id} + src={banner.image_url} + className={`rounded w-${ + banners.length === 1 ? 'full' : '[80%]' + } min-w-[340px] h-28 object-cover`} + /> + ))}
diff --git a/resources/js/Pages/Home/Index/UserBanner.jsx b/resources/js/Pages/Home/Index/UserBanner.jsx index 6ed4b66..7d00114 100644 --- a/resources/js/Pages/Home/Index/UserBanner.jsx +++ b/resources/js/Pages/Home/Index/UserBanner.jsx @@ -9,7 +9,7 @@ export default function UserBanner({ user }) {
{user.name}
-
+62{user.phone}
+
{user.display_phone}
{user.level.name}
diff --git a/resources/js/Pages/Home/Profile/Index.jsx b/resources/js/Pages/Home/Profile/Index.jsx index a95330e..fbab70d 100644 --- a/resources/js/Pages/Home/Profile/Index.jsx +++ b/resources/js/Pages/Home/Profile/Index.jsx @@ -42,7 +42,7 @@ export default function Index({ auth: { user } }) {
{user.name}
-
+62{user.phone}
+
{user.display_phone}
{user.level.name}
diff --git a/routes/admin.php b/routes/admin.php index 3f9e720..2027157 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -1,6 +1,7 @@ name('info.store'); Route::put('/infos/{info}', [InfoController::class, 'update'])->name('info.update'); Route::delete('/infos/{info}', [InfoController::class, 'destroy'])->name('info.destroy'); + + // upload + Route::post('/upload', [GeneralController::class, 'upload'])->name('post.upload'); + + // banner + Route::get('/banner', [BannerController::class, 'index'])->name('banner.index'); + Route::get('/banner/create', [BannerController::class, 'create'])->name('banner.create'); + Route::post('/banner', [BannerController::class, 'store'])->name('banner.store'); + Route::get('/banner/{banner}', [BannerController::class, 'edit'])->name('banner.edit'); + Route::post('/banner/{banner}', [BannerController::class, 'update'])->name('banner.update'); + Route::delete('/banner/{banner}', [BannerController::class, 'destroy'])->name('banner.destroy'); }); }); diff --git a/routes/web.php b/routes/web.php index 23d3ac8..a5feafe 100644 --- a/routes/web.php +++ b/routes/web.php @@ -19,6 +19,8 @@ use Illuminate\Support\Facades\Route; Route::middleware(['http_secure_aware', 'guard_should_customer', 'inertia.customer'])->group(function () { Route::get('/', [HomeController::class, 'index'])->name('home.index'); + Route::get('/banner/{banner}', [HomeController::class, 'banner'])->name('home.banner'); + Route::middleware('auth:customer')->group(function () { // profile Route::get('profile', [ProfileController::class, 'index'])->name('customer.profile.index');