diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..641ca762e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,22 @@
+{
+ "workbench.colorCustomizations": {
+ "activityBar.activeBackground": "#65c89b",
+ "activityBar.background": "#65c89b",
+ "activityBar.foreground": "#15202b",
+ "activityBar.inactiveForeground": "#15202b99",
+ "activityBarBadge.background": "#945bc4",
+ "activityBarBadge.foreground": "#e7e7e7",
+ "commandCenter.border": "#15202b99",
+ "sash.hoverBorder": "#65c89b",
+ "statusBar.background": "#42b883",
+ "statusBar.foreground": "#15202b",
+ "statusBarItem.hoverBackground": "#359268",
+ "statusBarItem.remoteBackground": "#42b883",
+ "statusBarItem.remoteForeground": "#15202b",
+ "titleBar.activeBackground": "#42b883",
+ "titleBar.activeForeground": "#15202b",
+ "titleBar.inactiveBackground": "#42b88399",
+ "titleBar.inactiveForeground": "#15202b99"
+ },
+ "peacock.color": "#42b883"
+}
diff --git a/README.md b/README.md
index 31466b54c..2cb803605 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,49 @@
-# Final Project
+# Randify πΎ
-Replace this readme with your own information about your project.
+Fullstack project built by Nicolinabl & Demijuls
-Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
+Live app: https://julia-nicolina.netlify.app
+backend: https://final-project-task-randomizer.onrender.com
-## The problem
+Your task-list shouldn't feel like a boss battle before you even start. Let us do your mental labour! We'll pick the quest. You just win it.
-Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
+## What is it?
-## View it live
+Doing tasks is boring. Choosing a task is a task. Too many tasks gets overwhelming β> nothing gets done.
+Randify makes your endless task-list fun and easy. Tell the app how much time you have available, and a random task (or as we like to call it: quest) is chosen for you according to how much time you have available. No decision fatigue, no excuses. Complete your quest everyday and keep your avatar happy. Keep a streak and share with your friends.
-Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
\ No newline at end of file
+## Features
+
+π² Quest randomizer: enter how many minutes you have, get a quest that fits
+π Quest library: don't know what to do? Browse our library and add pre-made quests to your personal list
+β
Complete quests: check off quests and keep your avatar happy
+π Mood avatar: your avatar's mood reflects your streak (sad β happy β super happy)
+π― Friends feed: see what quests others have completed
+π Kudos: give kudos to friends for finishing their quests
+π₯ Streaks: keep completing quests to build your streak
+
+## Tech Stack
+
+### Frontend
+
+- React
+- React Router for navigation
+- Zustand for global state management
+- Styled Components for styling
+- Framer Motion for animations
+- DiceBear for mood-based avatar generation
+- Lottie for animations
+
+## Backend
+
+- Node.js with Express
+- MongoDB with Mongoose
+- JWT authentication
+
+- bcrypt for password hashing
+
+## Deployment
+
+Frontend hosted on Netlify
+Backend hosted on Render
+Database hosted on MongoDB Atlas
diff --git a/backend/authMiddleware.js b/backend/authMiddleware.js
new file mode 100644
index 000000000..dcf95b7d7
--- /dev/null
+++ b/backend/authMiddleware.js
@@ -0,0 +1,24 @@
+import { User } from "./schemas.js";
+
+// FIXME - add error handling, put accesstoken req into variable
+export const authentificateUser = async (req, res, next) => {
+ try {
+ const token = req.header("Authorization");
+ if (!token) {
+ return res
+ .status(401)
+ .json({ success: false, message: "User is logged out" });
+ }
+ const user = await User.findOne({ accessToken: token });
+ if (user) {
+ req.user = user;
+ next();
+ } else {
+ res.status(401).json({ loggedOut: true });
+ }
+ } catch (err) {
+ res
+ .status(500)
+ .json({ message: "Internal server error", error: err.errors });
+ }
+};
diff --git a/backend/controllers/friendController.js b/backend/controllers/friendController.js
new file mode 100644
index 000000000..1db5a4651
--- /dev/null
+++ b/backend/controllers/friendController.js
@@ -0,0 +1,76 @@
+import mongoose from "mongoose";
+import { Quest } from "../schemas.js";
+import { User } from "../schemas.js";
+
+import "dotenv/config";
+
+// ---- All friends routes -----
+
+// TODO ---- FRIENDS FEED ----
+
+// FIXME (add friends only to filter) MUST ---- Friends Feed page >>>>> only for auth users( if it's a friends page, otherwise for everybody?)
+// What it does:
+// - req auth,
+// - fetches done quests from other users
+// - sorts them by done date (desc)
+// - excludes current users quests
+// - populates users info
+const getFriendsFeed = async (req, res) => {
+ const { _id: userId } = req.user;
+
+ //building query to find all quests made by other users with filter:
+ const query = Quest.find({
+ done: true,
+ doneAt: { $ne: null },
+ createdBy: { $ne: userId },
+ })
+ .sort({ doneAt: -1 })
+ .limit(20);
+
+ try {
+ //returning all friends quests filtered with query, and populating only fields we need
+ const friendsQuests = await query
+ .populate({ path: "createdBy", select: "name moodUrl" })
+ .select("message category timeNeeded doneAt createdBy kudos");
+
+ //console.log(`quests: ${friendsQuests}`);
+ if (!friendsQuests.length) {
+ return res.status(200).json({
+ success: true,
+ response: [],
+ message: "Nobody completed quests yet",
+ });
+ }
+ return res.status(200).json(friendsQuests);
+ } catch (err) {
+ return res.status(500).json({
+ success: false,
+ message: "Server error, couldn't fetch friends quests",
+ error: err.message,
+ });
+ }
+};
+
+// FIXME NICE+ ---- Find a friend bi ID page
+const findUserById = async (req, res) => {
+ const friend = await User.findById(req.params.id); //search through users ids in database?
+ res.json(friend);
+};
+
+// TODO NICE+ ---- Find a friend by :name page
+const findUserdByName = async (req, res) => {
+ const friend = await User.findOne(req.params.name); //search through users names in database?
+ res.json(friend);
+};
+
+// FIXME NICE+ ---- Remove a friend >>>>> only for authorised users for their feed
+const removeFriend = async (req, res) => {
+ console.log("delete friend");
+};
+
+export default {
+ getFriendsFeed,
+ findUserById,
+ findUserdByName,
+ removeFriend,
+};
diff --git a/backend/controllers/questController.js b/backend/controllers/questController.js
new file mode 100644
index 000000000..b8b0f318d
--- /dev/null
+++ b/backend/controllers/questController.js
@@ -0,0 +1,331 @@
+import mongoose from "mongoose";
+
+import { Quest } from "../schemas.js";
+
+import { LibraryQuest } from "../schemas.js";
+import quests from "../quests.json" with { type: "json" };
+
+import "dotenv/config";
+import { json, response } from "express";
+
+// ---- All Quest routes: ----
+
+// --- Create a quest ----
+// add: redirecting for not authorized (EXTRA)
+const createQuestUnAuth = async (req, res) => {
+ const { message, timeNeeded, category, deadline } = req.body;
+ //console.log("createdBy:", req.user?.id);
+
+ try {
+ const quest = await new Quest({
+ message,
+ timeNeeded,
+ category,
+ deadline,
+ createdBy: req.user._id,
+ }).save();
+ res.status(201).json(quest);
+ } catch (err) {
+ res.status(500).json({
+ message: "Couldn't save quest, please try again",
+ error: err.errors,
+ });
+ }
+};
+
+// -------- Give kudos ----------
+const giveKudos = async (req, res) => {
+ //console.log("Give kudos");
+ const { id } = req.params;
+ const userId = req.user._id;
+
+ if (!mongoose.Types.ObjectId.isValid(id)) {
+ return res
+ .status(404)
+ .json({ success: false, response: "Quest id is invalid" });
+ }
+
+ try {
+ const update = { $inc: { kudos: 1 }, $push: { kudosByUser: userId } };
+ const options = { new: true, runValidators: true };
+
+ const addKudos = await Quest.findOneAndUpdate(
+ { _id: id, kudosByUser: { $ne: userId } },
+ update,
+ options,
+ );
+
+ if (!addKudos) {
+ return res.status(400).json({
+ success: false,
+ message: "Can't give cudos more than once to the same quest",
+ });
+ }
+ return res.status(200).json({ success: true, response: addKudos });
+ } catch (err) {
+ return res.status(500).json({
+ success: false,
+ message: "Couldn't add kudos, try again",
+ error: err.errors,
+ });
+ }
+};
+
+// ---- Show All Quests from Library, (OBS! Doesn't requie authentication, filters on category and time <=N -----
+//Test example: http://localhost:8080/quests/library/?category=cleaning&time=20
+const showDefaultQuests = async (req, res) => {
+ try {
+ let { category, time } = req.query;
+ //const query = { category, time };
+ const query = {};
+
+ if (category) {
+ query.category = category.toLowerCase();
+ }
+ if (time) {
+ query.timeNeeded = { $lte: Number(time) };
+ }
+
+ const filteredQuests = await LibraryQuest.find(query);
+
+ if (!filteredQuests.length) {
+ return res.status(404).json({
+ success: false,
+ response: [],
+ message: "No quests",
+ });
+ }
+
+ return res
+ .status(200)
+ .json({ success: true, response: filteredQuests, message: "Success" });
+ } catch (err) {
+ return res.status(500).json({ success: false, message: err.errors });
+ }
+};
+
+// ----- Returns all user's quests, can filter on category and time <= N) >>>>> only for auth users.
+const showUserQuests = async (req, res) => {
+ let { category, time } = req.query;
+ const query = { createdBy: req.user._id };
+
+ if (category) {
+ category = category.toLowerCase();
+ query.category = category;
+ }
+ if (time) {
+ time = Number(time);
+ query.timeNeeded = { $lte: time };
+ }
+
+ try {
+ const filteredQuests = await Quest.find(query)
+ .populate({
+ path: "createdBy",
+ })
+ .sort({ createdAt: -1 });
+
+ if (!filteredQuests.length) {
+ return res.status(200).json({
+ success: true,
+ response: [],
+ message: "No quests yet",
+ });
+ }
+ return res
+ .status(200)
+ .json({ success: true, response: filteredQuests, message: "Success" });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, response: [], message: err.errors });
+ }
+};
+
+// ---- User's daily random(!) quest -------
+const getRandomQuest = async (req, res) => {
+ const { id } = req.params;
+
+ try {
+ const dailyQuest = await Quest.findById(id); //from database
+ if (!dailyQuest) {
+ return res.status(404).json({
+ success: false,
+ respons: null,
+ error: `Quest with ${id} is not found`,
+ });
+ }
+
+ res.json({ success: true, response: dailyQuest });
+ } catch (err) {
+ res.status(500).json({
+ success: false,
+ response: null,
+ error: `Something went wrong, ${id} is not valid`,
+ });
+ }
+};
+
+// ----- Duplicates a single quest from library to user's quest lits -------
+
+const duplicateQuest = async (req, res) => {
+ try {
+ const { id } = req.params;
+
+ if (!mongoose.Types.ObjectId.isValid(id)) {
+ return res
+ .status(400)
+ .json({ success: false, message: `This id ${id} is not valid` });
+ }
+
+ const defaultQuest = await LibraryQuest.findById(id);
+ if (!defaultQuest) {
+ return res
+ .status(404)
+ .json({ message: `Couldn't find quest with id ${id}` });
+ }
+
+ const questFromDefault = await Quest.create({
+ message: defaultQuest.message,
+ timeNeeded: defaultQuest.timeNeeded,
+ category: defaultQuest.category,
+ createdBy: req.user._id,
+ done: false,
+ });
+
+ return res.status(200).json({
+ success: true,
+ response: questFromDefault,
+ message: "Quest added to your list",
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, message: "Server error", errors: err.message });
+ }
+};
+
+// FIXME and query, error handling MUST ---- User's done quests /quests/done/true
+const filterUserQuests = (req, res) => {
+ const done = req.params.done;
+ const questsDone = quests.filter((item) => item.done === done);
+ res.json(questsDone);
+};
+
+// FIXME ------ Check quest as done ----- >>> only for auth users
+const checkQuestDone = async (req, res) => {
+ // get quest id from the url
+ const { id } = req.params; // get the done value (true or false), this will be sent from the frontend when quest is checked or not
+
+ const { done } = req.body; // check if the id is valid
+
+ const updateData = { done, doneAt: done ? new Date() : null };
+
+ if (typeof done !== "boolean") {
+ //console.log(typeof done);
+ //console.log(done);
+ return res
+ .status(400)
+ .json({ success: false, message: "Invalid type of data" });
+ }
+
+ if (!mongoose.Types.ObjectId.isValid(id)) {
+ return res
+ .status(404)
+ .json({ success: false, message: "Invalid quest ID" });
+ }
+
+ try {
+ // Find the quest in the database and update its done field
+ const quest = await Quest.findOneAndUpdate(
+ { _id: id, createdBy: req.user._id }, // ensures user can only update their own quests
+ updateData,
+ { new: true, runValidators: true },
+ );
+
+ if (!quest) {
+ return res
+ .status(404)
+ .json({ success: false, message: "Quest not found or unauthorized" });
+ }
+
+ res.status(200).json({ success: true, response: quest });
+ } catch (err) {
+ res.status(500).json({
+ success: false,
+ message: "Something went wrong",
+ errors: err.errors,
+ });
+ }
+};
+
+// FIXME add auth for users MUST ---- Delete one quest >>>>> only for authorised users for their list
+const deleteQuest = async (req, res) => {
+ //console.log("delete test");
+ const { id } = req.params;
+
+ if (!mongoose.Types.ObjectId.isValid(id)) {
+ return res.status(404).json({
+ error: `Couldn't find the quest with id ${id}, check if it is valid.`,
+ });
+ }
+
+ try {
+ const quest = await Quest.findByIdAndDelete(id).exec();
+ if (!quest) {
+ return res
+ .status(404)
+ .json({ error: `Couldn't find and delete quest with id ${id}` });
+ }
+
+ res.status(200).json({ message: "Quest was successfully deleted" });
+ } catch (err) {
+ res.status(400).json({ error: `Something went wrong, ${id} is not valid` });
+ }
+};
+
+//FIXME NICE+ ---- User completes task too fast confirmation >>>>> only for auth users
+const confirmCompletion = async (req, res) => {
+ console.log("Do not cheat, ok?");
+};
+
+// FIXME MUST ---- ??? is it post? Re-try to get a new quest >>>>> only for auth users
+const getNewQuest = async (req, res) => {
+ console.log("re-try");
+};
+
+// FIXME EXTRA ---- Add actual time >>>>> only for auth users
+const changeTime = async (req, res) => {
+ console.log("Add actual time");
+};
+
+// FIXME NICE+ ---- Skip a day of quests >>>>> only for auth users
+const skipDay = async (req, res) => {
+ console.log("Skip a day");
+};
+
+// FIXME NICE+ ---- Repetitive quests >>>>> only for auth users
+
+// FIXME NICE+ ---- Quests history >>>>>> only for auth users
+const showQuestHistory = async (req, res) => {
+ console.log("Shows how much user have done before");
+};
+
+// FIXME EXTRA ---- DELETE more than 1 quest at a time >>>>> only for authorised users for their list
+const deleteManyQuests = async (req, res) => {
+ console.log("Delete user's quests");
+};
+
+export default {
+ createQuestUnAuth,
+ giveKudos,
+ showDefaultQuests,
+ showUserQuests,
+ filterUserQuests,
+ checkQuestDone,
+ deleteQuest,
+ getRandomQuest,
+ duplicateQuest,
+ showQuestHistory,
+ deleteManyQuests,
+};
diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js
new file mode 100644
index 000000000..f51155aff
--- /dev/null
+++ b/backend/controllers/userController.js
@@ -0,0 +1,212 @@
+import mongoose from "mongoose";
+
+import { Quest, User } from "../schemas.js";
+
+import bcrypt from "bcrypt-nodejs";
+
+import "dotenv/config";
+import { response } from "express";
+
+// ---- All user routes ----
+
+//β
---- Register new user ----
+const registerUser = async (req, res) => {
+ try {
+ const { name, email, password } = req.body;
+
+ if (!name || !email || !password) {
+ return res.status(400).json({
+ success: false,
+ message: "All fields are required to sign up",
+ });
+ }
+
+ if (password.length < 8) {
+ return res.status(400).json({
+ success: false,
+ message: "Password must be at least 8 characters long",
+ });
+ }
+
+ //One-way encryption:
+ const salt = bcrypt.genSaltSync();
+ const hashedPass = bcrypt.hashSync(password, salt);
+
+ const user = new User({
+ name,
+ email,
+ password: hashedPass,
+ });
+
+ await user.save();
+ res.status(201).json({ id: user._id, accessToken: user.accessToken });
+ } catch (err) {
+ if (err.code === 11000) {
+ return res.status(400).json({
+ success: false,
+ message: "User with this name or email already exists",
+ });
+ }
+ res.status(400).json({
+ success: false,
+ message: "Could not create user",
+ errors: err.errors,
+ });
+ }
+};
+
+//β
---- Login with existing user ----
+const loginUser = async (req, res) => {
+ const { email, password } = req.body;
+ const user = await User.findOne({ email }); //retrieving from database by email, should be unique
+
+ if (!email || !password) {
+ return res
+ .status(400)
+ .json({ success: false, message: "All fields are required to login" });
+ }
+
+ try {
+ if (user && bcrypt.compareSync(password, user.password)) {
+ //Success
+ res.json({ userID: user._id, accessToken: user.accessToken });
+ } else {
+ //Failed:
+ //1.User doesn't exist
+ //2.Password doesn't match
+ res.status(401).json({
+ success: false,
+ message: "Smth went wrong, check your email and password",
+ });
+ }
+ } catch (err) {
+ res.status(500).json({ success: false, err: "Something went wrong" });
+ }
+};
+
+// FIXME only user with the same id can delete itself // MUST ---- Delete user -----
+const deleteUser = async (req, res) => {
+ //console.log("auth by id and delete user");
+ const { id } = req.params;
+ if (!mongoose.Types.ObjectId.isValid(id)) {
+ return res
+ .status(404)
+ .json({ success: false, message: `Couldn't find user with id ${id}` });
+ }
+
+ try {
+ const user = await User.findByIdAndDelete(id);
+ if (!user) {
+ return res.status(404).json({
+ success: false,
+ message: `User with id ${id} doesn't exist or was permanently deleted`,
+ });
+ }
+ return res.status(200).json({
+ success: true,
+ response: [user.email, user.name],
+ message: "User was permanently deleted",
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, message: "Server error", error: err.message });
+ }
+};
+
+// FIXME EXTRA ---- Edit profile >>>>> only for authorised users for their own profiles(toggle easy/hard mode, change password?)
+const updateUser = async (req, res) => {
+ console.log("edit profile");
+};
+
+// TODO ----- REWARDS&STRIKES ------
+
+// FIXME MUST ---- User's Rewards Collection >>>>> only for auth users
+const userRewards = async (req, res) => {
+ res.send("Your reward is here");
+};
+
+// FIXME MUST ---- Streaks >>>>> only for auth users
+const userStreak = async (req, res) => {
+ //console.log("Your streak");
+
+ try {
+ const completedQuests = await Quest.find({
+ createdBy: req.user._id,
+ done: true,
+ }).sort({
+ doneAt: -1,
+ });
+
+ if (completedQuests.length === 0) {
+ return res.status(200).json({
+ success: true,
+ message: "There are no completed quests yet",
+ streak: 0,
+ });
+ }
+ //console.log(completedQuests);
+ let streak = 0;
+ let today = new Date().setHours(0, 0, 0, 0);
+ //console.log(today);
+ let checkDate = today;
+ //console.log(checkDate);
+
+ for (let i = 0; i < completedQuests.length; i++) {
+ let lastQuest = completedQuests[i];
+ let lastQuestDate = new Date(lastQuest.doneAt).setHours(0, 0, 0, 0);
+ //console.log(lastQuestDate);
+
+ if (checkDate - lastQuestDate > 86400000) {
+ //number is over one day in milisec, streak broken
+ break;
+ }
+ if (
+ checkDate - lastQuestDate === 86400000 ||
+ lastQuestDate === checkDate
+ ) {
+ streak++;
+ checkDate -= 86400000;
+ //console.log(checkDate);
+ }
+ //console.log(checkDate - lastQuestDate);
+ }
+
+ return res.status(200).json({ success: true, response: streak });
+ } catch (err) {
+ return res.status(500).json({
+ success: false,
+ message: "Somethng went wrong at the server",
+ error: err.errors,
+ });
+ }
+};
+
+// FIXME Nice+ ---- User page (shows: current strike, settings, log out, delete user, bonus points, profile picture state, user library) >>>>> only for auth users
+const profileSettings = async (req, res) => {
+ /* console.log("user info page"); */
+ res.send("User profle");
+};
+
+// TODO ---- PUNISHMENTS ----
+// EXTRA ---- Send an embarrassing message to smbdy >>>>> only for auth users
+/* app.post("/punishment/embarrass-me", (req, res) => {
+ console.log("Welp it didn't go well");
+}); */
+
+// EXTRA ---- Lock instagram or tiktok for an hour >>>>> only for auth users
+/* app.post("/punishment/lock", (req, res) => {
+ console.log("def too much");
+});
+ */
+
+export default {
+ registerUser,
+ loginUser,
+ deleteUser,
+ updateUser,
+ //userMood,
+ userRewards,
+ userStreak,
+ profileSettings,
+};
diff --git a/backend/package.json b/backend/package.json
index 08f29f244..b0a96f86c 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -3,18 +3,31 @@
"version": "1.0.0",
"description": "Server part of final project",
"scripts": {
- "start": "babel-node server.js",
- "dev": "nodemon server.js --exec babel-node"
+ "start": "node server.js --exec babel-node",
+ "dev": "nodemon server.js --exec babel-node",
+ "test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
- "@babel/core": "^7.17.9",
- "@babel/node": "^7.16.8",
- "@babel/preset-env": "^7.16.11",
+ "@babel/core": "^7.29.0",
+ "@babel/node": "^7.29.0",
+ "@babel/preset-env": "^7.29.0",
+ "@dicebear/collection": "^9.3.2",
+ "@dicebear/core": "^9.3.2",
+ "bcrypt": "^6.0.0",
+ "bcrypt-nodejs": "^0.0.3",
"cors": "^2.8.5",
- "express": "^4.17.3",
- "mongoose": "^8.4.0",
- "nodemon": "^3.0.1"
+ "crypto": "^1.0.1",
+ "dotenv": "^17.3.1",
+ "express": "^4.22.1",
+ "express-list-endpoints": "^7.1.1",
+ "mongodb": "^7.1.0",
+ "mongoose": "^8.23.0",
+ "nodemon": "^3.1.11",
+ "test": "^3.3.0"
+ },
+ "devDependencies": {
+ "@types/bcrypt-nodejs": "^0.0.31"
}
-}
\ No newline at end of file
+}
diff --git a/backend/quests.json b/backend/quests.json
new file mode 100644
index 000000000..012676fc0
--- /dev/null
+++ b/backend/quests.json
@@ -0,0 +1,54 @@
+[
+ {
+ "_id": "682bab8c12155b00101732db",
+ "message": "Clean the toilet",
+ "timeNeed": 20,
+ "category": ["Cleaning", "Bathroom"],
+ "deadline": "2026-03-15T22:07:08.999Z",
+ "done": "true",
+ "createdAt": "2026-03-08T22:07:08.999Z",
+ "updatedAt": "2025-03-14T22:07:08.999Z",
+ "createdBy": "",
+ "__v": 0
+ },
+ {
+ "_id": "682bab8c12155b00101732nb",
+ "message": "Do one laundry run",
+ "timeNeed": 12,
+ "category": ["Washing", "Clothes"],
+ "deadline": "2026-03-17T22:07:08.999Z",
+ "done": "false",
+ "createdAt": "2026-03-08T22:07:08.999Z",
+ "__v": 0
+ },
+ {
+ "_id": "682bab8c12155b00101732cb",
+ "message": "Vacuum clean bedroom",
+ "timeNeed": 25,
+ "category": ["Cleaning", "Bedroom"],
+ "deadline": "2026-03-10T22:07:08.999Z",
+ "done": "true",
+ "createdAt": "2026-03-04T22:07:08.999Z",
+ "updatedAt": "2025-03-09T22:07:08.999Z",
+ "__v": 0
+ },
+ {
+ "_id": "682bab8c12155b00101732kb",
+ "message": "Water your plants",
+ "timeNeed": 5,
+ "category": ["Plants"],
+ "done": "false",
+ "createdAt": "2026-03-08T22:07:08.999Z",
+ "__v": 0
+ },
+ {
+ "_id": "682bab8c12155b00101732db",
+ "message": "Unload dishwasher",
+ "timeNeed": 16,
+ "category": ["Cleaning"],
+ "done": "true",
+ "createdAt": "2026-03-08T22:07:08.999Z",
+ "updatedAt": "2025-03-09T22:07:08.999Z",
+ "__v": 0
+ }
+]
diff --git a/backend/questseeds.json b/backend/questseeds.json
new file mode 100644
index 000000000..015e9de44
--- /dev/null
+++ b/backend/questseeds.json
@@ -0,0 +1,47 @@
+[
+ {
+ "message": "Clean kitchen sink",
+ "timeNeeded": 10,
+ "category": ["Cleaning", "Kitchen"]
+ },
+ {
+ "message": "Unload washing machine",
+ "timeNeeded": 15,
+ "category": ["Washing", "Clothes"]
+ },
+ {
+ "message": "Clean the toilet",
+ "timeNeeded": 18,
+ "category": ["Cleaning", "Bathroom"]
+ },
+ {
+ "message": "Put things at their places from the work table",
+ "timeNeeded": 12,
+ "category": ["Cleaning", "Home office"]
+ },
+ {
+ "message": "Put clothes in the closet or in laundry",
+ "timeNeeded": 8,
+ "category": ["Washing", "Clothes", "Cleaning"]
+ },
+ {
+ "message": "Dust one room",
+ "timeNeeded": 15,
+ "category": ["Cleaning", "House"]
+ },
+ {
+ "message": "Load washing machine one time",
+ "timeNeeded": 15,
+ "category": ["Washing", "Clothes"]
+ },
+ {
+ "message": "Water the plants",
+ "timeNeeded": 5,
+ "category": ["Plants", "House"]
+ },
+ {
+ "message": "Clean one pair of shoes",
+ "timeNeeded": 12,
+ "category": ["Clothes", "Cleaning", "Wardrobe"]
+ }
+]
diff --git a/backend/schemas.js b/backend/schemas.js
new file mode 100644
index 000000000..78f56f33d
--- /dev/null
+++ b/backend/schemas.js
@@ -0,0 +1,164 @@
+import mongoose from "mongoose";
+import crypto from "crypto";
+import bcrypt from "bcrypt-nodejs";
+import { timeStamp } from "console";
+
+const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project";
+mongoose.connect(mongoUrl);
+mongoose.Promise = Promise;
+
+// FIXME ??add smth else MUST ---- User's Quest Model ----
+
+const questSchema = new mongoose.Schema(
+ {
+ message: {
+ type: String,
+ required: true,
+ minLength: 2,
+ },
+
+ timeNeeded: {
+ type: Number,
+ require: true,
+ },
+
+ category: [
+ {
+ type: String,
+ lowercase: true,
+ trim: true,
+ },
+ ],
+
+ deadline: {
+ type: Date,
+ require: false,
+ },
+
+ done: {
+ type: Boolean,
+ default: false,
+ },
+
+ doneAt: {
+ type: Date,
+ default: null,
+ },
+
+ createdBy: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: "User",
+ },
+
+ kudos: {
+ type: Number,
+ default: 0,
+ },
+
+ kudosByUser: [
+ {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: "User",
+ },
+ ],
+ },
+
+ { timestamps: true },
+);
+
+export const Quest = mongoose.model("Quest", questSchema);
+
+// FIXME add smth else?? MUST ---- User Model ----
+
+const userSchema = new mongoose.Schema(
+ {
+ name: {
+ type: String,
+ unique: true,
+ required: true,
+ minLength: 3,
+ maxLength: 24,
+ },
+
+ email: {
+ type: String,
+ required: true,
+ unique: true,
+ lowercase: true,
+ },
+
+ password: {
+ type: String,
+ required: true,
+ minLength: 8,
+ },
+
+ registerDate: {
+ type: Date,
+ default: () => new Date(),
+ },
+
+ streak: {
+ type: Number,
+ default: 0,
+ },
+
+ todayTaskCompleted: {
+ type: Boolean,
+ default: false,
+ },
+
+ lastTaskCompleted: {
+ type: Date,
+ },
+
+ moodUrl: {
+ type: String,
+ default: "https://static.productionready.io/images/smiley-cyrus.jpg",
+ },
+
+ accessToken: {
+ type: String,
+ default: () => crypto.randomBytes(128).toString("hex"),
+ },
+ },
+
+ { timestamps: true },
+);
+
+export const User = mongoose.model("User", userSchema);
+
+// ---- Library Quest Model -----
+
+const libraryQuest = new mongoose.Schema(
+ {
+ message: {
+ type: String,
+ required: true,
+ minLength: 2,
+ },
+
+ timeNeeded: {
+ type: Number,
+ require: true,
+ },
+
+ category: [
+ {
+ type: String,
+ lowercase: true,
+ trim: true,
+ },
+ ],
+ },
+
+ { timestamps: true },
+);
+
+export const LibraryQuest = mongoose.model("LibraryQuest", libraryQuest);
+
+// ???---- Session ----
+// Schema to randomize and sessions?
+
+// ???NICE+ ---- Friends -----
+//When adding a friend?
diff --git a/backend/seedLibraryQuest.js b/backend/seedLibraryQuest.js
new file mode 100644
index 000000000..37f319be4
--- /dev/null
+++ b/backend/seedLibraryQuest.js
@@ -0,0 +1,21 @@
+import mongoose from "mongoose";
+import { LibraryQuest } from "./schemas.js";
+import questseeds from "./questseeds.json" with { type: "json" };
+
+//DONE ---- Seeding database with default tasks
+
+export const seedDatabase = async () => {
+ try {
+ const existingLibQuests = await LibraryQuest.countDocuments();
+ if (existingLibQuests > 0) {
+ console.log("Already seeded");
+ return;
+ }
+ if (!existingLibQuests) {
+ await LibraryQuest.insertMany(questseeds);
+ console.log("Successfully seeded");
+ }
+ } catch (err) {
+ return (console.log("Seeding failed"), err);
+ }
+};
diff --git a/backend/server.js b/backend/server.js
index 070c87518..f4f8ccb83 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -1,10 +1,13 @@
import express from "express";
import cors from "cors";
import mongoose from "mongoose";
-
-const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project";
-mongoose.connect(mongoUrl);
-mongoose.Promise = Promise;
+import listEndpoints from "express-list-endpoints";
+import { authentificateUser } from "./authMiddleware.js";
+import "dotenv/config";
+import userController from "./controllers/userController.js";
+import questController from "./controllers/questController.js";
+import friendController from "./controllers/friendController.js";
+import { seedDatabase } from "./seedLibraryQuest.js";
const port = process.env.PORT || 8080;
const app = express();
@@ -12,11 +15,132 @@ const app = express();
app.use(cors());
app.use(express.json());
+const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project";
+
+console.log("Starting server... With Mongo URL:", mongoUrl);
+
+mongoose.connect(mongoUrl);
+mongoose.Promise = Promise;
+
+// ---- Middleware to handle error at service availability before running anything else
+app.use((req, res, next) => {
+ if (mongoose.connection.readyState === 1) {
+ next();
+ } else {
+ res.status(503).json({ error: "Service unavailable" });
+ }
+});
+
+// ---- All ENDPOINTS, temporary ----
+
app.get("/", (req, res) => {
- res.send("Hello Technigo!");
+ const endpoints = listEndpoints(app);
+ //console.log({ endpoints: endpoints });
+ res.json({
+ message: "List of all endpoints",
+ endpoints: endpoints,
+ }); // delete res.json before prod!
+ //console.log("OUR ENV VAR", process.env.OUR_VAR);
});
+//---- USER ENDPOINTS ----
+
+//---- Register new user ----
+app.post("/signup", userController.registerUser);
+
+//---- Login with existing user ----
+app.post("/login", userController.loginUser);
+
+// ---- Delete user -----
+app.delete("/users/:id", authentificateUser, userController.deleteUser);
+
+// ------- User's Rewards Collection ---------
+app.get("/rewards", authentificateUser, userController.userRewards);
+
+// ---- Streaks ---------
+app.get("/streaks", authentificateUser, userController.userStreak);
+
+// Nice-to-have: ---- User page (shows: current strike, settings, log out, delete user, bonus points, profile picture state, user library) >>>>> only for auth users
+app.get("/profile/:id", authentificateUser, userController.profileSettings);
+
+// EXTRA ---- Edit profile, (toggle easy/hard mode, change password)
+app.patch("/profile/settings", authentificateUser, userController.updateUser);
+
+// ---- QUESTS ----
+
+// --- Create a quest -----
+app.post("/quests", authentificateUser, questController.createQuestUnAuth);
+
+// --- Give kudos --------
+app.post("/quests/:id/kudos", authentificateUser, questController.giveKudos);
+
+// ---- Show All Quests from Library, (OBS! Doesn't requie authentication, filters on category and time <=N -----
+//Test example: http://localhost:8080/quests/library/?category=cleaning&time=20
+app.get("/quests/library", questController.showDefaultQuests);
+
+// ----- Returns all user's quests, can filter on category and time <= N) -------
+app.get("/quests/all", authentificateUser, questController.showUserQuests);
+
+// ---- Duplicates a quest from library >>> only for auth users -------
+app.post(
+ "/quests/library/:id/add",
+ authentificateUser,
+ questController.duplicateQuest,
+);
+
+// ------ Check quest as done -----
+app.patch(
+ "/quests/:id/complete",
+ authentificateUser,
+ questController.checkQuestDone,
+);
+
+// ---- Delete one quest ----------
+app.delete("/quests/:id", questController.deleteQuest);
+
+// ---- User's done quests /quests/done/true
+app.get(
+ "/quests/done?done=true",
+ authentificateUser,
+ questController.filterUserQuests,
+);
+
+// ---- User's daily random quest ------
+app.get("/quests/:id", authentificateUser, questController.getRandomQuest);
+
+// Nice-to-have ---- Quests history >>>>>> only for auth users
+app.get(
+ "/quests/history",
+ authentificateUser,
+ questController.showQuestHistory,
+);
+
+// ---- Delete more than 1 quest at a time >>>>> only for authorised users for their list
+app.delete(
+ "/quests/delete/more",
+ authentificateUser,
+ questController.deleteManyQuests,
+);
+
+// ---- FRIENDS ----
+
+// ---- Friends Feed page --------
+app.get("/feed/quests", authentificateUser, friendController.getFriendsFeed);
+
+// ---- Find a friend by ID ------
+app.get("/friends/:id", authentificateUser, friendController.findUserById);
+
+// ---- Find a friend by Name ------
+app.get("/friends/:name", authentificateUser, friendController.findUserdByName);
+
+// ---- Delete a friend ---------
+app.delete("/friends/:id", friendController.removeFriend);
+
+// -------------------------------------------
+
// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
+ seedDatabase();
+ console.log("Run the seedDatabase");
});
diff --git a/frontend/README.md b/frontend/README.md
index 5cdb1d9cf..2cb803605 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,8 +1,49 @@
-# Frontend part of Final Project
+# Randify πΎ
-This boilerplate is designed to give you a head start in your React projects, with a focus on understanding the structure and components. As a student of Technigo, you'll find this guide helpful in navigating and utilizing the repository.
+Fullstack project built by Nicolinabl & Demijuls
-## Getting Started
+Live app: https://julia-nicolina.netlify.app
+backend: https://final-project-task-randomizer.onrender.com
-1. Install the required dependencies using `npm install`.
-2. Start the development server using `npm run dev`.
\ No newline at end of file
+Your task-list shouldn't feel like a boss battle before you even start. Let us do your mental labour! We'll pick the quest. You just win it.
+
+## What is it?
+
+Doing tasks is boring. Choosing a task is a task. Too many tasks gets overwhelming β> nothing gets done.
+Randify makes your endless task-list fun and easy. Tell the app how much time you have available, and a random task (or as we like to call it: quest) is chosen for you according to how much time you have available. No decision fatigue, no excuses. Complete your quest everyday and keep your avatar happy. Keep a streak and share with your friends.
+
+## Features
+
+π² Quest randomizer: enter how many minutes you have, get a quest that fits
+π Quest library: don't know what to do? Browse our library and add pre-made quests to your personal list
+β
Complete quests: check off quests and keep your avatar happy
+π Mood avatar: your avatar's mood reflects your streak (sad β happy β super happy)
+π― Friends feed: see what quests others have completed
+π Kudos: give kudos to friends for finishing their quests
+π₯ Streaks: keep completing quests to build your streak
+
+## Tech Stack
+
+### Frontend
+
+- React
+- React Router for navigation
+- Zustand for global state management
+- Styled Components for styling
+- Framer Motion for animations
+- DiceBear for mood-based avatar generation
+- Lottie for animations
+
+## Backend
+
+- Node.js with Express
+- MongoDB with Mongoose
+- JWT authentication
+
+- bcrypt for password hashing
+
+## Deployment
+
+Frontend hosted on Netlify
+Backend hosted on Render
+Database hosted on MongoDB Atlas
diff --git a/frontend/api.js b/frontend/api.js
new file mode 100644
index 000000000..441e8d35c
--- /dev/null
+++ b/frontend/api.js
@@ -0,0 +1 @@
+export const apiUrl = import.meta.env.VITE_API_URL || "http://localhost:8080";
diff --git a/frontend/index.html b/frontend/index.html
index 664410b5b..aa987d445 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,9 +2,14 @@
-
- Technigo React Vite Boiler Plate
+
+
+
+
+
+
+ Randify
diff --git a/frontend/package.json b/frontend/package.json
index 7b2747e94..efc12f67f 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -10,10 +10,21 @@
"preview": "vite preview"
},
"dependencies": {
+ "@dicebear/collection": "^9.4.0",
+ "@dicebear/core": "^9.4.0",
+ "framer-motion": "^12.35.0",
+ "hamburger-menu": "^0.7.1",
+ "hamburger-react": "^2.5.2",
+ "lottie-react": "^2.4.1",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-hot-toast": "^2.6.0",
+ "react-router-dom": "^7.13.0",
+ "styled-components": "^6.3.11",
+ "zustand": "^5.0.11"
},
"devDependencies": {
+ "@types/bcrypt-nodejs": "^0.0.31",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
diff --git a/frontend/public/_redirects b/frontend/public/_redirects
new file mode 100644
index 000000000..f8243379a
--- /dev/null
+++ b/frontend/public/_redirects
@@ -0,0 +1 @@
+/* /index.html 200
\ No newline at end of file
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 0a24275e6..b40a0f613 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,8 +1,56 @@
+import { Routes, Route, Link } from "react-router-dom";
+import { About } from "./pages/AboutPage";
+import { FriendFeed } from "./pages/FriendFeedPage";
+import { Home } from "./pages/HomePage";
+import { Quests } from "./pages/QuestPage";
+import { Rewards } from "./pages/RewardPage";
+import { UserProfile } from "./pages/UserProfilePage";
+import { Login } from "./pages/LoginPage";
+import { GlobalStyle } from "./styles/GlobalStyles";
+import { Signup } from "./pages/SignupPage";
+import { useEffect, useState } from "react";
+import { apiUrl } from "../api";
+import { Navbar } from "./components/Navbar";
+import { useUserStore } from "./stores/useUserStore";
+import { GiveUp } from "./pages/GiveUpPage";
+import { Toaster } from "react-hot-toast";
+
export const App = () => {
+ const { getUser, logout } = useUserStore();
+
+ // call getUser function on app mount (in userStore)
+ useEffect(() => {
+ getUser();
+ }, []);
+
+ const user = JSON.parse(localStorage.getItem("user"));
+ const accessToken = () => (user ? { Authorization: user.accessToken } : {});
return (
<>
- Welcome to Final Project!
+
+
+
+
+
+ } />
+ } />
+ }
+ />
+ } />
+ } />
+ } />
+ } />
+ } />
+
>
);
};
diff --git a/frontend/src/assets/animation/Heart.jsx b/frontend/src/assets/animation/Heart.jsx
new file mode 100644
index 000000000..0c37c3082
--- /dev/null
+++ b/frontend/src/assets/animation/Heart.jsx
@@ -0,0 +1,16 @@
+import Lottie from "lottie-react";
+import pixelHeart from "./pixelHeart.json" with { type: "json" };
+import styled from "styled-components";
+
+export const HeartAnimation = () => {
+ return (
+
+
+
+ );
+};
+
+const Div = styled.div`
+ border-radius: 12px;
+ overflow: hidden;
+`;
diff --git a/frontend/src/assets/animation/pixelHeart.json b/frontend/src/assets/animation/pixelHeart.json
new file mode 100644
index 000000000..cb1afb012
--- /dev/null
+++ b/frontend/src/assets/animation/pixelHeart.json
@@ -0,0 +1 @@
+{"nm":"Pixel Heart Fill_v02loader","ddd":0,"h":500,"w":500,"meta":{"g":"@lottiefiles/creator 1.2.0"},"layers":[{"ty":0,"nm":" Pixel Heart Fill","sr":1,"st":0,"op":150,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[250,250]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[80,80],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,100],"t":30},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[80,80],"t":60},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,100],"t":90},{"s":[80,80],"t":150}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"w":500,"h":500,"refId":"Scene 1","ind":1},{"ty":4,"nm":"Rectangle 1","sr":1,"st":0,"op":150,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[500,500]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.6118,0.451,0.9725,1]},"r":2,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":2}],"v":"5.7.0","fr":30,"op":150,"ip":0,"assets":[{"nm":"Pixel Heart Fill","id":"Scene 1","layers":[{"ty":4,"nm":"5","sr":1,"st":120,"op":150,"ip":120,"hd":false,"ln":"Layer_9","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[249.99999618530273,249.99999237060547]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[249.99999618530273,249.99999237060547]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group 1","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 1","d":1,"p":{"a":0,"k":[308.005,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 2","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 2","d":1,"p":{"a":0,"k":[269.34000000000003,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 3","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 3","d":1,"p":{"a":0,"k":[250,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 4","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 4","d":1,"p":{"a":0,"k":[114.65,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 5","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 5","d":1,"p":{"a":0,"k":[385.35,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 6","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 6","d":1,"p":{"a":0,"k":[366.02000000000004,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 7","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 7","d":1,"p":{"a":0,"k":[346.68,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 8","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 8","d":1,"p":{"a":0,"k":[327.35,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 9","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 9","d":1,"p":{"a":0,"k":[308.01,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 10","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 10","d":1,"p":{"a":0,"k":[288.67,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 11","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 11","d":1,"p":{"a":0,"k":[269.34000000000003,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 12","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 12","d":1,"p":{"a":0,"k":[250,356.35]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 13","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 13","d":1,"p":{"a":0,"k":[230.67,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 14","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 14","d":1,"p":{"a":0,"k":[211.32999999999998,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 15","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 15","d":1,"p":{"a":0,"k":[191.98999999999998,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 16","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 16","d":1,"p":{"a":0,"k":[172.66,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 17","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 17","d":1,"p":{"a":0,"k":[153.32,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 18","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 18","d":1,"p":{"a":0,"k":[133.98999999999998,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 19","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 19","d":1,"p":{"a":0,"k":[133.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 20","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 20","d":1,"p":{"a":0,"k":[366.02000000000004,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 21","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 21","d":1,"p":{"a":0,"k":[346.68,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 22","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 22","d":1,"p":{"a":0,"k":[346.68,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 23","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 23","d":1,"p":{"a":0,"k":[327.35,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 24","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 24","d":1,"p":{"a":0,"k":[308.01,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 25","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 25","d":1,"p":{"a":0,"k":[288.67,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 26","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 26","d":1,"p":{"a":0,"k":[269.34000000000003,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 27","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 27","d":1,"p":{"a":0,"k":[211.32999999999998,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[1,1,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 28","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 28","d":1,"p":{"a":0,"k":[172.66,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[1,1,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 29","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 29","d":1,"p":{"a":0,"k":[191.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[1,1,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 30","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 30","d":1,"p":{"a":0,"k":[153.32,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 31","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 31","d":1,"p":{"a":0,"k":[230.67,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 32","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 32","d":1,"p":{"a":0,"k":[191.995,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 33","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 33","d":1,"p":{"a":0,"k":[346.68,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 34","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 34","d":1,"p":{"a":0,"k":[366.02000000000004,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 35","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[259.67,308.01],[259.67,288.67],[279.01,288.67],[279.01,269.33000000000004],[298.34999999999997,269.33000000000004],[298.34999999999997,249.99000000000004],[317.68999999999994,249.99000000000004],[317.68999999999994,230.65000000000003],[337.0299999999999,230.65000000000003],[337.0299999999999,191.98000000000002],[356.3699999999999,191.98000000000002],[356.3699999999999,172.64000000000001],[337.0299999999999,172.64000000000001],[337.0299999999999,153.3],[279.0199999999999,153.3],[279.0199999999999,172.64000000000001],[259.67999999999995,172.64000000000001],[259.67999999999995,191.98000000000002],[240.33999999999995,191.98000000000002],[240.33999999999995,172.64000000000001],[220.99999999999994,172.64000000000001],[220.99999999999994,153.3],[162.98999999999995,153.3],[162.98999999999995,172.64000000000001],[143.64999999999995,172.64000000000001],[143.64999999999995,191.98000000000002],[124.30999999999995,191.98000000000002],[124.30999999999995,230.65000000000003],[143.64999999999995,230.65000000000003],[143.64999999999995,249.99000000000004],[162.98999999999995,249.99000000000004],[162.98999999999995,269.33000000000004],[182.32999999999996,269.33000000000004],[182.32999999999996,288.67],[201.66999999999996,288.67],[201.66999999999996,308.01],[221.00999999999996,308.01],[221.00999999999996,327.34999999999997],[240.34999999999997,327.34999999999997],[240.34999999999997,346.68999999999994],[259.68999999999994,346.68999999999994],[259.68999999999994,327.34999999999997],[279.0299999999999,327.34999999999997],[279.0299999999999,308.01],[259.68999999999994,308.01]]}}},{"ty":"sh","bm":0,"hd":false,"nm":"Path 2","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[221,211.33],[201.66,211.33],[201.66,191.99],[182.32,191.99],[182.32,211.33],[162.98,211.33],[162.98,191.99],[182.32,191.99],[182.32,172.65],[201.66,172.65],[201.66,191.99],[221,191.99],[221,211.33]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 36","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 35","d":1,"p":{"a":0,"k":[288.67,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 37","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 36","d":1,"p":{"a":0,"k":[308.01,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 38","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 37","d":1,"p":{"a":0,"k":[327.35,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":1},{"ty":4,"nm":"4","sr":1,"st":90,"op":120,"ip":90,"hd":false,"ln":"Layer_8","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[249.99999618530273,249.99999237060547]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[249.99999618530273,249.99999237060547]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group 1","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 1","d":1,"p":{"a":0,"k":[308.005,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 2","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 2","d":1,"p":{"a":0,"k":[269.34000000000003,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 3","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 3","d":1,"p":{"a":0,"k":[250,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 4","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 4","d":1,"p":{"a":0,"k":[114.65,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 5","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 5","d":1,"p":{"a":0,"k":[385.35,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 6","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 6","d":1,"p":{"a":0,"k":[366.02000000000004,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 7","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 7","d":1,"p":{"a":0,"k":[346.68,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 8","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 8","d":1,"p":{"a":0,"k":[327.35,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 9","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 9","d":1,"p":{"a":0,"k":[308.01,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 10","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 10","d":1,"p":{"a":0,"k":[288.67,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 11","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 11","d":1,"p":{"a":0,"k":[269.34000000000003,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 12","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 12","d":1,"p":{"a":0,"k":[250,356.35]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 13","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 13","d":1,"p":{"a":0,"k":[230.67,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 14","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 14","d":1,"p":{"a":0,"k":[211.32999999999998,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 15","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 15","d":1,"p":{"a":0,"k":[191.98999999999998,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 16","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 16","d":1,"p":{"a":0,"k":[172.66,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 17","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 17","d":1,"p":{"a":0,"k":[153.32,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 18","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 18","d":1,"p":{"a":0,"k":[133.98999999999998,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 19","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 19","d":1,"p":{"a":0,"k":[133.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 20","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 20","d":1,"p":{"a":0,"k":[366.02000000000004,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 21","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 21","d":1,"p":{"a":0,"k":[346.68,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 22","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 22","d":1,"p":{"a":0,"k":[346.68,221]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 23","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 23","d":1,"p":{"a":0,"k":[327.35,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 24","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 24","d":1,"p":{"a":0,"k":[308.01,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 25","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 25","d":1,"p":{"a":0,"k":[288.67,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 26","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 26","d":1,"p":{"a":0,"k":[269.34000000000003,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 27","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 27","d":1,"p":{"a":0,"k":[211.32999999999998,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 28","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 28","d":1,"p":{"a":0,"k":[172.66,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 29","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 29","d":1,"p":{"a":0,"k":[191.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 30","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 30","d":1,"p":{"a":0,"k":[153.32,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 31","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 31","d":1,"p":{"a":0,"k":[230.67,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 32","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 32","d":1,"p":{"a":0,"k":[191.995,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 33","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 33","d":1,"p":{"a":0,"k":[346.68,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 34","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 34","d":1,"p":{"a":0,"k":[366.02000000000004,221]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 35","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[221,211.33],[201.66,211.33],[201.66,191.99],[182.32,191.99],[182.32,211.33],[162.99,211.33],[162.99,191.99],[182.32,191.99],[182.32,172.66],[201.66,172.66],[201.66,191.99],[221,191.99],[221,211.33]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 36","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 35","d":1,"p":{"a":0,"k":[288.67,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 37","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 36","d":1,"p":{"a":0,"k":[308.01,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 38","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 37","d":1,"p":{"a":0,"k":[327.35,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 39","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 2","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[124.32,211.33],[124.32,230.66],[143.65,230.66],[143.65,250],[162.99,250],[162.99,269.34],[182.32,269.34],[182.32,288.67],[201.66,288.67],[201.66,308.01],[221,308.01],[221,327.34],[240.33,327.34],[240.33,346.68],[259.67,346.68],[259.67,327.34],[279,327.34],[279,308.01],[259.67,308.01],[259.67,288.67],[279,288.67],[279,269.34],[298.34,269.34],[298.34,250],[317.68,250],[317.68,230.66],[337.01,230.66],[337.01,211.33],[124.32,211.33]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":2},{"ty":4,"nm":"3","sr":1,"st":60,"op":90,"ip":60,"hd":false,"ln":"_3","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[249.99999618530273,249.99999237060547]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[249.99999618530273,249.99999237060547]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group 1","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 1","d":1,"p":{"a":0,"k":[308.005,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 2","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 2","d":1,"p":{"a":0,"k":[269.34000000000003,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 3","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 3","d":1,"p":{"a":0,"k":[250,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 4","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 4","d":1,"p":{"a":0,"k":[114.65,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 5","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 5","d":1,"p":{"a":0,"k":[385.35,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 6","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 6","d":1,"p":{"a":0,"k":[366.02000000000004,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 7","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 7","d":1,"p":{"a":0,"k":[346.68,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 8","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 8","d":1,"p":{"a":0,"k":[327.35,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 9","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 9","d":1,"p":{"a":0,"k":[308.01,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 10","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 10","d":1,"p":{"a":0,"k":[288.67,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 11","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 11","d":1,"p":{"a":0,"k":[269.34000000000003,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 12","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 12","d":1,"p":{"a":0,"k":[250,356.35]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 13","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 13","d":1,"p":{"a":0,"k":[230.67,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 14","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 14","d":1,"p":{"a":0,"k":[211.32999999999998,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 15","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 15","d":1,"p":{"a":0,"k":[191.98999999999998,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 16","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 16","d":1,"p":{"a":0,"k":[172.66,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 17","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 17","d":1,"p":{"a":0,"k":[153.32,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 18","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 18","d":1,"p":{"a":0,"k":[133.98999999999998,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 19","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 19","d":1,"p":{"a":0,"k":[133.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 20","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 20","d":1,"p":{"a":0,"k":[366.02000000000004,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 21","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 21","d":1,"p":{"a":0,"k":[346.68,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 22","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 22","d":1,"p":{"a":0,"k":[308.01,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 23","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 23","d":1,"p":{"a":0,"k":[288.67,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 24","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 24","d":1,"p":{"a":0,"k":[269.34000000000003,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 25","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 25","d":1,"p":{"a":0,"k":[211.32999999999998,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 26","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 26","d":1,"p":{"a":0,"k":[172.66,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 27","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 27","d":1,"p":{"a":0,"k":[191.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 28","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 28","d":1,"p":{"a":0,"k":[153.32,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 29","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 29","d":1,"p":{"a":0,"k":[230.67,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 30","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 30","d":1,"p":{"a":0,"k":[191.995,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 31","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[221,211.33],[201.66,211.33],[201.66,191.99],[182.32,191.99],[182.32,211.33],[162.99,211.33],[162.99,191.99],[182.32,191.99],[182.32,172.66],[201.66,172.66],[201.66,191.99],[221,191.99],[221,211.33]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 32","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 31","d":1,"p":{"a":0,"k":[288.67,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 33","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 32","d":1,"p":{"a":0,"k":[308.01,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 34","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 33","d":1,"p":{"a":0,"k":[327.35,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 35","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 2","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[162.99,269.34],[182.32,269.34],[182.32,288.67],[201.66,288.67],[201.66,308.01],[221,308.01],[221,327.34],[240.33,327.34],[240.33,346.68],[259.67,346.68],[259.67,327.34],[279,327.34],[279,308.01],[259.67,308.01],[259.67,288.67],[279,288.67],[279,269.34],[298.34,269.34],[298.34,250],[162.99,250],[162.99,269.34]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":3},{"ty":4,"nm":"2","sr":1,"st":30,"op":60,"ip":30,"hd":false,"ln":"_2","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[249.99999618530273,249.99999237060547]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[249.99999618530273,249.99999237060547]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group 1","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 1","d":1,"p":{"a":0,"k":[308.005,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 2","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 2","d":1,"p":{"a":0,"k":[269.34000000000003,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 3","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 3","d":1,"p":{"a":0,"k":[250,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 4","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 4","d":1,"p":{"a":0,"k":[114.65,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 5","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 5","d":1,"p":{"a":0,"k":[385.35,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 6","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 6","d":1,"p":{"a":0,"k":[366.02000000000004,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 7","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 7","d":1,"p":{"a":0,"k":[346.68,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 8","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 8","d":1,"p":{"a":0,"k":[327.35,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 9","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 9","d":1,"p":{"a":0,"k":[308.01,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 10","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 10","d":1,"p":{"a":0,"k":[288.67,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 11","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 11","d":1,"p":{"a":0,"k":[269.34000000000003,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 12","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 12","d":1,"p":{"a":0,"k":[250,356.35]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 13","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 13","d":1,"p":{"a":0,"k":[230.67,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 14","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 14","d":1,"p":{"a":0,"k":[211.32999999999998,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 15","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 15","d":1,"p":{"a":0,"k":[191.98999999999998,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 16","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 16","d":1,"p":{"a":0,"k":[172.66,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 17","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 17","d":1,"p":{"a":0,"k":[153.32,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 18","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 18","d":1,"p":{"a":0,"k":[133.98999999999998,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 19","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 19","d":1,"p":{"a":0,"k":[133.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 20","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 20","d":1,"p":{"a":0,"k":[366.02000000000004,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 21","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 21","d":1,"p":{"a":0,"k":[346.68,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 22","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 22","d":1,"p":{"a":0,"k":[211.32999999999998,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 23","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 23","d":1,"p":{"a":0,"k":[172.66,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 24","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 24","d":1,"p":{"a":0,"k":[191.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 25","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 25","d":1,"p":{"a":0,"k":[153.32,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 26","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 26","d":1,"p":{"a":0,"k":[230.67,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 27","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 27","d":1,"p":{"a":0,"k":[191.995,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 28","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[221,211.33],[201.66,211.33],[201.66,191.99],[182.32,191.99],[182.32,211.33],[162.99,211.33],[162.99,191.99],[182.32,191.99],[182.32,172.66],[201.66,172.66],[201.66,191.99],[221,191.99],[221,211.33]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 29","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 2","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[279,288.67],[259.67,288.67],[201.66,288.67],[201.66,308.01],[221,308.01],[221,327.34],[240.33,327.34],[240.33,346.68],[259.67,346.68],[259.67,327.34],[279,327.34],[279,308.01],[298.34,308.01],[298.34,288.67],[279,288.67]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":4},{"ty":4,"nm":"1","sr":1,"st":0,"op":30,"ip":0,"hd":false,"ln":"_1","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[249.99999618530273,249.99999237060547]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[249.99999618530273,249.99999237060547]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group 1","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 1","d":1,"p":{"a":0,"k":[308.005,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 2","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 2","d":1,"p":{"a":0,"k":[269.34000000000003,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 3","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 3","d":1,"p":{"a":0,"k":[250,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 4","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 4","d":1,"p":{"a":0,"k":[114.65,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 5","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 5","d":1,"p":{"a":0,"k":[385.35,211.32500000000002]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,38.67]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 6","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 6","d":1,"p":{"a":0,"k":[366.02000000000004,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 7","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 7","d":1,"p":{"a":0,"k":[346.68,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 8","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 8","d":1,"p":{"a":0,"k":[327.35,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 9","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 9","d":1,"p":{"a":0,"k":[308.01,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 10","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 10","d":1,"p":{"a":0,"k":[288.67,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 11","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 11","d":1,"p":{"a":0,"k":[269.34000000000003,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 12","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 12","d":1,"p":{"a":0,"k":[250,356.35]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 13","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 13","d":1,"p":{"a":0,"k":[230.67,337.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 14","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 14","d":1,"p":{"a":0,"k":[211.32999999999998,317.68]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 15","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 15","d":1,"p":{"a":0,"k":[191.98999999999998,298.34000000000003]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 16","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 16","d":1,"p":{"a":0,"k":[172.66,279.01]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 17","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 17","d":1,"p":{"a":0,"k":[153.32,259.67]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 18","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 18","d":1,"p":{"a":0,"k":[133.98999999999998,240.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 19","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 19","d":1,"p":{"a":0,"k":[133.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 20","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 20","d":1,"p":{"a":0,"k":[366.02000000000004,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 21","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 21","d":1,"p":{"a":0,"k":[346.68,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 22","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 22","d":1,"p":{"a":0,"k":[211.32999999999998,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 23","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 23","d":1,"p":{"a":0,"k":[172.66,201.66]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 24","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 24","d":1,"p":{"a":0,"k":[191.98999999999998,182.32999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.9294,0.3137,0.6196,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 25","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 25","d":1,"p":{"a":0,"k":[153.32,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 26","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 26","d":1,"p":{"a":0,"k":[230.67,162.98999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[19.34,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 27","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle 27","d":1,"p":{"a":0,"k":[191.995,143.64999999999998]},"r":{"a":0,"k":0},"s":{"a":0,"k":[58.01,19.34]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2392,0.2431,0,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 28","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 1","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[221,211.33],[201.66,211.33],[201.66,191.99],[182.32,191.99],[182.32,211.33],[162.99,211.33],[162.99,191.99],[182.32,191.99],[182.32,172.66],[201.66,172.66],[201.66,191.99],[221,191.99],[221,211.33]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.8497,0.78,1,1]},"r":1,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":5},{"ty":4,"nm":"Rectangle 1","sr":1,"st":0,"op":150,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[500,500]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"Group","it":[{"ty":"rc","bm":0,"hd":false,"nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[100,100]}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.6118,0.451,0.9725,1]},"r":2,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":6}]}]}
\ No newline at end of file
diff --git a/frontend/src/components/Avatar.jsx b/frontend/src/components/Avatar.jsx
new file mode 100644
index 000000000..372340b13
--- /dev/null
+++ b/frontend/src/components/Avatar.jsx
@@ -0,0 +1,160 @@
+//DiceBear open API for generating random mood-states avatars
+import { createAvatar } from "@dicebear/core";
+import { avataaarsNeutral } from "@dicebear/collection";
+
+import { useEffect, useState, useMemo } from "react";
+
+import styled from "styled-components";
+import { useUserStore } from "../stores/useUserStore";
+import { apiUrl } from "../../api";
+
+export const Avatar = () => {
+ const { user } = useUserStore();
+ const [streak, setStreak] = useState(0);
+
+ useEffect(() => {
+ if (!user?.accessToken) return;
+
+ const fetchStreak = async () => {
+ try {
+ const response = await fetch(`${apiUrl}/streaks`, {
+ headers: { Authorization: user.accessToken },
+ });
+ const data = await response.json();
+ if (response.ok && data.success) setStreak(data.response);
+ } catch (err) {
+ console.error("Error fetching streak:", err);
+ }
+ };
+ fetchStreak();
+ }, [user?.accessToken]);
+
+ const streakNumber = Number(streak) || 0;
+
+ const avatarUrl = useMemo(() => {
+ let moodVar = {};
+
+ if (streakNumber === 0) {
+ // return avatarRandom = sad
+ moodVar = {
+ eyebrows: [
+ "angry",
+ "angryNatural",
+ "frownNatural",
+ "sadConcerned",
+ "sadConcernedNatural",
+ "unibrowNatural",
+ "upDown",
+ "upDownNatural",
+ ],
+ eyes: [
+ "closed",
+ "cry",
+ "eyeRoll",
+ "side",
+ "squint",
+ "surprised",
+ "xDizzy",
+ ],
+ mouth: [
+ "concerned",
+ "disbelief",
+ "grimace",
+ "sad",
+ "screamOpen",
+ "serious",
+ "vomit",
+ ],
+ };
+ } else if (streakNumber >= 1 && streakNumber <= 10) {
+ //return avatarRandom = smiley
+ moodVar = {
+ eyebrows: [
+ "unibrowNatural",
+ "upDown",
+ "upDownNatural",
+ "flatNatural",
+ "default",
+ "defaultNatural",
+ ],
+ eyes: ["closed", "default", "winkWacky", "wink"],
+ mouth: ["default", "twinkle"],
+ };
+ } else if (streakNumber > 10 && streakNumber < 100) {
+ //return avatarRandom = happy
+ moodVar = {
+ eyebrows: [
+ "unibrowNatural",
+ "upDown",
+ "upDownNatural",
+ "flatNatural",
+ "defaultNatural",
+ "angry",
+ "default",
+ "raisedExcited",
+ "raisedExcitedNatural",
+ ],
+ eyes: ["winkWacky", "wink", "happy", "squint", "surprised"],
+ mouth: ["eating", "smile", "tongue"],
+ };
+ } else if (streakNumber >= 100) {
+ //return avatarRandom = super happy
+ moodVar = {
+ eyebrows: [
+ "upDownNatural",
+ "frownNatural",
+ "raisedExcited",
+ "raisedExcitedNatural",
+ "sadConcerned",
+ "sadConcernedNatural",
+ "unibrowNatural",
+ "upDown",
+ ],
+ eyes: ["hearts", "eyeRoll"],
+ mouth: ["eating", "smile", "tongue"],
+ };
+ } else {
+ moodVar = {
+ eyebrows: ["raisedExcited"],
+ eyes: ["xDizzy"],
+ mouth: ["serious"],
+ };
+ }
+ //console.log(typeof streakNumber);
+
+ const avatar = createAvatar(avataaarsNeutral, {
+ seed: crypto.randomUUID(), //Math.random().toString(), //instead of Math.random() to ensure there are no problem when a lot of avatars generated at the same time, for scalability
+ backgroundColor: [
+ "f8d25c",
+ "fd9841",
+ "b6e3f4",
+ "c0aede",
+ "d1d4f9",
+ "ffd5dc",
+ ],
+ randomizeIds: true, // - used for randomizing multiple avatars on the same page, needed at friends feed page
+ ...moodVar,
+ });
+
+ return avatar.toDataUri();
+ }, [streakNumber]);
+
+ return (
+
+
+
+ );
+};
+
+// --------- Styles --------
+
+const AvatarWrapper = styled.div`
+ text-align: center;
+ justify-content: center;
+`;
+
+const AvatarImg = styled.img`
+ min-width: 128px;
+ max-width: 30%;
+ height: auto;
+`;
diff --git a/frontend/src/components/Button.jsx b/frontend/src/components/Button.jsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/frontend/src/components/Checkbox.jsx b/frontend/src/components/Checkbox.jsx
new file mode 100644
index 000000000..25398f138
--- /dev/null
+++ b/frontend/src/components/Checkbox.jsx
@@ -0,0 +1,45 @@
+import styled from "styled-components";
+import { CheckboxEmpty } from "../icons/CheckboxEmpty";
+import { CheckboxChecked } from "../icons/CheckboxChecked";
+
+export const Checkbox = ({ checked, onChange }) => {
+ return (
+
+
+ {checked ? : }
+
+ );
+};
+
+const Label = styled.label`
+ display: inline-flex;
+ cursor: pointer;
+ position: relative;
+ align-items: center;
+`;
+
+const HiddenInput = styled.input`
+ position: absolute;
+ opacity: 0;
+ width: 0;
+ height: 0;
+`;
+
+const StyledBox = styled.div`
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.15s ease;
+
+ ${Label}:hover & {
+ transform: scale(1.1);
+ border-radius: 4px;
+ background-color: var(--light-purple);
+ }
+
+ &:active {
+ transform: scale(0.85);
+ }
+`;
diff --git a/frontend/src/components/CompletedQuests.jsx b/frontend/src/components/CompletedQuests.jsx
new file mode 100644
index 000000000..0a8c89b17
--- /dev/null
+++ b/frontend/src/components/CompletedQuests.jsx
@@ -0,0 +1,97 @@
+import { CheckboxChecked } from "../icons/CheckboxChecked";
+import { useQuestStore } from "../stores/useQuestStore";
+import { QuestCard } from "./cards/QuestCard";
+import styled from "styled-components";
+import { motion, AnimatePresence } from "framer-motion";
+import { useState } from "react";
+
+export const CompletedQuests = () => {
+ const { quests, completeQuest, deleteQuest } = useQuestStore();
+ const [isVisible, setIsVisible] = useState(false);
+
+ const completed = quests.filter((quest) => quest.done === true);
+
+ return (
+
+
+
+ My completed quests:
+ setIsVisible(!isVisible)}>
+ {isVisible ? "Hide" : "Show"}
+
+
+
+
+ {isVisible && (
+
+ {completed.map((quest) => (
+
+ ))}
+
+ )}
+
+
+ );
+};
+
+const Container = styled.div`
+ padding: 10px 0;
+ align-items: center;
+ width: 100%;
+`;
+
+const HeadingContainer = styled.div`
+ display: flex;
+ height: 64px;
+ gap: 8px;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 16px;
+ margin-bottom: 8px;
+ width: 100%;
+ align-self: stretch;
+ border-radius: 12px;
+ background: var(--main-white);
+`;
+
+const Button = styled.button`
+ display: inline-flex;
+ height: 44px;
+ padding: 4px 12px;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ border-radius: 12px;
+ border: 1px solid #6d48fe;
+ font-size: 14px;
+ font-family: "Roboto", sans-serif;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ background-color: var(--main-white);
+
+ /* Small shadow */
+ box-shadow: 0 1px 1px 0 #dbdbdb;
+
+ &:hover {
+ background: var(--light-purple);
+ }
+
+ &:active {
+ background: var(--accent-purple);
+ }
+`;
diff --git a/frontend/src/components/CreateQuest.jsx b/frontend/src/components/CreateQuest.jsx
new file mode 100644
index 000000000..e271f7693
--- /dev/null
+++ b/frontend/src/components/CreateQuest.jsx
@@ -0,0 +1,139 @@
+import styled from "styled-components";
+import { useState } from "react";
+import { useQuestStore } from "../stores/useQuestStore";
+import toast from "react-hot-toast";
+
+// component for creating quest
+export const CreateQuest = () => {
+ // state variables for form inputs
+ const [message, setMessage] = useState("");
+ const [timeNeeded, setTimeNeeded] = useState("");
+ const [category, setCategory] = useState("");
+ const [error, setError] = useState(null);
+
+ const createQuest = useQuestStore((state) => state.createQuest);
+
+ // Handle form submission
+ const handleSubmit = async (event) => {
+ event.preventDefault();
+ setError(null);
+
+ if (!message.trim()) {
+ //setError("You need to add a quest");
+ toast.error("Please write what you need to do");
+ return;
+ }
+
+ if (!timeNeeded) {
+ //setError("You need to add time");
+ toast.error("Please add time you need to do that quest");
+ return;
+ }
+
+ const result = await createQuest(message, timeNeeded, category);
+
+ if (!result.success) {
+ //setError(result.error);
+ toast.error("Oops, something went wrong:(");
+ return;
+ }
+
+ if (result.success) {
+ toast.success("Quest is added to your list!");
+ } //basic alert for better ux
+
+ // After form is submitted, clear the input field
+ setMessage("");
+ setTimeNeeded("");
+ setCategory("");
+ };
+
+ return (
+
+ );
+};
+
+const Form = styled.form`
+ padding: 16px;
+ border-radius: 12px;
+ border: 2px solid #b594ff;
+ background: #fff;
+ box-shadow: 2px 4px 4px 0 #dbdbdb;
+ text-align: center;
+ margin: 20px 0 20px 0;
+`;
+
+const Label = styled.label`
+ display: flex;
+ flex-direction: column;
+`;
+
+const Input = styled.input`
+ border: none;
+ border-radius: 12px;
+ padding: 15px 16px 14px 16px;
+ background: #f4f0ff;
+ margin: 16px 0;
+ width: 100%;
+`;
+
+const Button = styled.button`
+ display: flex;
+ height: 54px;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ border-radius: 12px;
+ border: 1px solid #1d30ce;
+ background: var(--medium-purple);
+ box-shadow: 2px 4px 4px 0 rgba(139, 139, 139, 0.3);
+ color: white;
+ font-family: "Pixelify Sans", sans-serif;
+ font-size: 20px;
+
+ &:hover {
+ background: var(--dark-purple);
+ }
+
+ &:active {
+ background: var(--accent-purple);
+ }
+`;
+
+
diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx
new file mode 100644
index 000000000..41de44835
--- /dev/null
+++ b/frontend/src/components/Header.jsx
@@ -0,0 +1,51 @@
+import styled from "styled-components";
+import HeartIcon from "../icons/HeartIcon";
+import { Streak } from "./StreakDisplay";
+import { useUserStore } from "../stores/useUserStore";
+
+export const Header = () => {
+ const { user } = useUserStore()
+
+ return (
+
+ Welcome {user.email?.split('@')[0]}
+ Never spend energy on choosing your chores again
+
+
+
+
+ );
+};
+const HeaderWrapper = styled.div`
+ border: 2px solid var(--accent-purple);
+ border-radius: 12px;
+ background: var(--main-white);
+ box-shadow: 2px 4px 4px 0 #dbdbdb;
+ border: 2px solid #B594FF;
+
+ display: flex;
+ flex-direction: column;
+ // justify-content: center;
+ align-items: center;
+
+ gap: 8px;
+ width: 100%;
+ max-width: 800px;
+
+ margin: 24px auto;
+ padding: 16px 16px;
+
+ box-sizing: border-box;
+
+ @media (min-width: 768px) {
+ margin: 24px auto;
+ }
+`;
+const HeadingContainer = styled.div`
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: baseline;
+`;
diff --git a/frontend/src/components/LoginForm.jsx b/frontend/src/components/LoginForm.jsx
new file mode 100644
index 000000000..3e4c4ecbb
--- /dev/null
+++ b/frontend/src/components/LoginForm.jsx
@@ -0,0 +1,178 @@
+import styled from 'styled-components'
+import { Link, useNavigate } from 'react-router-dom'
+import { useEffect, useState } from 'react'
+import { apiUrl } from '../../api'
+import { useUserStore } from '../stores/useUserStore'
+import { useQuestStore } from '../stores/useQuestStore'
+
+export const LoginForm = () => {
+ const fetchQuests = useQuestStore((state) => state.fetchQuests)
+ // State variables to store form input values
+ // const [name, setName] = useState('')
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+
+ // State to store and display error messages
+ const [error, setError] = useState(null)
+
+ // Hook to navigate to different routes
+ const navigate = useNavigate()
+
+ // Grabs login action from store
+ const login = useUserStore((state) => state.login)
+
+ // When form is submitted, this function runs
+ const handleSubmit = async (event) => {
+ event.preventDefault()
+ setError(null)
+
+ if(!email.trim()) {
+ setError('Please enter an email adress')
+ return
+ }
+
+ if(!password.trim()) {
+ setError('Please enter a password')
+ }
+
+ try {
+ // send post request to signup endpoint with user data
+ const response = await fetch(apiUrl + '/login', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ email, password })
+ })
+
+ const data = await response.json()
+ console.log('Server response data:', data)
+
+ // check if response is unsuccessful
+ if (!response.ok) {
+ if (response.status === 401) {
+ setError('Incorrect email or password')
+ } else if (response.status === 404) {
+ setError ('No account found with that email')
+ } else {
+ setError(data.message || 'Login failed. Please try again')
+ }
+ return
+ }
+
+ console.log('Login successful:', data)
+
+ login({
+ accessToken: data.accessToken,
+ userId: data.userID,
+ email: email,
+ name: data.name // check what the API actually returns here
+ })
+
+ // Store the access token in browser's localStorage for future requests
+ // localStorage.setItem('accessToken', data.accessToken)
+ // Store the user ID
+ // localStorage.setItem('userId', data.id)
+ // store user email
+ // localStorage.setItem('userEmail', email)
+
+ // Clear the form inputs
+ setEmail('')
+ setPassword('')
+
+ await fetchQuests(data.accessToken)
+
+ // When signed up successfully -> redirect to profile page
+ navigate('/')
+
+ } catch (error) {
+ console.error('Error:', error)
+ setError('Something went wrong. Please try again')
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+
+const Form = styled.form`
+ margin: 10px;
+ padding: 16px;
+ border-radius: 12px;
+ border: 2px solid #B594FF;
+ background: #FFF;
+ box-shadow: 2px 4px 4px 0 #DBDBDB;
+ text-align: center;
+`
+const Label = styled.label`
+ display: flex;
+ flex-direction: column;
+`
+
+const Input = styled.input`
+ border: none;
+ border-radius: 12px;
+ padding: 15px 16px 14px 16px;
+ background: #F4F0FF;
+ margin: 16px 0;
+ width: 100%;
+ font-size: 16px;
+`
+
+const Button = styled.button`
+ display: flex;
+ height: 54px;
+ width: 100%;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ border-radius: 12px;
+ border: 1px solid #1D30CE;
+ background: var(--medium-purple);
+ box-shadow: 2px 4px 4px 0 rgba(139, 139, 139, 0.30);
+ color: white;
+ font-family: "Pixelify Sans", sans-serif;
+ font-size: 20px;
+ font-weight: 500;
+ cursor: pointer;
+
+ &:hover {
+ background: var(--dark-purple);
+ }
+
+ &:active {
+ background: var(--accent-purple);
+ }
+`
+
+const StyledLink = styled(Link)`
+ text-decoration: none;
+ color: black;
+ margin: 20px;
+ display: block;
+
+ &:hover{
+ transform: scale(1.1)
+ }
+`
\ No newline at end of file
diff --git a/frontend/src/components/MemeDaily.jsx b/frontend/src/components/MemeDaily.jsx
new file mode 100644
index 000000000..17d29bdc6
--- /dev/null
+++ b/frontend/src/components/MemeDaily.jsx
@@ -0,0 +1,55 @@
+import { useEffect, useState } from "react";
+import styled from "styled-components";
+
+export const MemeOfTheDay = () => {
+ const [meme, setMeme] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ useEffect(() => {
+ const fetchMeme = async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const response = await fetch(
+ "https://meme-api.com/gimme/wholesomememes",
+ );
+ const data = await response.json();
+ setMeme(data);
+ } catch (err) {
+ setError("Ooops, couldn't fetch meme");
+ } finally {
+ setLoading(false);
+ }
+ };
+ fetchMeme();
+ }, []);
+
+ /* if (taskDone) {
+ fetchMeme();
+ }
+ }, */
+
+ if (loading) return Loading your reward....
;
+ if (error) return {error}
;
+
+ return (
+
+ {loading && loadingMeme
}
+ {error && Error: {message}
}
+ {meme && }
+
+ );
+};
+
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 24px;
+ margin-bottom: 12px;
+`;
+
+const MemeImg = styled.img`
+ max-width: 100%;
+ border-radius: 12px;
+`;
diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx
new file mode 100644
index 000000000..81d2cbde1
--- /dev/null
+++ b/frontend/src/components/Navbar.jsx
@@ -0,0 +1,146 @@
+import { Link, useNavigate } from "react-router-dom";
+import styled from "styled-components";
+import { useState } from "react";
+import Hamburger from "hamburger-react";
+
+export const Navbar = ({ onLogout }) => {
+ const navigate = useNavigate();
+
+ const [isOpen, setOpen] = useState(false);
+
+ const handleLogoutClick = () => {
+ onLogout(); // Call the logout function
+ setOpen(false);
+ navigate("/"); // Redirect to homepage
+ };
+
+ return (
+
+
+
+
+
+
+ setOpen(false)}>
+ Home
+
+ {/* isOpen(false)}>
+ Profile
+ */}
+ setOpen(false)}>
+ Friends
+
+ setOpen(false)}>
+ About
+
+ setOpen(false)}>
+ Log in
+
+
+ Log out
+
+
+
+ );
+};
+
+const Nav = styled.nav`
+ background-color: var(--primary-color);
+ padding: 8px 20px;
+ height: 56px;
+ width: 100%;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ //position: fixed;
+ left: 0;
+ right: 0;
+ z-index: 100;
+
+ border-radius: 0 0 12px 12px;
+ /* Basic shadow */
+ box-shadow: 1px 2px 2px 0 #dbdbdb;
+`;
+const HamburgerWrapper = styled.div`
+ margin-left: auto;
+ z-index: 1000;
+`;
+
+const NavMenu = styled.div`
+ background-color: var(--medium-pink);
+ display: ${({ $isOpen }) => ($isOpen ? "flex" : "none")};
+ flex-direction: column;
+ position: fixed;
+ z-index: 999;
+ top: 0;
+ left: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+ padding: 0;
+ gap: 16px;
+`;
+
+const StyledLink = styled(Link)`
+ text-decoration: none;
+ font-family: "Roboto", sans-serif;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ color: var(--main-text-color);
+
+ &:hover {
+ font-weight: 500;
+ color: var(--dark-purple);
+ transform: 1.2;
+ transition: ease;
+ }
+
+ &:active {
+ color: var(--secondary-button-color);
+ transform: 0.85;
+ }
+`;
+
+const LogoutButton = styled.button`
+ display: inline-flex;
+ height: 44px;
+ padding: 4px 12px;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ border-radius: 12px;
+ border: 1px solid var(--medium-purple);
+ font-size: 14px;
+ font-family: "Roboto", sans-serif;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ background-color: var(--medium-pink);
+
+ &:hover {
+ font-weight: 500;
+ color: var(--dark-purple);
+ transform: scale(1.1);
+ transition: ease;
+ }
+
+ &:active {
+ color: var(--secondary-button-color);
+ transform: scale(0.85);
+ }
+`;
diff --git a/frontend/src/components/QuestLibrary.jsx b/frontend/src/components/QuestLibrary.jsx
new file mode 100644
index 000000000..71a34f9b7
--- /dev/null
+++ b/frontend/src/components/QuestLibrary.jsx
@@ -0,0 +1,113 @@
+import { LibraryQuestCard } from "./cards/LibraryQuestCard";
+import { useEffect, useState } from "react";
+import { useQuestStore } from "../stores/useQuestStore";
+import styled from "styled-components";
+//import questLibrary from "../library.json";
+import HeartIcon from "../icons/HeartIcon";
+import { motion, AnimatePresence } from "framer-motion";
+import toast from "react-hot-toast";
+
+export const QuestLibrary = () => {
+ const { fetchLibraryQuests, libraryQuests, duplicateQuest, createQuest } =
+ useQuestStore();
+ const [isVisible, setIsVisible] = useState(false);
+
+ useEffect(() => {
+ fetchLibraryQuests();
+ }, []);
+
+ const handleAdd = async (quest) => {
+ const result = await duplicateQuest(quest);
+
+ if (result?.success) {
+ toast.success("Quest is added to your list!");
+ }
+ if (result?.error) {
+ toast.error(result?.error || "Oops, couldn't add quest to your list");
+ }
+ };
+
+ return (
+
+
+
+ Add from quest library:
+ setIsVisible(!isVisible)}>
+ {isVisible ? "Hide" : "Show"}
+
+
+
+
+ {isVisible && (
+
+ {libraryQuests.map((quest) => (
+ handleAdd(quest._id)}
+ />
+ ))}
+
+ )}
+
+
+ );
+};
+
+const Container = styled.div`
+ padding: 10px 0;
+ align-items: center;
+ width: 100%;
+`;
+
+const HeadingContainer = styled.div`
+ display: flex;
+ height: 64px;
+ padding: 0 16px;
+ gap: 8px;
+ align-items: center;
+ padding: 8px 16px;
+ margin-bottom: 8px;
+ width: 100%;
+ align-self: stretch;
+ border-radius: 12px;
+ background: var(--main-white);
+ justify-content: space-between;
+`;
+
+const Button = styled.button`
+ display: inline-flex;
+ height: 44px;
+ padding: 4px 12px;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ border-radius: 12px;
+ border: 1px solid #6d48fe;
+ font-size: 14px;
+ font-family: "Roboto", sans-serif;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ background-color: var(--main-white);
+
+ /* Small shadow */
+ box-shadow: 0 1px 1px 0 #dbdbdb;
+
+ &:hover {
+ background: var(--light-purple);
+ }
+
+ &:active {
+ background: var(--accent-purple);
+ }
+`;
diff --git a/frontend/src/components/QuestList.jsx b/frontend/src/components/QuestList.jsx
new file mode 100644
index 000000000..74ac2724e
--- /dev/null
+++ b/frontend/src/components/QuestList.jsx
@@ -0,0 +1,121 @@
+// import { apiUrl } from '../../api';
+import styled from "styled-components";
+import { useEffect, useState } from "react";
+import { QuestCard } from "./cards/QuestCard";
+import { useQuestStore } from "../stores/useQuestStore";
+import { useUserStore } from "../stores/useUserStore";
+import { HeartAnimation } from "../assets/animation/Heart";
+import { motion, AnimatePresence } from 'framer-motion'
+
+export const QuestList = () => {
+ const { quests, error, isLoading, fetchQuests, deleteQuest, completeQuest } =
+ useQuestStore();
+ const { user } = useUserStore();
+ const [ isVisible, setIsVisible ] = useState(false)
+
+ useEffect(() => {
+ fetchQuests();
+ }, [user]);
+
+ if (isLoading) return Loading quests...
;
+ if (error) return {error}
;
+
+ return (
+
+
+
+ My quests to conquer:
+ setIsVisible(!isVisible)}>
+ {isVisible ? 'Hide' : 'Show'}
+
+
+
+
+ {isVisible && (
+
+
+ {quests
+ .filter((quest) => !quest.done)
+ .map((quest) => (
+
+ ))}
+
+ )}
+
+
+ )
+}
+
+const Container = styled.div`
+ padding: 10px 0;
+ align-items: center;
+ width: 100%;
+`;
+
+const HeadingContainer = styled.div`
+ display: flex;
+ height: 64px;
+ padding: 0 16px;
+ gap: 8px;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+ width: 100%;
+ align-self: stretch;
+ border-radius: 12px;
+ background: var(--main-white);
+`;
+
+const Button = styled.button`
+ display: inline-flex;
+ height: 44px;
+ padding: 4px 12px;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ border-radius: 12px;
+ border: 1px solid #6d48fe;
+ font-size: 14px;
+ font-family: "Roboto", sans-serif;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ background-color: var(--main-white);
+
+ /* Small shadow */
+ box-shadow: 0 1px 1px 0 #dbdbdb;
+
+ &:hover {
+ background: var(--light-purple)
+ }
+
+ &:active {
+ background: var(--accent-purple)
+ }
+`;
+
+// const Div = styled.div`
+// display: flex;
+// flex-direction: column;
+// background-color: var(--primary-color);
+// margin: 10px;
+// border-radius: 12px;
+// `
+
+// 1. GET request from api to get all quests quests/all
+// 2. map over quests, for each quest create a display of message, time and catagory
diff --git a/frontend/src/components/SignupForm.jsx b/frontend/src/components/SignupForm.jsx
new file mode 100644
index 000000000..16e8d4eb7
--- /dev/null
+++ b/frontend/src/components/SignupForm.jsx
@@ -0,0 +1,188 @@
+import styled from 'styled-components'
+import { Link, useNavigate } from 'react-router-dom'
+import { useEffect, useState } from 'react'
+import { apiUrl } from '../../api'
+import { useUserStore } from '../stores/useUserStore'
+import { useQuestStore } from '../stores/useQuestStore'
+
+export const SignupForm = () => {
+ // State variables to store form input values
+ const [name, setName] = useState('')
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+
+ // State to store and display error messages
+ const [error, setError] = useState(null)
+
+ // Hook to navigate to different routes
+ const navigate = useNavigate()
+
+ const login = useUserStore((state) => state.login)
+ const fetchQuests = useQuestStore((state) => state.fetchQuests)
+
+ // When form is submitted, this function runs
+ const handleSubmit = async (event) => {
+ event.preventDefault()
+ setError(null)
+
+ if (!name.trim()) {
+ setError('Please enter a username.')
+ return
+ }
+
+ if (!email.trim()) {
+ setError('Please enter your email.')
+ return
+ }
+
+ if (!password.trim()) {
+ setError('Please enter a password.')
+ return
+ }
+
+ if (password.length < 8) {
+ setError('Password must be at least 8 characters.')
+ return
+ }
+
+ try {
+ // send post request to signup endpoint with user data
+ const response = await fetch(apiUrl + '/signup', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ name, email, password })
+ })
+
+ const data = await response.json()
+ console.log('Server response data:', data)
+
+ // check if response is unsuccessful
+ if (!response.ok) {
+ if (response.status === 409) {
+ setError('An account with that email already exists.')
+ } else {
+ setError(data.message || 'Signup failed. Please try again.')
+ }
+ return
+ }
+
+ console.log('Signup successful:', data)
+
+ // // Store the access token in browser's localStorage for future requests
+ // localStorage.setItem('accessToken', data.accessToken)
+ // // Store the user ID
+ // localStorage.setItem('userId', data.id)
+ // // Store username
+ // localStorage.setItem('userName', name)
+ // // store email
+ // localStorage.setItem('userEmail', email)
+
+ // on signup also login
+ login({
+ accessToken: data.accessToken,
+ userId: data.id,
+ email: email,
+ name: name
+ })
+
+ // When signed up successfully and quests are fetched -> redirect to profile page
+ await fetchQuests(data.accessToken)
+ navigate('/')
+
+ // Clear the form inputs
+ setName('')
+ setEmail('')
+ setPassword('')
+
+ } catch (error) {
+ console.error('Error:', error)
+ setError('Something went wrong. Please try again')
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+
+const Form = styled.form`
+ margin: 10px;
+ padding: 16px;
+ border-radius: 12px;
+ border: 2px solid #B594FF;
+ background: #FFF;
+ box-shadow: 2px 4px 4px 0 #DBDBDB;
+ text-align: center;
+`
+const Label = styled.label`
+ display: flex;
+ flex-direction: column;
+`
+
+const Input = styled.input`
+ border: none;
+ border-radius: 12px;
+ padding: 15px 16px 14px 16px;
+ background: #F4F0FF;
+ margin: 16px 0;
+ width: 100%;
+ font-size: 16px;
+`
+
+const Button = styled.button`
+ display: flex;
+ height: 54px;
+ width: 100%;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ border-radius: 12px;
+ border: 1px solid #1D30CE;
+ background: var(--medium-purple);
+ box-shadow: 2px 4px 4px 0 rgba(139, 139, 139, 0.30);
+ color: white;
+ font-family: "Pixelify Sans", sans-serif;
+ font-size: 20px;
+
+ &:hover {
+ background: var(--dark-purple);
+ }
+
+ &:active {
+ background: var(--accent-purple);
+ }
+`
+
+const StyledLink = styled(Link)`
+ text-decoration: none;
+ color: black;
+ margin: 20px;
+ display: block;
+
+ &:hover{
+ transform: scale(1.1)
+ }
+`
+
diff --git a/frontend/src/components/StreakDisplay.jsx b/frontend/src/components/StreakDisplay.jsx
new file mode 100644
index 000000000..5f49a17f7
--- /dev/null
+++ b/frontend/src/components/StreakDisplay.jsx
@@ -0,0 +1,38 @@
+import styled from "styled-components";
+
+import { useUserStore } from "../stores/useUserStore";
+
+export const Streak = () => {
+ const { streak, isStreakLoading } = useUserStore();
+
+ return (
+
+ My streak:
+ {isStreakLoading ? (
+ Loading...
+ ) : (
+ {streak}
+ )}
+
+ );
+};
+
+const StreakWrapper = styled.div`
+ display: flex;
+ gap: 8px;
+ justify-content: flex-start;
+ align-items: center;
+ margin-top: 20px;
+`;
+
+const StreakNumber = styled.span`
+ font-weight: 700;
+ font-size: 20px;
+ font-family: "Roboto", sans-serif;
+ font-style: normal;
+ line-height: normal;
+ color: var(--dark-purple);
+ background-color: var(--light-pink);
+ padding: 2px 8px;
+ border-radius: 4px;
+`;
diff --git a/frontend/src/components/cards/FriendQuestCard.jsx b/frontend/src/components/cards/FriendQuestCard.jsx
new file mode 100644
index 000000000..af52946eb
--- /dev/null
+++ b/frontend/src/components/cards/FriendQuestCard.jsx
@@ -0,0 +1,215 @@
+import styled, { keyframes } from "styled-components";
+import { useState, useEffect } from "react";
+import { apiUrl } from "../../../api";
+import { useUserStore } from "../../stores/useUserStore";
+
+export const FriendQuestCard = ({
+ id,
+ message,
+ createdBy,
+ category,
+ timeNeeded,
+ doneAt,
+ kudos,
+ isNew,
+}) => {
+ const { user } = useUserStore();
+ const [kudosCount, setKudosCount] = useState(kudos || 0);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ setKudosCount(kudos || 0);
+ }, [kudos]);
+
+ //conditional check
+ const handleClick = async () => {
+ if (loading) return;
+
+ setLoading(true);
+ //setKudosCount((prev) => prev + 1);
+
+ try {
+ const response = await fetch(apiUrl + `/quests/${id}/kudos`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: user?.accessToken,
+ },
+ });
+
+ const data = await response.json();
+ //console.log("STATUS:", response.status);
+ //console.log("DATA:", data);
+
+ if (!response.ok || !data.success) {
+ throw new Error(data.message || "Failed to give kudos");
+ }
+
+ setKudosCount(data.response.kudos);
+ } catch (err) {
+ //setKudosCount((prev) => prev - 1);
+ console.error(err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+ {createdBy.name || "User"}
+ {/* */}
+
+
+ Kudos: {kudosCount}
+
+
+
+
+
+ {message}
+ {category}
+ {timeNeeded} min
+
+
+
+ {new Date(doneAt).toLocaleDateString("en-GB", {
+ day: "numeric",
+ month: "short",
+ year: "numeric",
+ hour: "2-digit",
+ minute: "2-digit",
+ })}
+
+
+
+
+ );
+};
+
+// Styles
+
+const slideIn = keyframes`
+ 0% {
+ transform: translateY(-20px);
+ opacity: 0;
+ }
+
+ 100% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+`;
+const MainWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ background-color: #ffffff;
+ margin: 4px;
+ padding: 12px 12px;
+ border-radius: 12px;
+ width: 100%;
+ border: 1px solid var(--accent-color);
+`;
+
+const Cardheader = styled.div`
+ display: flex;
+ background-color: var(--main-bg-color);
+ justify-content: space-between;
+ margin: 0;
+ padding: 5px;
+ border-radius: 12px;
+`;
+
+const Name = styled.h1`
+ font-size: 15px;
+ font-weight: 700;
+ padding: 12px 12px;
+`;
+
+const Avatar = styled.img`
+ width: 42px;
+ height: 42px;
+ border-radius: 100%;
+ object-fit: cover;
+`;
+
+const QuestInfoWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ margin: 0;
+ padding: 5px;
+`;
+
+const Quest = styled.div`
+ font-size: 16px;
+ font-weight: 400;
+`;
+
+const Category = styled.div`
+ font-size: 14px;
+ font-weight: 400;
+`;
+
+const Time = styled.div`
+ font-size: 12px;
+ font-weight: 400;
+`;
+
+const TimeP = styled.p`
+ font-family: Roboto;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ color: var(--medium-purple);
+ margin: 0;
+`;
+
+const Done = styled.div`
+ font-size: 10px;
+ font-weight: 400;
+`;
+
+const ActionWrapper = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+`;
+
+const Button = styled.button`
+ display: flex;
+ padding: 5px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ flex-shrink: 0;
+ align-self: stretch;
+ border-radius: 12px;
+ border: 2px solid #e9628c;
+ background: #f497b4;
+ box-shadow: 2px 4px 4px 0 rgba(139, 139, 139, 0.3);
+ cursor: pointer;
+ font-family: "Pixelify Sans", sans-serif;
+
+ /* Small shadow */
+ box-shadow: 0 1px 1px 0 #dbdbdb;
+
+ &:hover {
+ background: var(--secondary-button-color);
+ }
+
+ &:active {
+ background: #e48187;
+ }
+`;
+
+const TopInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+`;
+
+const BottomRow = styled.div`
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 8px;
+`;
diff --git a/frontend/src/components/cards/InfoCard.jsx b/frontend/src/components/cards/InfoCard.jsx
new file mode 100644
index 000000000..06e94e972
--- /dev/null
+++ b/frontend/src/components/cards/InfoCard.jsx
@@ -0,0 +1,34 @@
+import styled from 'styled-components'
+
+export const InfoCard = ({ title, description, icon }) => {
+ return (
+
+ {icon}
+
+
{title}
+
{description}
+
+
+ )
+}
+
+const Div = styled.div`
+ display: flex;
+ background-color: var(--accent-color);
+ margin: 10px;
+ justify-content: center;
+ border-radius: 12px;
+ padding: 16px;
+ gap: 10px;
+ border: 2px solid var(--accent-color);
+ background: #FFFFFF;
+`;
+
+const H2 = styled.h2`
+ font-family: "Pixelify Sans", sans-serif;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ margin: 0;
+`
diff --git a/frontend/src/components/cards/LibraryQuestCard.jsx b/frontend/src/components/cards/LibraryQuestCard.jsx
new file mode 100644
index 000000000..3992de8e7
--- /dev/null
+++ b/frontend/src/components/cards/LibraryQuestCard.jsx
@@ -0,0 +1,107 @@
+import styled from "styled-components";
+
+export const LibraryQuestCard = ({
+ message,
+ timeNeeded,
+ category,
+ onAdd,
+ id,
+}) => {
+ return (
+
+
+
+
{message}
+ {/*
Category: {category}
*/}
+
+ {timeNeeded} min
+
+
+
+ +
+
+ );
+};
+
+const Container = styled.div`
+ display: flex;
+ padding: 8px 16px;
+ margin: 4px auto;
+ width: 100%;
+ justify-content: space-between;
+ align-items: center;
+ align-self: stretch;
+
+ border-radius: 12px;
+ border: 1px solid var(--accent-color);
+
+ background-color: var(--main-white);
+ box-shadow: 0 2px 2px 0 #dbdbdb;
+`;
+
+const P = styled.p`
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ margin: 0;
+`;
+
+const ChipContainer = styled.div`
+ width: fit-content;
+ padding: 2px 4px;
+ justify-content: center;
+ align-items: center;
+
+ border-radius: 4px;
+ background: var(--light-pink);
+`;
+
+const TimeP = styled.p`
+ font-family: Roboto;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ color: var(--medium-purple);
+ margin: 0;
+`;
+
+const ButtonAdd = styled.button`
+ display: flex;
+ width: 44px;
+ height: 44px;
+ min-height: 44px;
+ min-width: 44px;
+ padding: 4px;
+ justify-content: center;
+ align-items: center;
+
+ border-radius: 100%;
+ border: none;
+
+ font-size: 14px;
+ font-family: "Roboto", sans-serif;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ color: var(--main-white);
+
+ background-color: var(--secondary-button-color);
+
+ /* Small shadow */
+ box-shadow: 0 2px 2px 0 #dbdbdb;
+
+ &:hover {
+ background: var(--accent-color);
+ }
+
+ &:active {
+ background: #e48187;
+ }
+`;
+
+const Div = styled.div`
+ display: flex;
+ gap: 5px;
+`;
diff --git a/frontend/src/components/cards/QuestCard.jsx b/frontend/src/components/cards/QuestCard.jsx
new file mode 100644
index 000000000..97767893c
--- /dev/null
+++ b/frontend/src/components/cards/QuestCard.jsx
@@ -0,0 +1,119 @@
+import styled from "styled-components";
+import { Checkbox } from "../Checkbox";
+
+export const QuestCard = ({
+ message,
+ category,
+ timeNeeded,
+ onDelete,
+ id,
+ handleChecked,
+ done,
+}) => {
+ return (
+
+
+
handleChecked(id, event.target.checked)}
+ />
+ {/* {
+ console.log(event);
+ handleChecked(id, event.target.checked);
+ }}
+ /> */}
+
+
{message}
+ {/*
Category: {category}
*/}
+
+ {timeNeeded} min
+
+
+
+ onDelete(id)}>Delete
+
+ );
+};
+
+const Container = styled.div`
+ display: flex;
+ padding: 8px 16px;
+ margin: 4px auto;
+ width: 100%;
+ justify-content: space-between;
+ align-items: center;
+ align-self: stretch;
+
+ border-radius: 12px;
+ border: 1px solid var(--accent-color);
+
+ background-color: var(--main-white);
+ box-shadow: 0 2px 2px 0 #dbdbdb;
+`;
+
+const Div = styled.div`
+ display: flex;
+ gap: 8px;
+ justify-content: flex-start;
+`;
+
+const P = styled.p`
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ margin: 0;
+`;
+
+const ChipContainer = styled.div`
+ width: fit-content;
+ padding: 2px 4px;
+ justify-content: center;
+ align-items: center;
+
+ border-radius: 4px;
+ background: var(--light-pink);
+`;
+
+const TimeP = styled.p`
+ font-family: Roboto;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ color: var(--medium-purple);
+ margin: 0;
+`;
+
+const ButtonDelete = styled.button`
+ display: inline-flex;
+ height: 44px;
+ padding: 4px 12px;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ border-radius: 12px;
+ border: 1px solid #6d48fe;
+ font-size: 14px;
+ font-family: "Roboto", sans-serif;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ background-color: var(--main-white);
+
+ /* Small shadow */
+ box-shadow: 0 1px 1px 0 #dbdbdb;
+
+ &:hover {
+ background: var(--light-purple)
+ }
+
+ &:active {
+ background: var(--accent-purple)
+ }
+`;
+
+
diff --git a/frontend/src/components/icons/Dice.jsx b/frontend/src/components/icons/Dice.jsx
new file mode 100644
index 000000000..a1b2d6257
--- /dev/null
+++ b/frontend/src/components/icons/Dice.jsx
@@ -0,0 +1,19 @@
+export const Dice = () => {
+ return (
+
+
+
+ );
+};
diff --git a/frontend/src/components/icons/Friends.jsx b/frontend/src/components/icons/Friends.jsx
new file mode 100644
index 000000000..8493d41bd
--- /dev/null
+++ b/frontend/src/components/icons/Friends.jsx
@@ -0,0 +1,7 @@
+export const Friends = () => {
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/icons/GitHub.jsx b/frontend/src/components/icons/GitHub.jsx
new file mode 100644
index 000000000..78083187a
--- /dev/null
+++ b/frontend/src/components/icons/GitHub.jsx
@@ -0,0 +1,7 @@
+export const GitHub = () => {
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/icons/Heart.jsx b/frontend/src/components/icons/Heart.jsx
new file mode 100644
index 000000000..cd2507221
--- /dev/null
+++ b/frontend/src/components/icons/Heart.jsx
@@ -0,0 +1,21 @@
+export const Heart = () => {
+ return (
+
+
+
+ );
+};
diff --git a/frontend/src/components/icons/Library.jsx b/frontend/src/components/icons/Library.jsx
new file mode 100644
index 000000000..31abb4cfa
--- /dev/null
+++ b/frontend/src/components/icons/Library.jsx
@@ -0,0 +1,9 @@
+export const Library = () => {
+ return (
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/icons/Linkedin.jsx b/frontend/src/components/icons/Linkedin.jsx
new file mode 100644
index 000000000..c47b7cf83
--- /dev/null
+++ b/frontend/src/components/icons/Linkedin.jsx
@@ -0,0 +1,7 @@
+export const Linkedin = () => {
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/icons/Star.jsx b/frontend/src/components/icons/Star.jsx
new file mode 100644
index 000000000..824852f09
--- /dev/null
+++ b/frontend/src/components/icons/Star.jsx
@@ -0,0 +1,22 @@
+export const Star = () => {
+ return (
+
+
+
+
+ );
+};
diff --git a/frontend/src/icons/CheckboxChecked.jsx b/frontend/src/icons/CheckboxChecked.jsx
new file mode 100644
index 000000000..142318766
--- /dev/null
+++ b/frontend/src/icons/CheckboxChecked.jsx
@@ -0,0 +1,16 @@
+export const CheckboxChecked = () => {
+ return (
+
+
+
+ );
+};
diff --git a/frontend/src/icons/CheckboxEmpty.jsx b/frontend/src/icons/CheckboxEmpty.jsx
new file mode 100644
index 000000000..969975efc
--- /dev/null
+++ b/frontend/src/icons/CheckboxEmpty.jsx
@@ -0,0 +1,16 @@
+export const CheckboxEmpty = () => {
+ return (
+
+
+
+ );
+};
diff --git a/frontend/src/icons/HeartIcon.jsx b/frontend/src/icons/HeartIcon.jsx
new file mode 100644
index 000000000..94284c332
--- /dev/null
+++ b/frontend/src/icons/HeartIcon.jsx
@@ -0,0 +1,19 @@
+const HeartIcon = (props) => {
+ return (
+
+
+
+ );
+};
+
+export default HeartIcon;
diff --git a/frontend/src/library.json b/frontend/src/library.json
new file mode 100644
index 000000000..284d51e64
--- /dev/null
+++ b/frontend/src/library.json
@@ -0,0 +1,38 @@
+[
+ {
+ "message": "Clean kitchen sink",
+ "timeNeeded": 10
+ },
+ {
+ "message": "Unload washing machine",
+ "timeNeeded": 15
+ },
+ {
+ "message": "Clean the toilet",
+ "timeNeeded": 18
+ },
+ {
+ "message": "Put things at their places from the work table",
+ "timeNeeded": 12
+ },
+ {
+ "message": "Put clothes in the closet or in laundry",
+ "timeNeeded": 8
+ },
+ {
+ "message": "Dust one room",
+ "timeNeeded": 15
+ },
+ {
+ "message": "Load washing machine one time",
+ "timeNeeded": 15
+ },
+ {
+ "message": "Water the plants",
+ "timeNeeded": 5
+ },
+ {
+ "message": "Clean one pair of shoes",
+ "timeNeeded": 12
+ }
+]
\ No newline at end of file
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index 51294f399..ce897ee2e 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -2,9 +2,12 @@ import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "./App.jsx";
import "./index.css";
+import { BrowserRouter } from "react-router-dom";
ReactDOM.createRoot(document.getElementById("root")).render(
-
-
+
+
+
+ ,
);
diff --git a/frontend/src/pages/AboutPage.jsx b/frontend/src/pages/AboutPage.jsx
new file mode 100644
index 000000000..68ae51a85
--- /dev/null
+++ b/frontend/src/pages/AboutPage.jsx
@@ -0,0 +1,22 @@
+import styled from 'styled-components'
+import { Home } from './HomePage'
+import { useUserStore } from '../stores/useUserStore'
+
+export const About = () => {
+ const { user } = useUserStore()
+
+ return (
+
+ {!user && Log in to access the app
}
+ {user && }
+
+ );
+};
+
+const PageWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 0 20px;
+`;
diff --git a/frontend/src/pages/FriendFeedPage.jsx b/frontend/src/pages/FriendFeedPage.jsx
new file mode 100644
index 000000000..576f08f78
--- /dev/null
+++ b/frontend/src/pages/FriendFeedPage.jsx
@@ -0,0 +1,126 @@
+import styled from "styled-components";
+import { useState, useEffect } from "react";
+import { Navbar } from "../components/Navbar";
+import { FriendQuestCard } from "../components/cards/FriendQuestCard";
+import { apiUrl } from "../../api";
+import { useUserStore } from "../stores/useUserStore";
+
+export const FriendFeed = () => {
+ const { user } = useUserStore();
+ const [friendsQuests, setFriendsQuests] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ /* const [user, setUser] = useState(() => {
+ const saved = localStorage.getItem("user");
+ return saved ? JSON.parse(saved) : null;
+ }); */
+
+ /* const fetchFriends = async () => {
+ try {
+ const accessToken = localStorage.getItem("accessToken");
+
+ const response = await fetch(apiUrl + "/friends", {
+ headers: { Authorizarion: `Bearer ${accessToken}` },
+ });
+ } catch (err) {
+ setError(err.message);
+ }
+ }; */
+
+ useEffect(() => {
+ const fetchFeed = async () => {
+ try {
+ const response = await fetch(apiUrl + "/feed/quests", {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: user?.accessToken,
+ },
+ });
+
+ if (!response.ok) throw new Error("Couldn't fetch feed");
+
+ const data = await response.json();
+ setFriendsQuests(data);
+ } catch (err) {
+ setError("Couldn't load quests feed");
+ console.error(err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (user) fetchFeed();
+ }, [user]);
+
+ if (loading) return Loading feed...
;
+ if (error) return {error}
;
+
+ return (
+ // pass params from login and register forms to an authentication component to then add authentication after H2
+ // smth like {!user ? ()}
+
+
+ Friends completed quests
+
+
+ {/* */}
+ {friendsQuests.map((quest, index) => (
+
+ ))}
+
+ );
+};
+
+//Styles
+
+const HeaderWrapper = styled.div`
+ margin: 16px 0 4px;
+`;
+
+const PageWrapper = styled.main`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 0 20px;
+`;
+
+const H1 = styled.h1`
+ margin: 20px 0;
+`;
+
+// //add error handling
+// setLoading(true);
+// fetch(apiUrl + "/friends", {
+// headers: {
+// "Content-Type": "application/json",
+// ...accessToken(),
+// },
+// })
+// .then((res) => {
+// if (!res.ok) throw new Error("Couldn't fetch data");
+// return res.json();
+// })
+// .then((data) => {
+// setFriendsQuests(data);
+// })
+// .catch((err) => {
+// console.error(err);
+// setError("Coulndn't load friends quests");
+// })
+// .finally(() => setLoading(false));
diff --git a/frontend/src/pages/GiveUpPage.jsx b/frontend/src/pages/GiveUpPage.jsx
new file mode 100644
index 000000000..a66aa46da
--- /dev/null
+++ b/frontend/src/pages/GiveUpPage.jsx
@@ -0,0 +1,66 @@
+import { Link } from "react-router-dom"
+import styled from "styled-components"
+
+export const GiveUp = () => {
+ return (
+
+
+
please dont, you can at least start! π₯²
+ Ok, fine... take me back
+ Give up
+
+
+ )
+}
+
+const Div = styled.div`
+ display: flex;
+ flex-direction: column;
+ background-color: var(--main-white);
+ max-height: 280px;
+ max-width: 350px;
+ margin: 10px;
+ border-radius: 12px;
+ padding: 10px;
+ border: 2px solid #B594FF;
+ text-align: center;
+ align-items: center;
+`
+
+const Button = styled.button`
+ display: flex;
+ height: 54px;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ border-radius: 12px;
+ border: 1px solid #1D30CE;
+ background: var(--medium-purple);
+ box-shadow: 2px 4px 4px 0 rgba(139, 139, 139, 0.30);
+ color: white;
+ font-family: "Pixelify Sans", sans-serif;
+ margin: 15px 0;
+ font-size: 20px;
+
+ &:hover {
+ background: var(--dark-purple)
+ }
+
+ &:active {
+ background: var(--accent-purple)
+ }
+`
+
+const StyledLink = styled(Link)`
+ text-decoration: none;
+ display: contents;
+`
+
+const PageWrapper = styled.main`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 0 20px;
+`
\ No newline at end of file
diff --git a/frontend/src/pages/HomePage.jsx b/frontend/src/pages/HomePage.jsx
new file mode 100644
index 000000000..b04cddec9
--- /dev/null
+++ b/frontend/src/pages/HomePage.jsx
@@ -0,0 +1,183 @@
+import styled from "styled-components";
+import { InfoCard } from "../components/cards/InfoCard";
+import { Star } from "../components/icons/Star";
+import { Heart } from "../components/icons/Heart";
+import { Library } from "../components/icons/Library";
+import { Dice } from "../components/icons/Dice";
+import { Friends } from "../components/icons/Friends";
+import { Linkedin } from "../components/icons/Linkedin";
+import { GitHub } from "../components/icons/GitHub";
+import { Link } from "react-router-dom";
+import HeartIcon from "../icons/HeartIcon";
+
+export const Home = () => {
+ return (
+
+
+
+ Welcome!
+ Never spend energy on choosing your chores again
+
+
+
+
+
+ Start your quest
+
+
+ Find your friends
+
+
+
+
+ How it works
+
+ }
+ title="1. Add your own quests"
+ description="Got something specific in mind? Add your own custom quests and how long they take to complete."
+ />
+
+ }
+ title="2. Don't know what to add?"
+ description="Browse our quest library! Plenty of ideas for self-care, fitness, creativity, and productivity."
+ />
+
+ }
+ title="3. Get a random daily quest from your list"
+ description="Every day we pick a quest from your list. All you have to do is tell us how much time you have. And we'll roll the dice!"
+ />
+
+ }
+ title="4. Keep keep streaks & share"
+ description="Keep your avatar happy with daily streaks. Challenge friends and share your progress!"
+ />
+
+
+
Ready to quest?
+
Your avatar is waiting. Don't leave them hanging!
+
+ Let's go!
+
+
+
+ Made by:
+
+
+ Nicolina
+
+
+
+
+
+
+
+ Julia
+
+
+
+
+
+
+
+ );
+};
+
+export const Button = styled.button`
+ display: flex;
+ height: 54px;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ flex-shrink: 0;
+ align-self: stretch;
+ border-radius: 12px;
+ border: 2px solid var(--secondary-button-color);
+ background: var(--accent-color);
+ box-shadow: 2px 4px 4px 0 rgba(139, 139, 139, 0.3);
+ margin: 20px 0;
+ cursor: pointer;
+ font-family: "Pixelify Sans", sans-serif;
+
+ &:hover {
+ background: var(--secondary-button-color);
+ }
+
+ &:active {
+ background: #e48187;
+ }
+`;
+
+const ButtonDiv = styled.div`
+ display: flex;
+ gap: 10px;
+`;
+
+const Main = styled.main`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+`;
+
+const HeaderWrapper = styled.div`
+ border: 2px solid var(--accent-purple);
+ border-radius: 12px;
+ background: var(--main-white);
+ box-shadow: 2px 4px 4px 0 #dbdbdb;
+ border: 2px solid #b594ff;
+
+ display: flex;
+ flex-direction: column;
+ // justify-content: center;
+ align-items: center;
+
+ gap: 8px;
+ width: 100%;
+ max-width: 800px;
+
+ margin: 24px auto;
+ padding: 16px 16px;
+
+ box-sizing: border-box;
+
+ @media (min-width: 768px) {
+ margin: 24px auto;
+ }
+`;
+
+const Div = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+`;
+
+const CreatorDiv = styled.div`
+ display: flex;
+ gap: 20px;
+`;
+
+const LinksDiv = styled.div`
+ display: flex;
+ gap: 5px;
+`;
+
+const NameDiv = styled.div`
+ text-align: center;
+`;
+
+const H3 = styled.h3`
+ font-family: "Pixelify Sans", sans-serif;
+ font-size: 12px;
+`;
+
+const StyledLink = styled(Link)`
+ text-decoration: none;
+ display: contents;
+`;
+
+
diff --git a/frontend/src/pages/LoginPage.jsx b/frontend/src/pages/LoginPage.jsx
new file mode 100644
index 000000000..94364c003
--- /dev/null
+++ b/frontend/src/pages/LoginPage.jsx
@@ -0,0 +1,7 @@
+import { LoginForm } from "../components/LoginForm";
+
+export const Login = () => {
+ return ;
+};
+
+
diff --git a/frontend/src/pages/QuestPage.jsx b/frontend/src/pages/QuestPage.jsx
new file mode 100644
index 000000000..cc2c5f0e0
--- /dev/null
+++ b/frontend/src/pages/QuestPage.jsx
@@ -0,0 +1,250 @@
+import styled from 'styled-components'
+import { useQuestStore } from '../stores/useQuestStore'
+import { useUserStore } from '../stores/useUserStore'
+import { useReducer } from 'react'
+import { Link, useNavigate } from 'react-router-dom'
+import { Checkbox } from '../components/Checkbox'
+
+// Defines initial state in one object instead of three separate useState calls
+const initialState = {
+ timeAvailable: '',
+ randomQuest: null,
+ noMatch: ''
+}
+
+// Reducer function. "When this action happens -> update state"
+const reducer = (state, action) => {
+ const { type } = action
+
+ if (type === 'setTime') {
+ // when user types in input, update timeAvailable
+ return { ...state, timeAvailable: action.time }
+ } else if (type === 'setQuest') {
+ // when a quest is found, save it
+ return { ...state, randomQuest: action.quest }
+ } else if (type === 'noMatch') {
+ // when something goes wrong, save the error message
+ // ...state "keep everything else the same, just change this one thing"
+ return { ...state, noMatch: action.message }
+ } else {
+ return state
+ }
+}
+
+export const Quests = () => {
+const { fetchQuests, completeQuest, quests } = useQuestStore()
+const { user } = useUserStore()
+const navigate = useNavigate()
+
+// state = current state object, dispatch = function to trigger state changes
+const [state, dispatch] = useReducer(reducer, initialState)
+// state = the current values of timeAvailable, randomQuest, noMatch
+// dispatch = the function you call to trigger a state change
+
+// Get the live version of the quest from the store so checkbox stays in sync
+const currentQuest = quests.find(quest => quest._id === state.randomQuest?._id)
+
+ const handleComplete = async (id, checked) => {
+ await completeQuest(id, checked);
+ if (checked) {
+ //alert("Great work! π");
+ navigate("/rewards");
+ }
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault()
+
+ // dispatch actions: instead of doing setTimeAvailable('30)
+ if (!state.timeAvailable) {
+ dispatch({ type: 'noMatch', message:'Please enter how many minutes you have available'})
+ return
+ }
+
+ const freshQuests = await fetchQuests()
+
+ if (freshQuests.length === 0) {
+ dispatch({ type: "noMatch", message: "You have no quests yet!" });
+ return;
+ }
+
+ // Filter out already completed quests first
+ const notDone = freshQuests.filter(quest => !quest.done)
+
+ if (notDone.length === 0) {
+ dispatch({ type: 'noMatch', message: 'You have no quests left to do!' })
+ return
+ }
+
+ // Filter quests. Show only quests under chosen time frame
+ const filtered = notDone.filter(quest => quest.timeNeeded <= Number(state.timeAvailable))
+
+ if (filtered.length === 0) {
+ dispatch({
+ type: "noMatch",
+ message: `Looks like you don't have quests under ${state.timeAvailable} minutes, try again!`,
+ });
+ return;
+ }
+
+ // Problem initially: gave random quest below requested time available, but could giv quest a LOT shorter then inputted time. Solved by: first sort timeNeeded closest to available time first.
+ const sorted = filtered.sort((a, b) => b.timeNeeded - a.timeNeeded)
+
+ // THEN: Take the top 3 closest matches and pick randomly from them
+ const topMatches = sorted.slice(0, 3)
+ const random = topMatches[Math.floor(Math.random() * topMatches.length)]
+ dispatch({ type: 'setQuest', quest: random })
+ }
+
+
+ return (
+
+ {!state.randomQuest && (
+
+ )}
+ {state.randomQuest &&
+
+
Ok {user.email?.split('@')[0]}, here is your quest of the day:
+
+
+ handleComplete(state.randomQuest._id, event.target.checked)}
+ />
+
+
+ {state.randomQuest.message}
+ {state.randomQuest.timeNeeded} min
+
+
+
+
Give up
+
+
+ }
+
+ )
+}
+
+const Form = styled.form`
+ display: flex;
+ flex-direction: column;
+ background-color: var(--main-white);
+ max-height: 280px;
+ max-width: 350px;
+ margin: 10px;
+ border-radius: 12px;
+ padding: 10px;
+ border: 2px solid #B594FF;
+ text-align: center;
+`
+
+const Label = styled.label`
+ display: flex;
+ flex-direction: column;
+`;
+
+const Div = styled.div`
+ display: flex;
+ flex-direction: column;
+ background-color: #FFFFFF;
+ max-height: 280px;
+ max-width: 350px;
+ margin: 10px;
+ border-radius: 12px;
+ padding: 10px;
+ border: 2px solid #B594FF;
+ text-align: center;
+`
+
+const Input = styled.input`
+ background-color: var(--background-light-purple);
+ display: flex;
+ height: 52px;
+ padding: 15px 16px 14px 16px;
+ align-items: center;
+ gap: 10px;
+ align-self: stretch;
+ border-radius: 12px;
+ border: none;
+ margin-top: 16px;
+`
+
+const Button = styled.button`
+ display: flex;
+ height: 54px;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ border-radius: 12px;
+ border: 1px solid #1D30CE;
+ background: var(--medium-purple);
+ box-shadow: 2px 4px 4px 0 rgba(139, 139, 139, 0.30);
+ color: white;
+ font-family: "Pixelify Sans", sans-serif;
+ margin: 15px 0;
+ font-size: 20px;
+
+ &:hover {
+ background: var(--dark-purple)
+ }
+
+ &:active {
+ background: var(--accent-purple)
+ }
+`
+
+const QuestDiv = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ background-color: #F5F0FF;
+ border-radius: 12px;
+ padding: 10px;
+ gap: 10px;
+ margin: 20px 0;
+`
+
+const P = styled.p`
+ font-size: 18px;
+`
+
+const TimeP = styled.p`
+ font-size: 12px;
+`
+
+const CompleteDiv = styled.div`
+ display: flex;
+ align-items: center;
+`
+
+const TextDiv = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ text-align: left;
+`
+
+const PageWrapper = styled.main`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 0 20px;
+`;
+// user does something β dispatch is called β reducer runs and returns new state β component re-renders with new state.
\ No newline at end of file
diff --git a/frontend/src/pages/RewardPage.jsx b/frontend/src/pages/RewardPage.jsx
new file mode 100644
index 000000000..2cc77dff0
--- /dev/null
+++ b/frontend/src/pages/RewardPage.jsx
@@ -0,0 +1,106 @@
+import styled from "styled-components";
+import { useEffect, useState } from "react";
+
+import { Link, Navigate, useNavigate } from "react-router-dom";
+import { MemeOfTheDay } from "../components/MemeDaily";
+
+export const Rewards = () => {
+ //check if quest is checked as done
+ //navigate from "get quest of the day" to "reward"
+ //fetch random meme from api
+ //prevent fetching new meme on refresh
+
+ const navigate = useNavigate();
+
+ const handleClick = (e) => {
+ e.preventDefault();
+ navigate("/");
+ };
+
+ return (
+
+ X
+ You are on fire todayπ₯
+ Keep it up and don't forget to smile!
+
+ Back home
+
+ );
+};
+
+const PageWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ justify-content: center;
+ align-items: center;
+ padding: 0 20px;
+`;
+
+const ButtonClose = styled.button`
+ padding: 4px;
+ border: none;
+ background-color: transparent;
+ width: 44px;
+ height: 44px;
+ margin-top: 24px;
+
+ font-size: 16px;
+ font-weight: 400;
+ color: var(--main-text-color);
+
+ align-self: flex-end;
+
+ &:hover {
+ border-radius: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transform: scale(1.05);
+ background-color: var(--light-purple);
+ }
+
+ &:active {
+ border-radius: 12px;
+ transform: scale(0.85);
+ background-color: var(--accent-purple);
+ }
+`;
+
+const ButtonReward = styled.button`
+ display: inline-flex;
+ height: 44px;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ background-color: transparent;
+
+ border-radius: 12px;
+ border: 1px solid var(--medium-purple);
+ margin-bottom: 64px;
+
+ /* Small shadow */
+ box-shadow: 0 2px 2px 0 #dbdbdb;
+
+ &:hover {
+ font-weight: 500;
+ background-color: var(--light-purple);
+ transform: scale(1.05);
+ transition: ease-in-out;
+ }
+
+ &:active {
+ border: 1px solid var(--dark-purple);
+ background-color: var(--accent-purple);
+ transform: scale(0.85);
+ }
+`;
+
+//Fetch a random meme from api
+//Show it to user in the questOftheDay after it is checked DONE
+//button Close
+//Button hahaha
+
+
diff --git a/frontend/src/pages/SignupPage.jsx b/frontend/src/pages/SignupPage.jsx
new file mode 100644
index 000000000..1caa4ded9
--- /dev/null
+++ b/frontend/src/pages/SignupPage.jsx
@@ -0,0 +1,9 @@
+import { SignupForm } from '../components/SignupForm'
+
+export const Signup = () => {
+ return (
+ <>
+
+ >
+ )
+}
diff --git a/frontend/src/pages/UserProfilePage.jsx b/frontend/src/pages/UserProfilePage.jsx
new file mode 100644
index 000000000..30c293cc6
--- /dev/null
+++ b/frontend/src/pages/UserProfilePage.jsx
@@ -0,0 +1,102 @@
+import { Header } from "../components/Header";
+import styled from "styled-components";
+import { CreateQuest } from "../components/CreateQuest";
+import { QuestLibrary } from "../components/QuestLibrary";
+import { Avatar } from "../components/Avatar";
+//import { Strike } from "../components/StrikeDisplay";
+import { Link } from "react-router-dom";
+import { QuestList } from "../components/QuestList";
+import { useUserStore } from "../stores/useUserStore";
+import { CompletedQuests } from "../components/CompletedQuests";
+import { Home } from "./HomePage";
+
+export const UserProfile = () => {
+ const user = useUserStore((state) => state.user);
+
+ return (
+
+ {!user && }
+
+ {user && (
+ <>
+
+
+
+
+
+ Keep your avatar happy, click here to get daily quest!
+
+
+
+
+
+
+
+ >
+ )}
+
+ );
+};
+
+const PageWrapper = styled.main`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 0 20px;
+`;
+
+const Div = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ margin: 40px 0;
+ text-align: center;
+`;
+
+const H2 = styled.h2`
+ margin: 0 0 30px 0;
+`;
+
+const StyledLink = styled(Link)`
+ display: flex;
+ height: 54px;
+ min-width: 275px;
+ padding: 8px 16px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+
+ border-radius: 12px;
+ border: 1px solid #1d30ce;
+ background: #6d48fe;
+
+ /* Basic shadow */
+ box-shadow: 2px 4px 4px 0 #dbdbdb;
+
+ text-decoration: none;
+ color: var(--main-white);
+ font-family: "Pixelify Sans", sans-serif;
+ margin-top: 10px;
+
+ &:hover {
+ transform: scale(1.1);
+ border-radius: 12px;
+ background: #2d0fa3;
+
+ /* Hover shadow */
+ box-shadow: 1px 2px 2px 0 #bbb;
+
+ &:active {
+ transform: scale(0.85);
+ border-radius: 12px;
+ background: #b594ff;
+
+ /* Basic shadow */
+ box-shadow: 2px 4px 4px 0 #dbdbdb;
+ }
+ }
+`;
+
+
diff --git a/frontend/src/stores/useQuestStore.jsx b/frontend/src/stores/useQuestStore.jsx
new file mode 100644
index 000000000..ef03fca87
--- /dev/null
+++ b/frontend/src/stores/useQuestStore.jsx
@@ -0,0 +1,174 @@
+import { create } from "zustand";
+import { apiUrl } from "../../api";
+import { useUserStore } from "./useUserStore";
+import toast from "react-hot-toast";
+
+export const useQuestStore = create((set) => ({
+ // Initial state
+ quests: [],
+ libraryQuests: [],
+ error: null,
+ isLoading: false,
+
+ // fetch all quests for the logged in user
+ fetchQuests: async (token) => {
+ const accessToken = token || useUserStore.getState().user?.accessToken;
+
+ if (!accessToken) return [];
+
+ set({ isLoading: true, error: null });
+
+ try {
+ const response = await fetch(apiUrl + "/quests/all", {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: accessToken,
+ },
+ });
+ const data = await response.json();
+
+ if (!response.ok) {
+ set({ error: data.message, isLoading: false });
+ return [];
+ }
+
+ set({ quests: data.response, isLoading: false });
+ return data.response;
+ } catch (error) {
+ set({ error: "Something went wrong", isLoading: false });
+ return [];
+ }
+ },
+
+ // create new quest
+ createQuest: async (message, timeNeeded, category) => {
+ // get the accessToken and users id from the userStore
+ const { user } = useUserStore.getState();
+
+ if (!user?.accessToken) return { success: false, error: "Not logged in" };
+
+ try {
+ const response = await fetch(apiUrl + "/quests", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: user.accessToken,
+ },
+ body: JSON.stringify({
+ message,
+ timeNeeded: Number(timeNeeded),
+ category: category,
+ }),
+ });
+
+ const data = await response.json();
+
+ if (!response.ok) return { success: false, error: data.message };
+
+ set((state) => ({ quests: [...state.quests, data.response || data] }));
+ return { success: true };
+ } catch (error) {
+ return { success: false, error: "Something went wrong" };
+ }
+ },
+
+ // Delete quest
+ deleteQuest: async (questId) => {
+ const accessToken = useUserStore.getState().user?.accessToken;
+ try {
+ const response = await fetch(apiUrl + `/quests/${questId}`, {
+ method: "DELETE",
+ headers: { Authorization: accessToken },
+ });
+ if (!response.ok) throw new Error("Failed to delete quest");
+
+ // Remove from store
+ set((state) => ({
+ quests: state.quests.filter((q) => q._id !== questId),
+ }));
+
+ toast.success("Ok, quest was deleted");
+ } catch (error) {
+ console.error("Error deleting quest:", error);
+ toast.error("Couldn't delete quest");
+ }
+ },
+
+ // Check quest as complete
+ completeQuest: async (questId, done) => {
+ const { user, fetchStreak } = useUserStore.getState();
+
+ const accessToken = user?.accessToken;
+ if (!user?.accessToken) return;
+
+ try {
+ const response = await fetch(apiUrl + `/quests/${questId}/complete`, {
+ method: "PATCH",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: accessToken,
+ },
+ body: JSON.stringify({ done }),
+ });
+ if (!response.ok) throw new Error("Failed to check quest as done");
+
+ // Update the quest in the store
+ set((state) => ({
+ quests: state.quests.map((quest) =>
+ quest._id === questId ? { ...quest, done } : quest,
+ ),
+ }));
+
+ await fetchStreak();
+
+ if (done) {
+ toast.success("Wow, you did it again!");
+ } else {
+ toast("Ok, changed to undone");
+ }
+ } catch (err) {
+ console.error("Error completing quest:", err);
+ toast.error("Couldn't change quest status");
+ }
+ },
+
+ // Fetch quests from questLibrary
+ fetchLibraryQuests: async () => {
+ try {
+ const response = await fetch(apiUrl + "/quests/library");
+ const data = await response.json();
+
+ if (!response.ok) throw new Error("Couldn't fetch library quests");
+
+ set({ libraryQuests: data.response });
+ } catch (error) {
+ console.error(error);
+ }
+ },
+
+ // Duplicate quests from library to personal questlist
+ duplicateQuest: async (id) => {
+ const accessToken = useUserStore.getState().user?.accessToken;
+ if (!accessToken) return;
+
+ try {
+ const response = await fetch(apiUrl + `/quests/library/${id}/add`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: accessToken,
+ },
+ });
+
+ const data = await response.json();
+ if (!response.ok || !data.success)
+ return { success: false, error: data.message };
+
+ // Add the new quest to the user's quest list in the store
+ set((state) => ({ quests: [data.response, ...state.quests] }));
+ return { success: true };
+ } catch (err) {
+ return { succes: false, error: "Error adding quest from library" };
+ }
+ },
+}));
diff --git a/frontend/src/stores/useUserStore.jsx b/frontend/src/stores/useUserStore.jsx
new file mode 100644
index 000000000..c9e0d01b7
--- /dev/null
+++ b/frontend/src/stores/useUserStore.jsx
@@ -0,0 +1,81 @@
+import { create } from "zustand";
+import { apiUrl } from "../../api";
+
+export const useUserStore = create((set, get) => ({
+ // CREATE STATE (what is in the store (initial state))
+ user: null,
+ isLoggedIn: false,
+ streak: 0,
+ isStreakLoading: false,
+ streakError: null,
+
+ // SET STATE (change what is in the store)
+
+ // Call getUser when app mounts to access user from local storage
+ getUser: () => {
+ const accessToken = localStorage.getItem("accessToken");
+ const userId = localStorage.getItem("userId");
+ const email = localStorage.getItem("userEmail");
+ // FIXME Look into name. Name data not accessed. How to get username when user logs in
+ const name = localStorage.getItem("userName");
+
+ // If both accessToken and useId exist in localStorage -> update store state. Keeps user logged in after page refresh
+ if (accessToken && userId) {
+ set({ user: { accessToken, userId, email, name }, isLoggedIn: true });
+
+ get().fetchStreak();
+ }
+ },
+
+ login: (userData) => {
+ localStorage.setItem("accessToken", userData.accessToken);
+ localStorage.setItem("userId", userData.userId);
+ localStorage.setItem("userEmail", userData.email);
+ localStorage.setItem("userName", userData.userName);
+ set({ user: userData, isLoggedIn: true });
+
+ //streak is fetched after login
+ get().fetchStreak();
+ },
+
+ logout: () => {
+ localStorage.removeItem("accessToken");
+ localStorage.removeItem("userId");
+ localStorage.removeItem("userEmail");
+ localStorage.removeItem("userName");
+ set({ user: null, isLoggedIn: false, streak: 0 });
+ },
+
+ //Fetching streak:
+
+ fetchStreak: async () => {
+ const accessToken = get().user?.accessToken;
+ if (!accessToken) return 0;
+
+ set({ isStreakLoading: true, streakError: null });
+
+ try {
+ const response = await fetch(apiUrl + "/streaks", {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: accessToken,
+ },
+ });
+
+ const data = await response.json();
+
+ if (!response.ok) {
+ set({ streakError: data.message, isStreakLoading: false });
+ return 0;
+ }
+
+ set({ streak: data.response, isStreakLoading: false });
+
+ return data.response;
+ } catch (err) {
+ set({ streakError: "Something went wrong", isStreakLoading: false });
+ return 0;
+ }
+ },
+}));
diff --git a/frontend/src/styles/GlobalStyles.jsx b/frontend/src/styles/GlobalStyles.jsx
new file mode 100644
index 000000000..9a8b1b55e
--- /dev/null
+++ b/frontend/src/styles/GlobalStyles.jsx
@@ -0,0 +1,80 @@
+import { createGlobalStyle } from "styled-components";
+
+export const GlobalStyle = createGlobalStyle`
+ * {
+ box-sizing: border-box;
+ margin: 0px;
+ }
+
+ :root {
+ --main-bg-color: #FFF1F9;
+ --background-light-purple: #F4F0FF;
+
+ --main-text-color: #000000;
+ --main-white: #ffffff;
+
+ --primary-color: #FFF4CA;
+ --secondary-color: #DAFFE6;
+ --secondary-button-color: #E9628C;
+
+ --accent-color: #F497B4;
+ --accent-purple: #B594FF;
+
+ --medium-pink: #FFD2EC;
+ --medium-purple: #7954fd;
+ --light-yellow: #FFF4CA;
+ --light-pink: #FFE2F3;
+ --light-purple: #E5DEF8;
+ --dark-purple: #2D0FA3;
+
+ }
+
+ #root {
+ max-width: 500px;
+ width: 100%;
+ }
+
+ body {
+ margin: 0;
+ display: flex;
+ justify-content: center;
+ background-color: var(--main-bg-color);
+ font-family: "Roboto", sans-serif;
+ }
+
+ h1 {
+ font-family: "Pixelify Sans", sans-serif;
+ font-size: 26px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ }
+
+ h2 {
+ font-family: "Pixelify Sans", sans-serif;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ margin: 10px 0;
+ }
+
+ h3 {
+ font-family: "Roboto", sans-serif;
+ font-size: 18px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ }
+
+ p {
+ font-family: "Roboto", sans-serif;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ text-wrap: pretty;
+ }
+
+
+`;