ログインしたユーザーがTODOリストを作成できるようにする

## ログインしたユーザーがTODOリストを作成できるようにする

ユーザーごとに異なるクッキーを作成し、ユーザーIDを用いて各ユーザーの情報を分離して管理する。

### TODOリスト情報

* タスク名
* タスク説明
* タスク期限
* ユーザー情報




### ログイン機能について

##### 使用技術
* html,
* css
* javascript 

##### 認証方法
* Email
* パスワード
* ユーザーID

##### 保存情報
* Email
* パスワード
* ユーザーID
* ユーザー名
* ユーザーアイコン

##### その他
* APIなし
* フロントエンドのみ
* 保存する箇所はCookiesに保存する

-------
### ファイル構成
```
login |--html
      |     |
      |     |--login.html
      |     |--profile.html
      |     |--create-account.html
      |     |--todo.html
      | 
      |--app.js
      |  
      |--styles.css
```
-----

#### login.html

```
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ログイン</title>
    <link rel="stylesheet" href="styles.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
    
</head>
<body>

    <div class="container">
        <h2>ログイン</h2>
        <form id="loginForm">
            <input type="email" id="email" placeholder="メールアドレス" required>
            <input type="password" id="password" placeholder="パスワード" required>
            <input type="text" id="userId" placeholder="ユーザーID" required> <!-- ユーザーID入力フィールドを追加 -->
            <button type="submit">ログイン</button>
        </form>
        <p>アカウントをお持ちでない方は <a href="create-account.html">新規アカウント作成</a></p>
    </div>
    

    <script src="app.js"></script>
</body>
</html>

```
------


#### profile.html

```

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ユーザープロフィール</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="container">
        <h2>ようこそ、<span id="displayName"></span>さん</h2>
        <img id="displayIcon" src="" alt="User Icon">
        <!-- TODOリストへのリンク追加 -->
        <p><a href="todo.html" class="todo-link">TODOリストを管理</a></p>
    </div>
    <div id="displayName"></div>
    <script src="app.js"></script>
</body>
</html>

```

-------
#### create-account.html

```
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>新規アカウント作成</title>
    <link rel="stylesheet" href="styles.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
    
</head>
<body>
    
    <div class="container">
        <h2>新規アカウント作成</h2>
        <form id="accountForm">
            <input type="email" id="newEmail" placeholder="メールアドレス" required>
            <input type="password" id="newPassword" placeholder="パスワード" required>
            <input type="text" id="username" placeholder="ユーザー名" required>
            <input type="text" id="newUserId" placeholder="ユーザーID" required> <!-- ユーザーID入力フィールドを追加 -->
            <input type="file" id="userIcon" accept="image/*">
            <button type="submit">アカウント作成</button>
        </form>
    </div>
    


    <script src="app.js"></script>  
</body>
</html>

```
-------
#### todo.html

```
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TODOリスト</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="container">
        <h2>TODOリスト</h2>
        <form id="todoForm">
            <input type="text" id="taskName" placeholder="タスク名" required>
            <textarea id="taskDescription" placeholder="タスク説明" required></textarea>
            <input type="date" id="taskDeadline" required>
            <button type="submit">タスク追加</button>
        </form>
        <!-- プロフィールへ戻るボタン追加 -->
        <button onclick="window.location.href='profile.html'" class="back-button">プロフィールに戻る</button>
        <div id="todoList">
            <!-- JavaScriptで動的にタスクを表示 -->
        </div>
        <div id="todoList" class="todo-list"></div>
    </div>

    <script src="app.js"></script>
</body>
</html>

```
--------

#### app.js


```
// ログインフォームの処理
if (document.getElementById('loginForm')) {  //HTMLの'loginForm'を確認
    document.getElementById('loginForm').addEventListener('submit', function(e) {  //'loginForm'があった場合のイベントを追加
        e.preventDefault();                 //デフォルトの動作をキャンセルする
        
        const email = document.getElementById('email').value;     //ユーザーが入力したemailを取得
        const password = document.getElementById('password').value;  //ユーザーが入力したpasswordを取得
        const userId = document.getElementById('userId').value; // ユーザーが入力したユーザーIDを取得
        const storedEmail = getCookie('email', userId);
        const storedPassword = getCookie('password', userId);
        if (email === storedEmail && password === storedPassword) {
            window.location.href = 'profile.html';
        } else {
            toastr.error('メールアドレスまたはパスワードが正しくありません。');
        }
    });
}


// アカウント作成フォームの処理
if (document.getElementById('accountForm'))     //HTMLの'accountForm'を確認
    document.getElementById('accountForm').addEventListener('submit', async function(e) {    //'accountFormがあった場合のイベントを追加 //asyncを追加
        e.preventDefault();              //デフォルトの動作をキャンセルする

        const email = document.getElementById('newEmail').value;    //ユーザーが入力したemailを取得
        const password = document.getElementById('newPassword').value;    //ユーザーが入力したpasswordを取得
        const username = document.getElementById('username').value;    //ユーザーが入力したユーザー名を取得
        const userId = document.getElementById('newUserId').value; // ユーザーが入力したユーザーIDを取得
        const userIcon = document.getElementById('userIcon').files[0];    //ユーザーが入力したアイコンを取得
        
      
        const formData = new FormData();     //空のフォーム
        formData.append('image', userIcon);     //サーバにユーザが指定したアイコンデータを送信する


        try {
            const response = await fetch(`https://api.imgbb.com/1/upload?key=c0f0b6237007dc47943f085d13b621c1`, { //アップロード先のURL
                method: 'POST',
                body: formData     //送信するデータ
            });
            const data = await response.json();
            if (data.status === 200) {
                const imageUrl = data.data.url;
                setCookie('userId', userId, 7);
                setCookie('email', email, 7, userId);
                setCookie('password', password, 7, userId);
                setCookie('username', username, 7, userId);
                setCookie('userIcon', imageUrl, 7, userId);
                toastr.success('アカウントが正常に作成されました。');
                window.location.href = 'login.html';
            } else {
                throw new Error('Failed to upload image');
            }
        } catch (error) {
            console.error('Error uploading image:', error);
            toastr.error('画像のアップロードに失敗しました。');
        }
    }



// プロファイルページでユーザー情報を表示
if (window.location.pathname.includes('profile.html')) {
    // ログイン時
const userId = getCookie('userId'); // ユーザーIDを取得

// プロファイル表示時
const username = getCookie('username', userId);
const userIcon = getCookie('userIcon', userId);

    if (username && userIcon) {     //ユーザー名とアイコンが存在するか確認
        document.getElementById('displayName').textContent = username;     //クッキーから取得したユーザー名を表示
        document.getElementById('displayIcon').src = userIcon;     //クッキーから取得したアイコンを表示
    } else {
        window.location.href = 'login.html';     //クッキーに情報がない場合'login.html'ページに移動
    }
}

// Cookieを設定する関数
function setCookie(name, value, days, userId) {
    const date = new Date();
    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
    const expires = "expires=" + date.toUTCString();
    document.cookie = userId + "_" + name + "=" + value + ";" + expires + ";path=/";
}


// Cookieを取得する関数

function getCookie(name, userId) {
    const nameEQ = userId + "_" + name + "=";
    const ca = document.cookie.split(';');
    for (let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) === ' ') c = c.substring(1);
        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
}


// TODOリストページの処理
if (window.location.pathname.includes('todo.html')) {
    document.getElementById('todoForm').addEventListener('submit', function(e) {
        console.log(`追加ボタンを押したら発火`)

        e.preventDefault();     //デフォルトの動作をキャンセルする
        const taskName = document.getElementById('taskName').value;     //ユーザーが入力したタスク名を取得
        console.log(`taskName`, taskName)
        const taskDescription = document.getElementById('taskDescription').value;     //ユーザーが入力したタスクの説明文を取得
        console.log(`taskDescription`, taskDescription)
        const taskDeadline = document.getElementById('taskDeadline').value;     //ユーザーが入力した期限を取得
        console.log(`taskDeadline`, taskDeadline)
        
        const newTask = {     //新しいタスクの作成と追加
            name: taskName,     //ユーザーが入力したタスク名を保持
            description: taskDescription,     //ユーザーが入力したタスクの説明を保持
            deadline: taskDeadline      //ユーザーが入力したタスクの期限を保持
        };

        console.log(`newTask`, newTask)
        
        addTask(newTask);     //ユーザーがクリックすると新しいタスクを追加できる
        displayTasks();     //更新されたタスクリストをユーザーに表示
       
    });
    
    function addTask(task) {
        console.log(`addTask()`)
        const tasks = getTasks();     //保存されているタスクのリストを返す
        console.log(`tasks`, tasks)
        tasks.push(task);     //新しいタスクを追加
        console.log(`tasks`, tasks)
        saveTasks(tasks);     //変更されたタスクを保存
    }

    function displayTasks() {
        console.log(`displayTasks`)
        const tasks = getTasks();     //保存されている全てのタスクを取得
        console.log(`tasks`, tasks)
        const todoList = document.getElementById('todoList');     //HTMLの'todoList'を取得
        console.log(`todoList`, todoList)
        todoList.innerHTML = ''; // 'todoList'を空に設定して新たにタスクを表示する準備
        tasks.forEach((task, index) => {     //タスクに対して処理を行うループ
            console.log(`task`, task)
            console.log(`index`, index)
            const taskElement = document.createElement('div');     //各タスクの情報を囲むコンテナ
            console.log(`taskElement`, taskElement)
            taskElement.innerHTML = `     
                <p>タスク名: ${task.name}</p>
                <p>説明: ${task.description}</p>
                <p>期限: ${task.deadline}</p>
                <button onclick="deleteTask(${index})">削除</button>        
            `;     //タスクの詳細（名前、説明、期限）と削除ボタンを含むHTMLを設定
            console.log(`taskElement`, taskElement)
            todoList.appendChild(taskElement);     //画面上にタスクが表示される
            console.log(`todoList`, todoList)
        });
    }

    function deleteTask(index) { 
        console.log(`deleteTask()`)
        const tasks = getTasks();    //保存されているタスクを取得
        console.log(`tasks`, tasks)
        tasks.splice(index, 1);     //１つのタスクを削除
        console.log(`tasks`, tasks)
        saveTasks(tasks);     //更新されたタスクリストを再び保存
        displayTasks();     //
    }

    function getTasks() {
        const userId = getCookie('userId');
        const tasks = getCookie('tasks', userId);
        return tasks ? JSON.parse(tasks) : [];
    }
    
    function saveTasks(tasks) {
        const userId = getCookie('userId');
        setCookie('tasks', JSON.stringify(tasks), 7, userId);
    }
   
    }

    // 既存の Cookie 操作関数 `setCookie` と `getCookie` はそのまま利用


// ページロード時にタスクを表示
window.onload = function() {
    if (window.location.pathname.includes('todo.html')) {
        displayTasks();
    }
};


```

-------
styles.css

```
/* 基本設定 */
body {
    font-family: 'Poppins', sans-serif;
    background-color: #f5f5f5;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

/* コンテナのスタイル */
.container {
    width: 100%;
    max-width: 350px;
    padding: 30px;
    background-color: white;
    border-radius: 10px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
    text-align: center;
}

/* 見出しスタイル */
h2 {
    margin-bottom: 20px;
    font-size: 1.5rem;
    color: #333;
    font-weight: 600;
}

/* 入力フィールドのスタイル */
input[type="email"],
input[type="password"],
input[type="text"],
input[type="file"] {
    width: 80%;
    max-width: 280px;
    padding: 12px 15px;
    margin: 10px 0;
    border: 2px solid #ddd;
    border-radius: 8px;
    background-color: #f9f9f9;
    font-size: 1rem;
    transition: border 0.3s, background-color 0.3s;
}

/* フォーカス時のスタイル */
input[type="email"]:focus,
input[type="password"]:focus,
input[type="text"]:focus {
    border-color: #b654c5;
    background-color: white;
    outline: none;
    box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}

/* ボタンのスタイル */
button {
    width: 80%;
    max-width: 280px;
    padding: 12px;
    background-color: #b654c5;
    color: white;
    font-size: 1rem;
    font-weight: 600;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: background-color 0.3s, box-shadow 0.3s;
}

/* ボタンホバー時のエフェクト */
button:hover {
    background-color: #b654c5;
    box-shadow: 0 4px 12px rgba(0, 91, 179, 0.2);
}

/* リンクのスタイル */
p a {
    color: #b654c5;
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease;
}

p a:hover {
    color: #b654c5;
}

/* ユーザーアイコンのスタイル */
#displayIcon {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    object-fit: cover;
    margin-top: 10px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

/* レスポンシブ設定 */
@media (max-width: 768px) {
    .container {
        width: 90%;
    }
}

/* TODOリストのスタイル調整 */
.todo-list {
    width: 100%;
    max-width: 350px;
    margin-top: 20px;
    padding: 10px;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

.todo-list div {
    padding: 10px;
    margin: 5px 0;
    border-bottom: 1px solid #eee;
}

.todo-list div:last-child {
    border-bottom: none;
}

.todo-list p {
    margin: 5px 0;
    color: #333;
}

button {
    margin-top: 10px;
}

```