feat: GitHub完全活用ガイド - 包括的な解説書とWebサイトを追加
🎯 主要機能: - GitHub機能の網羅的解説書 (10章構成) - 外部ツール代替戦略とコスト分析 - 実践的な設定例とベストプラクティス - 実務ケーススタディと段階的移行計画 🌐 GitHub Pages Webサイト: - Jekyll設定とレスポンシブデザイン - 自動デプロイワークフロー - 美しいランディングページ - SEO最適化とモバイル対応 📊 期待効果: - 年間37%のコスト削減 - 開発効率2倍向上 - セキュリティ強化 🚀 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
1e2b71e1b3
17 changed files with 4250 additions and 0 deletions
162
practice-project/README.md
Normal file
162
practice-project/README.md
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
# 🎯 Personal Task Manager - GitHub機能実践プロジェクト
|
||||
|
||||
> GitHub機能を体系的に学習するための実践プロジェクト
|
||||
|
||||
## 🎯 プロジェクト概要
|
||||
|
||||
シンプルなタスク管理アプリケーションを通じて、GitHub のすべての主要機能を実際に体験・習得します。
|
||||
|
||||
## 🏗️ プロジェクト構成
|
||||
|
||||
```
|
||||
personal-task-manager/
|
||||
├── index.html # メインページ
|
||||
├── style.css # スタイルシート
|
||||
├── script.js # JavaScript ロジック
|
||||
├── package.json # Node.js設定(Actions用)
|
||||
├── .github/
|
||||
│ ├── workflows/ # GitHub Actions
|
||||
│ ├── ISSUE_TEMPLATE/ # Issueテンプレート
|
||||
│ └── pull_request_template.md
|
||||
├── docs/ # GitHub Pages用
|
||||
└── tests/ # テストファイル
|
||||
```
|
||||
|
||||
## 🎓 学習目標と体験する機能
|
||||
|
||||
### Phase 1: 基本機能の体験
|
||||
- [x] **リポジトリ作成** - 設定とクローン
|
||||
- [ ] **Issues管理** - バグ報告、機能要望、タスク作成
|
||||
- [ ] **Projects** - かんばんボードでタスク進行管理
|
||||
- [ ] **ブランチ管理** - feature ブランチでの開発
|
||||
|
||||
### Phase 2: コラボレーション機能
|
||||
- [ ] **Pull Request** - コードレビューフロー体験
|
||||
- [ ] **テンプレート** - Issue/PR テンプレートの活用
|
||||
- [ ] **ラベル・マイルストーン** - プロジェクト管理の体系化
|
||||
- [ ] **レビュー機能** - サジェスト機能の活用
|
||||
|
||||
### Phase 3: 自動化・デプロイ
|
||||
- [ ] **GitHub Actions** - CI/CDパイプライン構築
|
||||
- [ ] **自動テスト** - コード品質の自動チェック
|
||||
- [ ] **GitHub Pages** - 自動デプロイメント
|
||||
- [ ] **セキュリティ** - Dependabot、Code scanning
|
||||
|
||||
## 🚀 実装予定機能
|
||||
|
||||
### 基本機能
|
||||
- ✅ タスクの追加・編集・削除
|
||||
- ✅ タスクの完了状態管理
|
||||
- ✅ カテゴリ別フィルタリング
|
||||
- ✅ ローカルストレージでの永続化
|
||||
|
||||
### 発展機能(GitHub機能習得用)
|
||||
- ⏳ タスクの優先度設定
|
||||
- ⏳ 期限管理機能
|
||||
- ⏳ 進捗レポート表示
|
||||
- ⏳ データエクスポート機能
|
||||
|
||||
## 📋 開発フロー(GitHub機能実践)
|
||||
|
||||
### 1. Issue駆動開発
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Issue作成] --> B[ラベル付与]
|
||||
B --> C[Projects追加]
|
||||
C --> D[ブランチ作成]
|
||||
D --> E[実装]
|
||||
E --> F[PR作成]
|
||||
F --> G[レビュー]
|
||||
G --> H[マージ]
|
||||
H --> I[Issue クローズ]
|
||||
```
|
||||
|
||||
### 2. ブランチ戦略
|
||||
```
|
||||
main : プロダクションコード
|
||||
develop : 開発版(統合テスト)
|
||||
feature/* : 機能開発用
|
||||
hotfix/* : 緊急修正用
|
||||
```
|
||||
|
||||
### 3. 自動化フロー
|
||||
```yaml
|
||||
# CI/CD パイプライン
|
||||
1. コミット → 自動テスト実行
|
||||
2. PR作成 → コードスキャン + レビュー
|
||||
3. mainマージ → 自動デプロイ(GitHub Pages)
|
||||
4. リリース → 自動タグ作成 + チェンジログ生成
|
||||
```
|
||||
|
||||
## 🎯 GitHub機能習得チェックリスト
|
||||
|
||||
### 基本操作
|
||||
- [ ] リポジトリ作成・クローン
|
||||
- [ ] ブランチ作成・切り替え
|
||||
- [ ] コミット・プッシュ
|
||||
- [ ] Issue作成・管理
|
||||
|
||||
### プロジェクト管理
|
||||
- [ ] GitHub Projects セットアップ
|
||||
- [ ] Issue テンプレート作成
|
||||
- [ ] ラベル体系の構築
|
||||
- [ ] マイルストーン設定
|
||||
|
||||
### コラボレーション
|
||||
- [ ] Pull Request作成
|
||||
- [ ] コードレビュー
|
||||
- [ ] サジェスト機能使用
|
||||
- [ ] コンフリクト解決
|
||||
|
||||
### 自動化
|
||||
- [ ] GitHub Actions セットアップ
|
||||
- [ ] CI/CD パイプライン構築
|
||||
- [ ] 自動テスト実装
|
||||
- [ ] 自動デプロイ設定
|
||||
|
||||
### セキュリティ・品質
|
||||
- [ ] Dependabot 有効化
|
||||
- [ ] Code scanning 設定
|
||||
- [ ] Branch protection 設定
|
||||
- [ ] Secret管理
|
||||
|
||||
### 公開・ドキュメント
|
||||
- [ ] GitHub Pages デプロイ
|
||||
- [ ] README 充実
|
||||
- [ ] Wiki 作成
|
||||
- [ ] Release 作成
|
||||
|
||||
## 🎮 次のアクション
|
||||
|
||||
1. **今すぐ試せること**:
|
||||
```bash
|
||||
# このプロジェクトをクローン
|
||||
git clone https://github.com/your-username/personal-task-manager
|
||||
cd personal-task-manager
|
||||
|
||||
# 最初のIssueを作成
|
||||
gh issue create --title "タスク追加機能の実装" --body "ユーザーが新しいタスクを追加できる機能を実装する"
|
||||
```
|
||||
|
||||
2. **最初の1週間で体験する機能**:
|
||||
- Issues でタスク管理
|
||||
- Projects でかんばん作成
|
||||
- 基本的なHTML/CSS/JS開発
|
||||
- Pull Request フロー
|
||||
|
||||
3. **2週間目で学ぶ高度な機能**:
|
||||
- GitHub Actions 設定
|
||||
- 自動テスト・デプロイ
|
||||
- セキュリティ機能
|
||||
- パフォーマンス最適化
|
||||
|
||||
## 💡 学習のコツ
|
||||
|
||||
1. **小さく始める** - 最初は基本機能のみ実装
|
||||
2. **実際に使う** - 本物のタスク管理に活用
|
||||
3. **記録する** - 体験したことを都度ドキュメント化
|
||||
4. **実験する** - 新しい機能を積極的に試す
|
||||
|
||||
---
|
||||
|
||||
このプロジェクトを通じて、理論だけでなく実践的なGitHub活用スキルを身につけましょう!
|
||||
143
practice-project/index.html
Normal file
143
practice-project/index.html
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Personal Task Manager</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>🎯 Personal Task Manager</h1>
|
||||
<p>GitHub機能学習用タスク管理アプリ</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<!-- タスク追加フォーム -->
|
||||
<section class="add-task-section">
|
||||
<h2>新しいタスクを追加</h2>
|
||||
<form id="addTaskForm">
|
||||
<div class="form-group">
|
||||
<input type="text" id="taskTitle" placeholder="タスクタイトル" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<textarea id="taskDescription" placeholder="詳細説明(任意)"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select id="taskCategory">
|
||||
<option value="feature">新機能</option>
|
||||
<option value="bug">バグ修正</option>
|
||||
<option value="improvement">改善</option>
|
||||
<option value="documentation">ドキュメント</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select id="taskPriority">
|
||||
<option value="low">低優先度</option>
|
||||
<option value="medium" selected>中優先度</option>
|
||||
<option value="high">高優先度</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit">タスクを追加</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- フィルタリング -->
|
||||
<section class="filter-section">
|
||||
<h2>フィルタ</h2>
|
||||
<div class="filters">
|
||||
<select id="categoryFilter">
|
||||
<option value="all">すべてのカテゴリ</option>
|
||||
<option value="feature">新機能</option>
|
||||
<option value="bug">バグ修正</option>
|
||||
<option value="improvement">改善</option>
|
||||
<option value="documentation">ドキュメント</option>
|
||||
</select>
|
||||
<select id="statusFilter">
|
||||
<option value="all">すべての状態</option>
|
||||
<option value="todo">TODO</option>
|
||||
<option value="in-progress">進行中</option>
|
||||
<option value="done">完了</option>
|
||||
</select>
|
||||
<select id="priorityFilter">
|
||||
<option value="all">すべての優先度</option>
|
||||
<option value="high">高優先度</option>
|
||||
<option value="medium">中優先度</option>
|
||||
<option value="low">低優先度</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- タスクリスト -->
|
||||
<section class="tasks-section">
|
||||
<h2>タスク一覧</h2>
|
||||
<div class="task-board">
|
||||
<div class="task-column" data-status="todo">
|
||||
<h3>📋 TODO</h3>
|
||||
<div class="task-list" id="todoTasks"></div>
|
||||
</div>
|
||||
<div class="task-column" data-status="in-progress">
|
||||
<h3>🔄 進行中</h3>
|
||||
<div class="task-list" id="inProgressTasks"></div>
|
||||
</div>
|
||||
<div class="task-column" data-status="done">
|
||||
<h3>✅ 完了</h3>
|
||||
<div class="task-list" id="doneTasks"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 統計情報 -->
|
||||
<section class="stats-section">
|
||||
<h2>統計</h2>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>総タスク数</h3>
|
||||
<span id="totalTasks">0</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>完了タスク</h3>
|
||||
<span id="completedTasks">0</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>進行中</h3>
|
||||
<span id="inProgressCount">0</span>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3>完了率</h3>
|
||||
<span id="completionRate">0%</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>GitHub機能学習プロジェクト - Personal Task Manager</p>
|
||||
<p>Issues、Projects、Actions、Pages の実践的な学習を目的としています</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- タスクテンプレート -->
|
||||
<template id="taskTemplate">
|
||||
<div class="task-card" draggable="true">
|
||||
<div class="task-header">
|
||||
<span class="task-id"></span>
|
||||
<span class="task-priority"></span>
|
||||
<span class="task-category"></span>
|
||||
</div>
|
||||
<h4 class="task-title"></h4>
|
||||
<p class="task-description"></p>
|
||||
<div class="task-meta">
|
||||
<span class="task-created"></span>
|
||||
<div class="task-actions">
|
||||
<button class="edit-btn" title="編集">✏️</button>
|
||||
<button class="delete-btn" title="削除">🗑️</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
412
practice-project/script.js
Normal file
412
practice-project/script.js
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
// Personal Task Manager - GitHub学習用JavaScript
|
||||
|
||||
class TaskManager {
|
||||
constructor() {
|
||||
this.tasks = this.loadTasks();
|
||||
this.taskIdCounter = this.getNextTaskId();
|
||||
this.initializeApp();
|
||||
}
|
||||
|
||||
// アプリケーション初期化
|
||||
initializeApp() {
|
||||
this.setupEventListeners();
|
||||
this.renderTasks();
|
||||
this.updateStats();
|
||||
this.setupDragAndDrop();
|
||||
}
|
||||
|
||||
// イベントリスナー設定
|
||||
setupEventListeners() {
|
||||
// タスク追加フォーム
|
||||
document.getElementById('addTaskForm').addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.addTask();
|
||||
});
|
||||
|
||||
// フィルタ変更
|
||||
document.getElementById('categoryFilter').addEventListener('change', () => this.applyFilters());
|
||||
document.getElementById('statusFilter').addEventListener('change', () => this.applyFilters());
|
||||
document.getElementById('priorityFilter').addEventListener('change', () => this.applyFilters());
|
||||
|
||||
// タスク操作(イベント委譲)
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('edit-btn')) {
|
||||
this.editTask(e.target.closest('.task-card').dataset.taskId);
|
||||
}
|
||||
if (e.target.classList.contains('delete-btn')) {
|
||||
this.deleteTask(e.target.closest('.task-card').dataset.taskId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// タスク追加
|
||||
addTask() {
|
||||
const title = document.getElementById('taskTitle').value.trim();
|
||||
const description = document.getElementById('taskDescription').value.trim();
|
||||
const category = document.getElementById('taskCategory').value;
|
||||
const priority = document.getElementById('taskPriority').value;
|
||||
|
||||
if (!title) {
|
||||
alert('タスクタイトルを入力してください');
|
||||
return;
|
||||
}
|
||||
|
||||
const task = {
|
||||
id: this.taskIdCounter++,
|
||||
title,
|
||||
description,
|
||||
category,
|
||||
priority,
|
||||
status: 'todo',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
};
|
||||
|
||||
this.tasks.push(task);
|
||||
this.saveTasks();
|
||||
this.renderTasks();
|
||||
this.updateStats();
|
||||
this.clearForm();
|
||||
|
||||
// GitHub風の成功メッセージ(実際のGitHubではIssue作成時に表示される)
|
||||
this.showNotification(`タスク #${task.id} を作成しました`, 'success');
|
||||
}
|
||||
|
||||
// タスク編集
|
||||
editTask(taskId) {
|
||||
const task = this.tasks.find(t => t.id === parseInt(taskId));
|
||||
if (!task) return;
|
||||
|
||||
const newTitle = prompt('タスクタイトル:', task.title);
|
||||
if (newTitle === null) return;
|
||||
|
||||
const newDescription = prompt('タスク説明:', task.description);
|
||||
if (newDescription === null) return;
|
||||
|
||||
task.title = newTitle.trim();
|
||||
task.description = newDescription.trim();
|
||||
task.updatedAt = new Date().toISOString();
|
||||
|
||||
this.saveTasks();
|
||||
this.renderTasks();
|
||||
this.showNotification(`タスク #${task.id} を更新しました`, 'info');
|
||||
}
|
||||
|
||||
// タスク削除
|
||||
deleteTask(taskId) {
|
||||
const task = this.tasks.find(t => t.id === parseInt(taskId));
|
||||
if (!task) return;
|
||||
|
||||
if (confirm(`タスク「${task.title}」を削除しますか?`)) {
|
||||
this.tasks = this.tasks.filter(t => t.id !== parseInt(taskId));
|
||||
this.saveTasks();
|
||||
this.renderTasks();
|
||||
this.updateStats();
|
||||
this.showNotification(`タスク #${task.id} を削除しました`, 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// タスクレンダリング
|
||||
renderTasks() {
|
||||
const todoContainer = document.getElementById('todoTasks');
|
||||
const inProgressContainer = document.getElementById('inProgressTasks');
|
||||
const doneContainer = document.getElementById('doneTasks');
|
||||
|
||||
// コンテナをクリア
|
||||
todoContainer.innerHTML = '';
|
||||
inProgressContainer.innerHTML = '';
|
||||
doneContainer.innerHTML = '';
|
||||
|
||||
// フィルタリングされたタスクを取得
|
||||
const filteredTasks = this.getFilteredTasks();
|
||||
|
||||
// 各タスクをレンダリング
|
||||
filteredTasks.forEach(task => {
|
||||
const taskElement = this.createTaskElement(task);
|
||||
|
||||
switch (task.status) {
|
||||
case 'todo':
|
||||
todoContainer.appendChild(taskElement);
|
||||
break;
|
||||
case 'in-progress':
|
||||
inProgressContainer.appendChild(taskElement);
|
||||
break;
|
||||
case 'done':
|
||||
doneContainer.appendChild(taskElement);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// タスク要素作成
|
||||
createTaskElement(task) {
|
||||
const template = document.getElementById('taskTemplate');
|
||||
const taskElement = template.content.cloneNode(true);
|
||||
const taskCard = taskElement.querySelector('.task-card');
|
||||
|
||||
taskCard.dataset.taskId = task.id;
|
||||
taskCard.dataset.status = task.status;
|
||||
|
||||
// タスク情報を設定
|
||||
taskElement.querySelector('.task-id').textContent = `#${task.id}`;
|
||||
taskElement.querySelector('.task-title').textContent = task.title;
|
||||
taskElement.querySelector('.task-description').textContent = task.description || '説明なし';
|
||||
|
||||
// 優先度設定
|
||||
const priorityElement = taskElement.querySelector('.task-priority');
|
||||
priorityElement.textContent = this.getPriorityLabel(task.priority);
|
||||
priorityElement.className = `task-priority ${task.priority}`;
|
||||
|
||||
// カテゴリ設定
|
||||
const categoryElement = taskElement.querySelector('.task-category');
|
||||
categoryElement.textContent = this.getCategoryLabel(task.category);
|
||||
categoryElement.className = `task-category ${task.category}`;
|
||||
|
||||
// 作成日時
|
||||
taskElement.querySelector('.task-created').textContent =
|
||||
new Date(task.createdAt).toLocaleDateString('ja-JP');
|
||||
|
||||
return taskElement;
|
||||
}
|
||||
|
||||
// フィルタリング適用
|
||||
applyFilters() {
|
||||
this.renderTasks();
|
||||
}
|
||||
|
||||
// フィルタリングされたタスク取得
|
||||
getFilteredTasks() {
|
||||
const categoryFilter = document.getElementById('categoryFilter').value;
|
||||
const statusFilter = document.getElementById('statusFilter').value;
|
||||
const priorityFilter = document.getElementById('priorityFilter').value;
|
||||
|
||||
return this.tasks.filter(task => {
|
||||
const matchesCategory = categoryFilter === 'all' || task.category === categoryFilter;
|
||||
const matchesStatus = statusFilter === 'all' || task.status === statusFilter;
|
||||
const matchesPriority = priorityFilter === 'all' || task.priority === priorityFilter;
|
||||
|
||||
return matchesCategory && matchesStatus && matchesPriority;
|
||||
});
|
||||
}
|
||||
|
||||
// 統計更新
|
||||
updateStats() {
|
||||
const totalTasks = this.tasks.length;
|
||||
const completedTasks = this.tasks.filter(t => t.status === 'done').length;
|
||||
const inProgressTasks = this.tasks.filter(t => t.status === 'in-progress').length;
|
||||
const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
|
||||
|
||||
document.getElementById('totalTasks').textContent = totalTasks;
|
||||
document.getElementById('completedTasks').textContent = completedTasks;
|
||||
document.getElementById('inProgressCount').textContent = inProgressTasks;
|
||||
document.getElementById('completionRate').textContent = `${completionRate}%`;
|
||||
}
|
||||
|
||||
// ドラッグ&ドロップ設定
|
||||
setupDragAndDrop() {
|
||||
// タスクカードのドラッグ開始
|
||||
document.addEventListener('dragstart', (e) => {
|
||||
if (e.target.classList.contains('task-card')) {
|
||||
e.target.classList.add('dragging');
|
||||
e.dataTransfer.setData('text/plain', e.target.dataset.taskId);
|
||||
}
|
||||
});
|
||||
|
||||
// ドラッグ終了
|
||||
document.addEventListener('dragend', (e) => {
|
||||
if (e.target.classList.contains('task-card')) {
|
||||
e.target.classList.remove('dragging');
|
||||
}
|
||||
});
|
||||
|
||||
// ドロップゾーンの設定
|
||||
document.querySelectorAll('.task-column').forEach(column => {
|
||||
column.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
column.classList.add('drag-over');
|
||||
});
|
||||
|
||||
column.addEventListener('dragleave', (e) => {
|
||||
if (!column.contains(e.relatedTarget)) {
|
||||
column.classList.remove('drag-over');
|
||||
}
|
||||
});
|
||||
|
||||
column.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
column.classList.remove('drag-over');
|
||||
|
||||
const taskId = e.dataTransfer.getData('text/plain');
|
||||
const newStatus = column.dataset.status;
|
||||
this.updateTaskStatus(parseInt(taskId), newStatus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// タスクステータス更新
|
||||
updateTaskStatus(taskId, newStatus) {
|
||||
const task = this.tasks.find(t => t.id === taskId);
|
||||
if (!task || task.status === newStatus) return;
|
||||
|
||||
const oldStatus = task.status;
|
||||
task.status = newStatus;
|
||||
task.updatedAt = new Date().toISOString();
|
||||
|
||||
this.saveTasks();
|
||||
this.renderTasks();
|
||||
this.updateStats();
|
||||
|
||||
// GitHub風のステータス更新メッセージ
|
||||
this.showNotification(
|
||||
`タスク #${taskId} を ${this.getStatusLabel(oldStatus)} から ${this.getStatusLabel(newStatus)} に移動しました`,
|
||||
'info'
|
||||
);
|
||||
}
|
||||
|
||||
// ユーティリティ関数
|
||||
getPriorityLabel(priority) {
|
||||
const labels = {
|
||||
'high': '高',
|
||||
'medium': '中',
|
||||
'low': '低'
|
||||
};
|
||||
return labels[priority] || priority;
|
||||
}
|
||||
|
||||
getCategoryLabel(category) {
|
||||
const labels = {
|
||||
'feature': '新機能',
|
||||
'bug': 'バグ',
|
||||
'improvement': '改善',
|
||||
'documentation': 'ドキュメント'
|
||||
};
|
||||
return labels[category] || category;
|
||||
}
|
||||
|
||||
getStatusLabel(status) {
|
||||
const labels = {
|
||||
'todo': 'TODO',
|
||||
'in-progress': '進行中',
|
||||
'done': '完了'
|
||||
};
|
||||
return labels[status] || status;
|
||||
}
|
||||
|
||||
// 通知表示(GitHub風)
|
||||
showNotification(message, type = 'info') {
|
||||
// 簡易的な通知実装
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `notification notification-${type}`;
|
||||
notification.textContent = message;
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 12px 16px;
|
||||
background: #0366d6;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
z-index: 1000;
|
||||
max-width: 300px;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
`;
|
||||
|
||||
// タイプ別スタイル
|
||||
const colors = {
|
||||
'success': '#28a745',
|
||||
'warning': '#ffd33d',
|
||||
'error': '#d73a49',
|
||||
'info': '#0366d6'
|
||||
};
|
||||
if (colors[type]) {
|
||||
notification.style.background = colors[type];
|
||||
}
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// 3秒後に削除
|
||||
setTimeout(() => {
|
||||
notification.remove();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// フォームクリア
|
||||
clearForm() {
|
||||
document.getElementById('addTaskForm').reset();
|
||||
}
|
||||
|
||||
// データ永続化
|
||||
saveTasks() {
|
||||
localStorage.setItem('github-learning-tasks', JSON.stringify(this.tasks));
|
||||
localStorage.setItem('github-learning-task-counter', this.taskIdCounter.toString());
|
||||
}
|
||||
|
||||
loadTasks() {
|
||||
const saved = localStorage.getItem('github-learning-tasks');
|
||||
return saved ? JSON.parse(saved) : [];
|
||||
}
|
||||
|
||||
getNextTaskId() {
|
||||
const saved = localStorage.getItem('github-learning-task-counter');
|
||||
return saved ? parseInt(saved) : 1;
|
||||
}
|
||||
|
||||
// デモデータ生成(学習用)
|
||||
generateSampleTasks() {
|
||||
const sampleTasks = [
|
||||
{
|
||||
id: this.taskIdCounter++,
|
||||
title: 'GitHub Issues の基本操作を学習',
|
||||
description: 'Issue の作成、編集、クローズの方法を実践で学ぶ',
|
||||
category: 'documentation',
|
||||
priority: 'high',
|
||||
status: 'todo',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: this.taskIdCounter++,
|
||||
title: 'Pull Request のワークフローを実践',
|
||||
description: 'ブランチ作成からマージまでの一連の流れを体験',
|
||||
category: 'feature',
|
||||
priority: 'high',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date(Date.now() - 86400000).toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: this.taskIdCounter++,
|
||||
title: 'GitHub Actions でCI/CDを設定',
|
||||
description: '自動テストとデプロイの仕組みを構築',
|
||||
category: 'improvement',
|
||||
priority: 'medium',
|
||||
status: 'todo',
|
||||
createdAt: new Date(Date.now() - 172800000).toISOString(),
|
||||
updatedAt: new Date(Date.now() - 172800000).toISOString()
|
||||
}
|
||||
];
|
||||
|
||||
if (this.tasks.length === 0) {
|
||||
this.tasks = sampleTasks;
|
||||
this.saveTasks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// アプリケーション開始
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const taskManager = new TaskManager();
|
||||
|
||||
// デモデータ生成(初回のみ)
|
||||
taskManager.generateSampleTasks();
|
||||
taskManager.renderTasks();
|
||||
taskManager.updateStats();
|
||||
|
||||
// グローバルに公開(デバッグ用)
|
||||
window.taskManager = taskManager;
|
||||
|
||||
console.log('🎯 Personal Task Manager が起動しました!');
|
||||
console.log('GitHub機能学習用のタスク管理アプリです。');
|
||||
console.log('タスクを追加して、GitHub の各機能を実践的に学習しましょう!');
|
||||
});
|
||||
377
practice-project/style.css
Normal file
377
practice-project/style.css
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/* Personal Task Manager - GitHub学習用スタイル */
|
||||
|
||||
:root {
|
||||
--primary-color: #0366d6;
|
||||
--secondary-color: #586069;
|
||||
--success-color: #28a745;
|
||||
--warning-color: #ffd33d;
|
||||
--danger-color: #d73a49;
|
||||
--background-color: #f6f8fa;
|
||||
--card-background: #ffffff;
|
||||
--border-color: #e1e4e8;
|
||||
--text-primary: #24292e;
|
||||
--text-secondary: #586069;
|
||||
--shadow: 0 1px 3px rgba(27, 31, 35, 0.12);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* ヘッダー */
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding: 20px;
|
||||
background: var(--card-background);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
header p {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* セクション共通スタイル */
|
||||
section {
|
||||
background: var(--card-background);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
section h2 {
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-primary);
|
||||
border-bottom: 2px solid var(--primary-color);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
/* フォーム */
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group textarea,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group textarea:focus,
|
||||
.form-group select:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.1);
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #0256cc;
|
||||
}
|
||||
|
||||
/* フィルタ */
|
||||
.filters {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* タスクボード */
|
||||
.task-board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.task-column {
|
||||
background: var(--background-color);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.task-column h3 {
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background: var(--card-background);
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
border: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.task-list {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
/* タスクカード */
|
||||
.task-card {
|
||||
background: var(--card-background);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
cursor: grab;
|
||||
transition: all 0.2s;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.task-card:hover {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 2px 8px rgba(27, 31, 35, 0.15);
|
||||
}
|
||||
|
||||
.task-card:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.task-id {
|
||||
font-family: 'Monaco', 'Menlo', monospace;
|
||||
background: var(--background-color);
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.task-priority {
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.task-priority.high {
|
||||
background: var(--danger-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.task-priority.medium {
|
||||
background: var(--warning-color);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.task-priority.low {
|
||||
background: var(--success-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.task-category {
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.task-category.feature {
|
||||
background: #28a745;
|
||||
}
|
||||
|
||||
.task-category.bug {
|
||||
background: #d73a49;
|
||||
}
|
||||
|
||||
.task-category.improvement {
|
||||
background: #0366d6;
|
||||
}
|
||||
|
||||
.task-category.documentation {
|
||||
background: #6f42c1;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
margin-bottom: 8px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.task-description {
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.task-actions button {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.task-actions button:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
/* 統計 */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: var(--background-color);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.stat-card h3 {
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.stat-card span {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* フッター */
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding: 20px;
|
||||
color: var(--text-secondary);
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* ドラッグ&ドロップ */
|
||||
.task-column.drag-over {
|
||||
background: rgba(3, 102, 214, 0.1);
|
||||
border: 2px dashed var(--primary-color);
|
||||
}
|
||||
|
||||
.task-card.dragging {
|
||||
opacity: 0.5;
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
|
||||
/* レスポンシブ */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.task-board {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.filters {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* アニメーション */
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.task-card {
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* GitHub風のボタン */
|
||||
.github-btn {
|
||||
background: #f6f8fa;
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.github-btn:hover {
|
||||
background: #e1e4e8;
|
||||
}
|
||||
|
||||
.github-btn.primary {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.github-btn.success {
|
||||
background: var(--success-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.github-btn.danger {
|
||||
background: var(--danger-color);
|
||||
color: white;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue