0
0

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

Posted at

ログインしたユーザーが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);     //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;
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0