Skip to content

Commit

Permalink
add a new project tiny to track project progress
Browse files Browse the repository at this point in the history
  • Loading branch information
haochengxu committed Jan 13, 2024
1 parent cf36682 commit 8de55fd
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 7 deletions.
29 changes: 22 additions & 7 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Link from 'next/link';
import { useState, useEffect } from 'react';

const IMAGE_NUMBER = 3;

Expand All @@ -7,20 +8,34 @@ const indexStyle = {
position: 'relative' as 'relative',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundImage: `url('/assets/photo/${Math.floor(Math.random() * IMAGE_NUMBER) + 1}.JPG')`,
height: '100vh',
};

const blurStyle = {
backdropFilter: 'blur(5px)',
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}

export default function Index() {
const [backgroundImage, setBackgroundImage] = useState('');

useEffect(() => {
setBackgroundImage(`url('/assets/photo/${Math.floor(Math.random() * IMAGE_NUMBER) + 1}.JPG')`);
}, []);

return (
<>
<div className="min-h-screen" style={indexStyle} >
<div className="min-h-screen" style={{...indexStyle, backgroundImage}} >
<h3 className="text-3xl mb-3 leading-snug">
<Link
href="/blog"
>
Blog
</Link>
<div className="pt-10 pl-5 pb-5 rounded" style={blurStyle}>
<Link href="/blog" className="text-orange-500 hover:text-orange-700">
Blog
</Link>
<br />
<Link href="/play-lab" className="text-green-500 hover:text-green-700">
PlayLab
</Link>
</div>
</h3>
</div>
</>
Expand Down
17 changes: 17 additions & 0 deletions pages/play-lab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// pages/play-lab.tsx 或 pages/play-lab/index.tsx

import React from 'react'
import Link from 'next/link'

const PlayLab = () => {
return (
<div>
<h1>Welcome to PlayLab!</h1>
<Link href="/projects/tiny">
Go to Project Tracker
</Link>
</div>
)
}

export default PlayLab
188 changes: 188 additions & 0 deletions pages/projects/tiny/IterationDetail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import React, { useState } from "react";
import { Project } from "./types";

interface Props {
// Define the props for your component here
project: Project;
updateProject: (project: Project) => void;
}

const IterationDetail: React.FC<Props> = ({
project: currentProject,
updateProject,
}) => {
const [timerId, setTimerId] = useState(null);
const [elapsedTime, setElapsedTime] = useState(0);
const [showPanel, setShowPanel] = useState(false);

const [startTime, setStartTime] = useState('');
const [isPaused, setIsPaused] = useState(false);
const [rating, setRating] = useState(1);
const [notes, setNotes] = useState("");

const startTimer = () => {
if (timerId !== null) return;
const id = setInterval(() => {
setElapsedTime((time) => time + 1);
}, 1000);
setTimerId(id);
};

const resumeTimer = () => {
const id = setInterval(() => {
setElapsedTime((prevTime) => prevTime + 1);
}, 1000);
setTimerId(id);
};

const pauseTimer = () => {
clearInterval(timerId);
setTimerId(null);
};

const submitIteration = () => {
if (!window.confirm(`你确定要完成这次${currentProject.name}吗?`)) {
return;
}
const newIteration = {
startTime: startTime,
elapsedTime: elapsedTime,
notes: notes,
quality: rating,
tags: [],
};

currentProject.iterations = currentProject.iterations ? [...currentProject.iterations, newIteration] : [newIteration];
updateProject(Object.assign({}, currentProject));
};

return (
<div>
{currentProject ? (
<>
<h3 className="text-2xl font-bold text-blue-600">
{currentProject && currentProject.name}
</h3>{" "}
<div className="summary">
{currentProject.iterations && (
<p className="text-zinc-500">
<span className="pr-5">
累积时间:
{Math.floor(
currentProject.iterations.reduce((prev, iteration) => {
return Number(iteration.elapsedTime) + prev;
}, 0) / 3600
)}
小时
</span>
<span>
累积次数:
{currentProject.iterations.length}
</span>
</p>
)}
</div>
<button
className="bg-blue-500 text-white px-4 text-sm py-2 rounded"
onClick={() => setShowPanel(!showPanel)}
>
开始{currentProject.name}
</button>
{showPanel && (
<div className="bg-gray-100 p-4 shadow-lg rounded mt-5">
<div className="text-zinc-500">
<button
className="bg-blue-500 text-white px-4 py-2 rounded"
onClick={() => {
if (startTime != null) return;
setStartTime(new Date().toISOString());
startTimer();
}}
>
开始计时
</button>
{startTime && (
<input
type="text"
readOnly
value={startTime.toLocaleString()}
className="mt-2 p-1 border rounded"
/>
)}
<button
className="ml-5 bg-yellow-500 text-white px-4 py-2 rounded"
onClick={() => {
if (!startTime) return;
setIsPaused(!isPaused);
if (isPaused) {
resumeTimer();
} else {
pauseTimer();
}
}}
>
{isPaused ? "恢复" : "暂停"}
</button>
<p className="mt-5 mb-5">
评分:
<input
type="number"
min="1"
max="5"
value={rating}
onChange={(e) => setRating(Number(e.target.value))}
/>
</p>
<p className="mt-5 mb-5">
笔记:
<textarea
value={notes}
onChange={(e) => setNotes(e.target.value)}
className="mt-2 p-1 border rounded"
/>
</p>
<button
className="mt-5 bg-green-500 text-white px-4 py-2 rounded"
onClick={submitIteration}
>
完成一次{currentProject.name}
</button>
<p>
本次累积时间:{Math.floor(elapsedTime / 3600)}小时
{Math.floor(elapsedTime / 60) % 60}分钟{elapsedTime % 60}
</p>
</div>
</div>
)}
{currentProject.iterations &&
currentProject.iterations.map((iteration, index) => {
return (
<div
key={index}
className="bg-white p-4 shadow rounded mt-5 border-gray-400"
>
<p className="text-lg font-bold text-blue-600">
{index + 1}{currentProject.name}
</p>
<p className="text-zinc-500">
开始时间:{" "}
{iteration.startTime
? `${new Date(iteration.startTime).toLocaleDateString()} ${new Date(iteration.startTime).toLocaleTimeString()}`
: "未知"}
</p>
<p className="text-zinc-500">
持续时间: {Math.floor(iteration.elapsedTime / 3600)} 小时{" "}
{Math.floor((iteration.elapsedTime % 3600) / 60)} 分钟
</p>
<p className="text-zinc-500">评价: {iteration.quality}</p>
<p className="text-zinc-500">小记: {iteration.notes}</p>
</div>
);
})}
</>
) : null}
</div>
);
};

export default IterationDetail;
91 changes: 91 additions & 0 deletions pages/projects/tiny/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React, { useState, useEffect } from "react";
import { Project } from "./types";
import IterationDetail from "./IterationDetail";

const IterationTracker: React.FC = () => {
const [projects, setProjects] = useState<Project[]>([]);
const [projectName, setProjectName] = useState("");
const [currentProjectId, setCurrentProjectId] = useState<string | null>(null);

const updateProject = (updatedProject: Project) => {
setProjects((prevProjects) => {
return prevProjects.map((project) => {
if (project.id === updatedProject.id) {
return {
...project,
...updatedProject,
};
}
return project;
});
});
localStorage.setItem("projects", JSON.stringify(projects));
};

const generateId = () => {
return Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
};

const currentProject = projects.find(
(project) => project.id === currentProjectId
);

const handleAddProject = () => {
if (projectName === "") return;
setProjects([...projects, { name: projectName, id: generateId() }]);
setProjectName("");
};

useEffect(() => {
const savedProjects = localStorage.getItem('projects');
if (savedProjects) {
setProjects(JSON.parse(savedProjects));
}
}, []);

return (
<div className="bg-white min-h-screen max-w-[1000px] mx-auto">
<h1 className="text-center text-4xl font-bold text-blue-500 pt-8">
毫末
</h1>
<div className="flex justify-between mt-10">
<div className="project-list flex-1 ml-10 min-w-[300px]">
<input
className="border border-gray-300 px-3 py-2 rounded "
value={projectName}
onChange={(e) => setProjectName(e.target.value)}
placeholder="项目名称"
/>
<button
className="ml-5 bg-blue-500 text-white px-4 py-2 rounded"
onClick={handleAddProject}
>
添加项目
</button>
<ul className="list-none mt-10 max-w-xs">
{projects.map((project, index) => (
<li
key={index}
className="p-2 border-b border-gray-200 bg-blue-200 shadow my-2 rounded text-gray-900"
onClick={() => {
setCurrentProjectId(project.id);
}}
>
{project.name}
</li>
))}
</ul>
</div>

<div className="project-detail flex-1 ml-20 min-w-[300px]">
<IterationDetail
project={currentProject}
updateProject={updateProject}
></IterationDetail>
</div>
</div>
</div>
);
};

export default IterationTracker;
16 changes: 16 additions & 0 deletions pages/projects/tiny/types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export interface Iteration {
elapsedTime: number;
quality: number;
notes: string;
tags: String[];
startTime?: string;
endTime?: Date;
}

export interface Project {
name: string;
id: string;
targetTime?: number;
targetNumberOfIterations?: number;
iterations?: Array<Iteration>;
}

0 comments on commit 8de55fd

Please sign in to comment.