Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Kvnbbg/a91f95a631b75db0582720afa4158f62 to your computer and use it in GitHub Desktop.
Save Kvnbbg/a91f95a631b75db0582720afa4158f62 to your computer and use it in GitHub Desktop.
Pomodoro Timer with Task Management, Login, and Registration

Pomodoro Timer with Task Management, Login, and Registration

This Vue.js application combines a Pomodoro timer with task management, and includes fake login and registration forms for demonstration purposes. The app is fully translated into English, French, Spanish, and German, and includes a floating bubble advertisement linking to job opportunities. It also features a responsive design that adapts to light or dark themes based on user preference. Ideal for enhancing productivity, this all-in-one app helps manage study sessions efficiently with an intuitive interface and engaging notifications. Check out my LinkedIn for more details.

A Pen by Kevin Marville on CodePen.

License.

<template>
<div id="app">
<nav>
<ul>
<li><a href="#" @click="showPage('pomodoro')">Pomodoro Timer</a></li>
<li><a href="#" @click="showPage('login')">Login</a></li>
<li><a href="#" @click="showPage('register')">Register</a></li>
</ul>
</nav>
<div v-if="page === 'pomodoro'">
<h1>Pomodoro Timer</h1>
<div class="timer">
<div>{{ formattedTime }}</div>
<button @click="startTimer">{{ isRunning ? "Pause" : "Start" }}</button>
<button @click="resetTimer">Reset</button>
</div>
<div class="tasks">
<h2>Tasks</h2>
<ul>
<li v-for="(task, index) in tasks" :key="index">
<input
type="checkbox"
v-model="task.completed"
@change="completeTask(index)"
/>
{{ task.name }}
<button @click="deleteTask(index)">Delete</button>
</li>
</ul>
<input
v-model="newTask"
placeholder="New task"
@keyup.enter="addTask"
/>
<button @click="addTask">Add Task</button>
</div>
<footer>
<p>
Check out my
<a href="https://linkedin.com/in/kevin-marville" target="_blank">
LinkedIn
</a>
</p>
</footer>
</div>
<div v-if="page === 'login'">
<h1>Login</h1>
<input v-model="username" placeholder="Username" />
<input type="password" v-model="password" placeholder="Password" />
<button @click="login">Login</button>
<p v-if="loginMessage">{{ loginMessage }}</p>
</div>
<div v-if="page === 'register'">
<h1>Register</h1>
<input v-model="newUsername" placeholder="Username" />
<input type="password" v-model="newPassword" placeholder="Password" />
<button @click="register">Register</button>
<p v-if="registerMessage">{{ registerMessage }}</p>
</div>
<div class="floating-bubble">
<a href="https://kvnbbg-creations.io" target="_blank">
<span role="img" aria-label="panda">🐼</span>
Je cherche de nouvelles opportunités sur le marché du travail
aujourd'hui
</a>
</div>
<audio ref="audio" :src="alarmSound"></audio>
</div>
</template>
<script>
export default {
data() {
return {
page: "pomodoro",
time: 25 * 60,
timer: null,
isRunning: false,
tasks: [],
newTask: "",
username: "",
password: "",
loginMessage: "",
newUsername: "",
newPassword: "",
registerMessage: "",
alarmSound: "https://www.soundjay.com/button/beep-07.wav"
};
},
computed: {
formattedTime() {
const minutes = Math.floor(this.time / 60);
const seconds = this.time % 60;
return `${minutes
.toString()
.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
}
},
methods: {
showPage(page) {
this.page = page;
},
startTimer() {
if (this.isRunning) {
this.pauseTimer();
} else {
this.isRunning = true;
this.timer = setInterval(() => {
if (this.time > 0) {
this.time--;
} else {
this.stopTimer();
this.playAlarm();
}
}, 1000);
}
},
pauseTimer() {
clearInterval(this.timer);
this.isRunning = false;
},
stopTimer() {
clearInterval(this.timer);
this.isRunning = false;
},
resetTimer() {
this.stopTimer();
this.time = 25 * 60;
},
addTask() {
if (this.newTask.trim()) {
this.tasks.push({ name: this.newTask, completed: false });
this.newTask = "";
}
},
deleteTask(index) {
this.tasks.splice(index, 1);
},
completeTask(index) {
if (this.tasks[index].completed) {
this.deleteTask(index);
}
},
playAlarm() {
this.$refs.audio.play();
},
login() {
if (this.username && this.password) {
this.loginMessage = `Bravo! ${this.username}, félicitations, vous avez réussi à vous connecter.`;
} else {
this.loginMessage = "Veuillez remplir tous les champs.";
}
},
register() {
if (this.newUsername && this.newPassword) {
this.registerMessage = `Bravo! ${this.newUsername}, félicitations, vous vous êtes inscrit avec succès.`;
} else {
this.registerMessage = "Veuillez remplir tous les champs.";
}
}
},
beforeDestroy() {
this.stopTimer();
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 90%;
max-width: 600px;
margin: 20px auto;
transition: all 0.3s ease;
}
nav ul {
list-style: none;
padding: 0;
display: flex;
justify-content: center;
margin-bottom: 20px;
}
nav li {
margin: 0 10px;
}
nav a {
text-decoration: none;
color: #4fc08d;
font-weight: bold;
}
h1 {
font-size: 2em;
margin-bottom: 20px;
}
.timer {
font-size: 2em;
margin-bottom: 20px;
}
button {
background: #4fc08d;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
transition: background 0.3s;
}
button:hover {
background: #3da76f;
}
.tasks {
text-align: left;
margin-top: 20px;
}
.tasks h2 {
font-size: 1.5em;
margin-bottom: 10px;
}
.tasks ul {
list-style: none;
padding: 0;
}
.tasks li {
background: #ececec;
border: 1px solid #ddd;
margin: 10px 0;
padding: 10px;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.tasks input[type="text"] {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
width: calc(100% - 40px);
}
.tasks input[type="checkbox"] {
margin-right: 10px;
}
footer {
margin-top: 20px;
}
footer a {
color: #4fc08d;
text-decoration: none;
}
.floating-bubble {
position: fixed;
bottom: 20px;
right: 20px;
background: #4fc08d;
color: #fff;
padding: 10px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
animation: float 5s ease-in-out infinite;
}
.floating-bubble a {
color: #fff;
text-decoration: none;
}
@keyframes float {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment