-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
220 lines (201 loc) · 11.2 KB
/
index.html
File metadata and controls
220 lines (201 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<!DOCTYPE html>
<html lang="en" class="dark"> <!-- Starts in dark mode by default -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MODE- 4040 Motion Graphics II</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;900&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
}
/* Add a smooth transition for color changes */
.card-container {
transition: all 0.3s ease;
}
</style>
<script>
// Check for saved theme preference in localStorage and apply it
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
</head>
<body class="bg-slate-100 dark:bg-slate-900 text-slate-800 dark:text-slate-200 antialiased">
<div id="app" class="container mx-auto p-4 sm:p-6 md:p-8">
<header class="relative text-center mb-8 md:mb-12">
<h1 class="text-3xl sm:text-4xl md:text-5xl font-bold text-indigo-600 dark:text-indigo-400">MODE-4040 Motion Graphics II</h1>
<p class="text-slate-500 dark:text-slate-400 mt-2 text-base sm:text-lg">Your project priorities at a glance.</p>
<!-- Dark Mode Toggle -->
<button id="theme-toggle" type="button" class="absolute top-0 right-0 text-slate-500 dark:text-slate-400 hover:bg-slate-200 dark:hover:bg-slate-700 focus:outline-none focus:ring-4 focus:ring-slate-200 dark:focus:ring-slate-700 rounded-lg text-sm p-2.5">
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 5.05A1 1 0 003.636 6.464l.707.707a1 1 0 001.414-1.414l-.707-.707zM3 11a1 1 0 100-2H2a1 1 0 100 2h1z"></path></svg>
</button>
</header>
<main id="milestones-container" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 md:gap-6">
<!-- Upcoming milestones will be dynamically inserted here -->
</main>
</div>
<script>
// --- CONFIGURATION ---
// This is where you paste the code from the "Project Data Generator" tool.
const projects = [
{
name: "Project 1: Projection Mapping",
kickOff: "8/26/2025",
brainstorming: "8/26/2025", // first project exception
roughCut: "9/2/2025",
finalRender: "9/9/2025"
},
{
name: "Project 2: Unreal Cloners/Effectors",
kickOff: "9/11/2025",
brainstorming: "9/9/2025", // due on P1 final
roughCut: "9/18/2025",
finalRender: "9/25/2025"
},
{
name: "Project 3: CAD Product Explainer",
kickOff: "9/30/2025",
brainstorming: "9/25/2025", // due on P2 final
roughCut: "10/7/2025",
finalRender: "10/16/2025"
},
{
name: "Project 4: AI Logo Design",
kickOff: "10/21/2025",
brainstorming: "10/16/2025", // due on P3 final
roughCut: "10/28/2025",
finalRender: "11/4/2025"
},
{
name: "Project 5: Commercial/PSA",
kickOff: "11/6/2025",
brainstorming: "11/4/2025", // due on P4 final
roughCut: "11/13/2025",
finalRender: "11/20/2025"
},
{
name: "Final: Demo Reel",
kickOff: "12/2/2025",
brainstorming: "11/20/2025", // due on P5 final
roughCut: "",
finalRender: "12/9/2025"
}
];
// --- APPLICATION LOGIC ---
document.addEventListener('DOMContentLoaded', () => {
const milestonesContainer = document.getElementById('milestones-container');
const today = new Date();
today.setHours(0, 0, 0, 0);
const allMilestones = [];
projects.forEach(project => {
if (project.brainstorming) allMilestones.push({ projectName: project.name, milestoneName: 'Brainstorming', date: project.brainstorming });
if (project.roughCut) allMilestones.push({ projectName: project.name, milestoneName: 'Rough Cut', date: project.roughCut });
if (project.finalRender) allMilestones.push({ projectName: project.name, milestoneName: 'Final Render', date: project.finalRender });
});
const getDaysRemaining = (dateString) => {
if (!dateString) return null;
const targetDate = new Date(dateString);
const timeDiff = targetDate.getTime() - today.getTime();
return Math.ceil(timeDiff / (1000 * 3600 * 24));
};
const upcomingMilestones = allMilestones
.filter(milestone => getDaysRemaining(milestone.date) >= 0)
.sort((a, b) => new Date(a.date) - new Date(b.date));
// **MODIFIED LOGIC**: Only take the first 9 items (1 + 2 + 6)
const itemsToRender = upcomingMilestones.slice(0, 9);
if (itemsToRender.length > 0) {
itemsToRender.forEach((milestone, index) => {
const days = getDaysRemaining(milestone.date);
const card = createMilestoneCard(milestone, days, index);
milestonesContainer.innerHTML += card;
});
} else {
milestonesContainer.innerHTML = `<p class="text-center text-slate-500 dark:text-slate-400 col-span-full">All projects are complete. Great job!</p>`;
}
function getColorInfo(days) {
if (days < 5) {
return { numberClass: 'text-red-500 dark:text-red-400', message: 'Due Soon!' };
}
if (days <= 10) {
return { numberClass: 'text-yellow-500 dark:text-yellow-400', message: 'days remaining' };
}
const greenShade = Math.min(8, Math.floor(days / 10) + 4);
return {
numberClass: `text-green-${greenShade}00 dark:text-green-${greenShade-1}00`,
message: 'days remaining'
};
}
function createMilestoneCard(milestone, days, index) {
const colorInfo = getColorInfo(days);
let layoutClasses, sizeClasses;
let numberText = (days === 0) ? '!' : days;
const formattedDate = new Date(milestone.date).toLocaleDateString('en-US', {
month: 'long', day: 'numeric', year: 'numeric'
});
let cardStyles;
if (milestone.milestoneName === 'Final Render') {
cardStyles = 'bg-slate-50 dark:bg-slate-800/50 ring-1 ring-indigo-300 dark:ring-indigo-500';
} else {
cardStyles = 'bg-white dark:bg-slate-800';
}
if (index === 0) { // Largest card
layoutClasses = 'col-span-full md:col-span-4 lg:col-span-6';
sizeClasses = { padding: 'p-8', title: 'text-2xl', subtitle: 'text-base', date: 'text-sm text-slate-400 dark:text-slate-500 mb-4', number: 'text-9xl', message: 'text-lg mt-2' };
} else if (index === 1 || index === 2) { // Medium cards
layoutClasses = 'col-span-full sm:col-span-1 md:col-span-2 lg:col-span-3';
sizeClasses = { padding: 'p-6', title: 'text-xl', subtitle: 'text-sm', date: 'text-xs text-slate-400 dark:text-slate-500 mb-3', number: 'text-7xl', message: 'text-base mt-1' };
} else { // Smallest cards
layoutClasses = 'col-span-full sm:col-span-1 md:col-span-2 lg:col-span-1';
sizeClasses = { padding: 'p-3', title: 'text-xs font-bold', subtitle: 'text-xs', date: 'text-[10px] text-slate-400 dark:text-slate-500 mb-1', number: 'text-5xl', message: 'text-xs' };
}
return `
<div class="card-container ${layoutClasses}">
<div class="rounded-2xl shadow-lg flex flex-col items-center justify-center text-center h-full ${sizeClasses.padding} ${cardStyles}">
<h2 class="font-semibold text-indigo-600 dark:text-indigo-400 ${sizeClasses.title}">${milestone.projectName}</h2>
<p class="text-slate-600 dark:text-slate-300 ${sizeClasses.subtitle}">${milestone.milestoneName}</p>
<p class="${sizeClasses.date}">${formattedDate}</p>
<div class="font-black ${colorInfo.numberClass} ${sizeClasses.number}">${numberText}</div>
<p class="text-slate-500 dark:text-slate-400 ${sizeClasses.message}">${colorInfo.message}</p>
</div>
</div>
`;
}
// --- Dark Mode Toggle Logic ---
const themeToggleBtn = document.getElementById('theme-toggle');
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
if (localStorage.getItem('theme') === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
themeToggleLightIcon.classList.remove('hidden');
} else {
themeToggleDarkIcon.classList.remove('hidden');
}
themeToggleBtn.addEventListener('click', function() {
themeToggleDarkIcon.classList.toggle('hidden');
themeToggleLightIcon.classList.toggle('hidden');
if (localStorage.getItem('theme')) {
if (localStorage.getItem('theme') === 'light') {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
} else {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
}
}
});
});
</script>
</body>
</html>