🐥

Webエンジニアへの道172日経過 - ポートフォリオアプリ ユーザー画面作成③

2024/10/14に公開

現在の学習状況

こんにちは、Someです!
まずは現在の学習状況をシェアしていきます。
※右にある数字は終了予定月と()内が完了実績月です。

  1. html・css 4月(4)
  2. Javascript 4月(4)
  3. Python 4月(4)
  4. Django 5月(5)
  5. git・github 5月(5)
  6. Linux 5月(5)
  7. DB・SQL 6月 (6)
  8. モダンJavaScript 6月(6)
  9. 業界研究、企業研究 7月(7)
  10. ポートフォリオアイデア 7月(7)
  11. PHP・Laravel基礎学習 7月(8)
  12. 安全なアプリケーション 8月(8)
  13. API 8月(8)
  14. ポートフォリオ作成開始 8月(8)←イマココ
  15. ポートフォリオ完成 10月
  16. 転職活動開始 10月
  17. 転職成功 12月

より詳細のロードマップ↓
(https://inquisitive-toy-f87.notion.site/519f079b4a34486781073b6f6b862572?v=13677f66b244447ea2ac02e6c6e455ca)

今週の学び・気付き

先週に引き続き、ポートフォリオアプリのユーザー画面を作成していきました!
ユーザートップ画面は盛り込む内容が非常に多いので、大苦戦しております。。。

献立設定画面


一週間分の献立を設定できる画面になります。
前回記事の見た目より、UI/UX向上を意識して作成しております。

コードはこちら↓

@php
use App\Models\UserDishes;
use App\Models\MenuOptions;
@endphp
<x-user-layout>
    <div class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 py-12">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="text-center mb-12">
                <h1 class="text-4xl font-bold text-gray-900 mb-2">週間メニュープランナー</h1>
                <p class="text-gray-600">1週間の献立を簡単に計画できます</p>
            </div>
            <!-- タブナビゲーション -->
            <div id="tabs" class="flex justify-around space-x-2 rounded-full bg-yellow-500/20 p-2 mb-8">
                @foreach(['Mon', 'Tues', 'Wednes', 'Thurs', 'Fri', 'Satur', 'Sun'] as $day)
                    <div class="tab w-full rounded-full py-3 text-lg font-semibold leading-5 text-blue-700 cursor-pointer text-center hover:bg-yellow-500 hover:text-white transition duration-300" data-tab="{{ $day }}">
                        {{ $day }}
                    </div>
                @endforeach
            </div>
            <!-- タブコンテンツ -->
            @foreach(['Mon', 'Tues', 'Wednes', 'Thurs', 'Fri', 'Satur', 'Sun'] as $day)
                <div id="{{ $day }}" class="tab-content mt-6 hidden">
                    <div class="bg-white rounded-2xl shadow-lg overflow-hidden">
                        <!-- 現在のメニュー表示部分 -->
                        <div class="bg-gradient-to-r from-blue-500 to-indigo-600 p-6 text-white">
                            <h2 class="text-2xl font-bold mb-4">{{ $day }}曜日の現在の献立</h2>
                            <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
                                @php
                                    $mealTypes = [
                                        ['title' => 'メインメニュー', 'key' => 'main_dish_id', 'icon' => '🍽️'],
                                        ['title' => 'サブメニュー1', 'key' => 'sub_dish1_id', 'icon' => '🥗'],
                                        ['title' => 'サブメニュー2', 'key' => 'sub_dish2_id', 'icon' => '🥘']
                                    ];
                                @endphp
                                @foreach($mealTypes as $meal)
                                    <div class="bg-white bg-opacity-10 backdrop-blur-lg rounded-xl p-4 border border-white border-opacity-20">
                                        <div class="flex items-center mb-2">
                                            <span class="text-2xl mr-2">{{ $meal['icon'] }}</span>
                                            <h3 class="font-medium">{{ $meal['title'] }}</h3>
                                        </div>
                                        @php
                                        // 最新の user_menus レコードを取得
                                        $latestUserMenu = $userMenus->where('day_of_week', $day)
                                            ->sortByDesc('created_at')  // created_at で降順にソート
                                            ->first();  // 最新のレコードを取得

                                        // 各メニュータイプに対応する料理名を取得
                                        $dishName = $latestUserMenu && $latestUserMenu->{$meal['key']}
                                            ? MenuOptions::find($latestUserMenu->{$meal['key']})->dish_name
                                            : '未設定';
                                        @endphp
                                        <p class="text-lg font-semibold">{{ $dishName }}</p>
                                    </div>
                                @endforeach
                            </div>
                        </div>

                        <!-- メニュー選択フォーム -->
                        <form action="{{ route('user.menu.keep') }}" method="POST" class="p-6">
                            @csrf
                            <input type="hidden" name="day_of_week" value="{{ $day }}">

                            <div class="mb-6">
                                <h2 class="text-2xl font-bold text-gray-900 mb-4">新しいメニューを選択</h2>
                                <p class="text-gray-600 mb-4">お好みのメニューを選択するか、ランダムに生成できます。</p>
                            </div>

                            <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
                                <div>
                                    <h3 class="font-semibold mb-4 text-lg">メインメニュー <i class="fas fa-drumstick-bite ml-2 text-yellow-500"></i></h3>
                                    <select name="main_menu" class="w-full p-3 border rounded-lg bg-yellow-50">
                                        @if($mainMenus->isNotEmpty())
                                            @foreach($mainMenus as $mainMenu)
                                                <option value="{{ $mainMenu->menu_option_id }}">{{ $mainMenu->menuOption->dish_name }}</option>
                                            @endforeach
                                        @else
                                            <option value="">No data available</option>
                                        @endif
                                    </select>
                                </div>
                                <div>
                                    <h3 class="font-semibold mb-4 text-lg">サブメニュー1 <i class="fas fa-leaf ml-2 text-yellow-500"></i></h3>
                                    <select name="sub_menu_1" class="w-full p-3 border rounded-lg bg-yellow-50">
                                        @foreach($subMenus as $subMenu)
                                            <option value="{{ $subMenu->menu_option_id }}">{{ $subMenu->menuOption->dish_name }}</option>
                                        @endforeach
                                    </select>
                                </div>
                                <div>
                                    <h3 class="font-semibold mb-4 text-lg">サブメニュー2 <i class="fas fa-carrot ml-2 text-yellow-500"></i></h3>
                                    <select name="sub_menu_2" class="w-full p-3 border rounded-lg bg-yellow-50">
                                        @foreach($subMenus as $subMenu)
                                            <option value="{{ $subMenu->menu_option_id }}">{{ $subMenu->menuOption->dish_name }}</option>
                                        @endforeach
                                    </select>
                                </div>
                            </div>

                            <div class="mt-8 flex space-x-4">
                                <button type="submit" class="keep flex-1 bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-lg transition duration-300">
                                    <i class="fas fa-save mr-2"></i>選択したメニューを保存
                                </button>
                            </form>
                                <!-- ランダムに保存 -->
                                <form action="{{ route('user.menu.randomkeep') }}" method="POST">
                                    @csrf
                                    <input type="hidden" name="day_of_week" value="{{ $day }}">
                                    <button type="submit" class="random flex-1 bg-yellow-500 hover:bg-yellow-600 text-white font-bold py-3 px-6 rounded-lg transition duration-300">
                                        <i class="fas fa-random mr-2"></i>ランダムに保存
                                    </button>
                                </form>
                            </div>
                    </div>
                </div>
            @endforeach

            {{-- <div class="mt-12 text-center">
                <form action="{{ route('user.menu.weekrandom') }}" method="POST" class="inline-block">
                    @csrf
                    <button type="submit" id="week-random" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-3 px-8 rounded-lg transition duration-300">
                        <i class="fas fa-random mr-2"></i>1週間分をランダムに設定
                    </button>
                </form>
            </div> --}}
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        $(document).ready(function() {
            var activeTab = '{{ session('active_tab', 'Mon') }}';

            // タブとコンテンツの初期化
            $('.tab-content').addClass('hidden');
            $('#' + activeTab).removeClass('hidden');

            $('.tab').removeClass('bg-white shadow');
            $('.tab[data-tab="' + activeTab + '"]').addClass('bg-white shadow');

            // タブクリック時のイベント
            $('.tab').click(function() {
                var tab = $(this).data('tab');
                $('.tab-content').addClass('hidden');
                $('#' + tab).removeClass('hidden');
                $('.tab').removeClass('bg-white shadow');
                $(this).addClass('bg-white shadow');
            });
        });
    </script>
</x-user-layout>

メニューを保存した時の曜日タブをそのままにするため、セッションを用いました。

献立候補設定画面


自分の環境において作る献立を選択できます。
ここでは献立候補と呼んでいます。

管理者が設定した献立候補が見られるようになっているので、今後はユーザーが献立候補を作成できるようにする予定です。
コードはこちら↓

@php
use App\Models\UserDishes;
@endphp

<x-user-layout>
    <div class="container mx-auto px-2 py-4">
        <div class="flex justify-between items-center mb-4">
            <h1 class="text-xl font-bold text-gray-800">献立候補</h1>
            <a href="{{ route('user.index') }}" class="bg-blue-500 hover:bg-blue-600 text-white text-xs font-bold py-1 px-3 rounded-full transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
                献立選択画面に戻る
            </a>
        </div>

        @if (session('success'))
            <div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-2 mb-3 text-xs" role="alert">
                <p class="font-bold">成功</p>
                <p>{{ session('success') }}</p>
            </div>
        @endif

        @if (session('error'))
            <div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-2 mb-3 text-xs" role="alert">
                <p class="font-bold">エラー</p>
                <p>{{ session('error') }}</p>
            </div>
        @endif

        <div class="mb-4">
            <h2 class="text-lg font-semibold mb-2 text-gray-700">メインメニュー</h2>
            <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2">
                @foreach ($menu_options->where('dish_type', 'main') as $menu_option)
                    @include('user.components.dish-card', ['menu_option' => $menu_option])
                @endforeach
            </div>
        </div>

        <div class="mb-4">
            <h2 class="text-lg font-semibold mb-2 text-gray-700">サブメニュー</h2>
            <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-2">
                @foreach ($menu_options->where('dish_type', 'sub') as $menu_option)
                    @include('user.components.dish-card', ['menu_option' => $menu_option])
                @endforeach
            </div>
        </div>
    </div>
</x-user-layout>

@php
use App\Models\UserDishes;
@endphp

@php
$userDishes = UserDishes::where('user_id', auth()->id())
    ->where('menu_option_id', $menu_option->id)
    ->first();
@endphp

<div class="bg-white rounded shadow-sm overflow-hidden transition duration-300 ease-in-out transform hover:-translate-y-1 hover:shadow-md relative">
    @if ($userDishes)
        <div class="absolute top-0 right-0 bg-green-500 text-white text-xs font-bold px-1 py-0.5 rounded-bl">追加済</div>
    @endif
    <div class="p-2">
        <h5 class="text-sm font-semibold text-gray-800 mb-1 truncate" title="{{ $menu_option->dish_name }}">{{ $menu_option->dish_name }}</h5>
        @if (!$userDishes)
            <form action="{{ route('user.dishes.store') }}" method="POST">
                @csrf
                <input type="hidden" name="menu_option_id" value="{{ $menu_option->id }}">
                <button type="submit" class="w-full bg-blue-500 hover:bg-blue-600 text-white text-xs font-bold py-2 px-2 rounded transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
                    追加
                </button>
            </form>
        @endif
        @if ($userDishes)
            <form action="{{ route('user.dishes.destroy', $userDishes->user_menu_id) }}" method="POST">
                @csrf
                @method('DELETE')
                <button type="submit" class="w-full bg-red-500 hover:bg-red-600 text-white text-xs font-bold py-2 px-2 rounded transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50">
                    削除
                </button>
            </form>
        @endif
    </div>
</div>

来週以降の進め方

今週は最も難しいと感じていた、曜日ごとの献立表示をある程度終えることができました!
やはり今の自分にとってはかなり難しく、AIの力なくして作ることはできませんでした。
しかしコードの意味はしっかり理解できているので、次に作るときには自分で書けるのではと思っています。

来週以降はユーザーにとって必ず必要な細かな項目を追加していき、まだまだ使い勝手が悪い部分を改善していきたいと思います!

それでは今回はこの辺で!
ありがとうございました!

Discussion