Skip to content

Commit

Permalink
Quests: Data sync, Leaderboard (#219)
Browse files Browse the repository at this point in the history
* blog: introducing quests

* fix: image link tag

* blog: update quest post

* blog: update quests article

* blog: update quest overview

* feat: add table & endpoint to store quest datasets

* fix: minor copy details

* feat: display steps in the last week on dashboard

* feat: mobile - push dataset & view quest dashboard

* fix: duplicate interface import

* misc: embarassing error but will do for now
  • Loading branch information
oreHGA committed Jun 1, 2024
1 parent 2f3ecac commit 8e27f6b
Show file tree
Hide file tree
Showing 29 changed files with 712 additions and 126 deletions.
Binary file added frontend/public/images/blog/quest_flow.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion frontend/public/posts/2023-11-11-coldplunge.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Total time: 25mins
- Stroop task (5mins)
- Resting state (5mins)

<img src="/images/blog/coldplunge/doing_experiment.png" alt="Picture of Participant in Doing Cognitive Task" data-zoomable style="cursor: zoom-in";/>
<img src="/images/blog/coldplunge/doing_experiment.png" alt="Picture of Participant in Doing Cognitive Task" data-zoomable style="cursor: zoom-in;" />

## What did we observe?

Expand Down
70 changes: 70 additions & 0 deletions frontend/public/posts/2024-02-01-quests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Introducing Quests with Fusion Copilot
description: A simple and private way to do distributed research & perform activities in groups
publishedDate: 2024/05/30
coverImage: /images/blog/quest_flow.jpg
tags:
- research
- health
- studies
slug: quests
authors:
- name: "Ore Ogundipe"
---

Maintaining a lifestyle that contributes to your longevity can be difficult. We sometimes discount the amount of work required to see progress which eventually leads to dropping the goals and the personalized prompts we optimistically set.

We designed **Quests**, to help you track activities, share results with others, and engage deeply with people that matter to you. In this article, we'll explore what Quests are, how they work, and how you can get started.

## What are Quests?

Quests allow you track various activities, share your progress, and connect with others through shared experiences with prompts. With quests, you can:

- share your health changes
- respond to prompts with your community to keep track of goals.

At its core quests are ways to deploy tools for behavioral and health research. With this, researchers can design prompts, deploy them, and analyze changes in participants' behaviors and health metrics, with informed consent.

## Why Use Quests?

We believe in the power of community, and quests provide a platform to connect with your friends, family, or even a supportive virtual community, while tracking your activities and achieving your well-being goals together.

- Share your health changes with others and see how you compare with the group.
- Behavioral Insights (For Researchers): Design custom quests with specific prompts to study targeted behaviors on trends and patterns without any geographical limits.
- Increased Motivation: Tracking progress alongside others will keep you accountable and motivated.
- Create Connections: Share your experiences with people that matter to you, motivate each other, and celebrate milestones together.

## How to Join a Quest

To join a quest on the Fusion Copilot app. Follow these steps:

<img src="/images/blog/quest_flow.jpg" alt="How to join a quest" data-zoomable style="cursor: zoom-in;" />

Use the code **u7uc39** on your Fusion Copilot app to get started.

- Open the Fusion Copilot App
- Tap on the bottom right corner of the app where it states 'Quests'.
- Enter the quest code provided to you by the quest creator.
- Start tracking your activities and sharing your progress with the quest community.

## How to Create a Quest

Navigate to the [NeuroFusion Explorer](https://usefusion.ai/quests) dashboard using your web browser.

- Sign in with your details…
- Create a new quest with a title, description, and prompts.
- Click on the "Create Quest" button.
- A quest code will be created for you, which you can share with participants or the community.

## Privacy and Security

We understand that privacy is crucial, especially when it comes to personal data. We ensure your information is secure providing you with:

- Anonymity by Default: You are anonymous by default, ensuring your identity is protected.
- Local Data Storage: All of your data is stored locally on your device, giving you full control over your information.
- Opt-in Sharing: When you join a quest, you opt-in to share your responses with the organizers, but you can control what you share and with whom.

We can't wait to see what quests you'll co-create. Let us know if you run into any issues!

With care,
NEUROFUSION Research, Inc
52 changes: 52 additions & 0 deletions frontend/src/@types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,55 @@ export interface IQuest {
organizerName?: string;
participants?: string[]; // userNpubs
}

export interface dataSource {
sourceName: string;
sourceId: string;
}

export interface FusionHealthDataset {
date: string;
sleepSummary: FusionSleepSummary;
stepSummary: FusionStepSummary;
heartRateSummary?: FusionHeartRateSummary;
}

export interface FusionQuestDataset {
userGuid: string;
questGuid: string;
timestamp: number;
value: FusionHealthDataset[];
type: string;
}

interface FusionStepSummary {
date: string;
totalSteps: number;
}
interface FusionSleepSummary {
date: string;
duration: number;
// sleepStartTime: string;
// sleepEndTime: string;
source?: dataSource;
summaryType?: "overall" | "device";
sourceId?: string;
sourceName?: string;
value?: "awake" | "asleep" | "core" | "rem" | "deep" | "inbed";
}

interface FusionHeartRateSummary {
date: string;
average: number;
min?: number;
max?: number;
source?: dataSource;
summaryType?: "overall" | "device";
sourceId?: string;
sourceName?: string;
}

export interface DisplayCategory {
name: string;
value: string;
}
1 change: 1 addition & 0 deletions frontend/src/components/charts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./line-chart";
68 changes: 68 additions & 0 deletions frontend/src/components/charts/line-chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import dayjs from "dayjs";
import ReactEcharts from "echarts-for-react";
import React, { FC } from "react";
import { DisplayCategory, FusionQuestDataset } from "~/@types";

export interface LineChartProps {
seriesData: FusionQuestDataset[];
startDate: dayjs.Dayjs;
endDate?: dayjs.Dayjs;
timePeriod: "day" | "week" | "month" | "year";
category: DisplayCategory;
}

export const FusionLineChart: FC<LineChartProps> = ({ seriesData, startDate, timePeriod, category }) => {
const [chartOptions, setChartOptions] = React.useState<any>({});

React.useEffect(() => {
// console.log(JSON.parse(seriesData[0].value) as FusionHealthDataset[]);
const dates = seriesData[0].value.map((item) => item.date);
console.log("dates", dates);
const chartOptions = {
animation: true, // Enable animation
animationDuration: 500,
xAxis: {
type: "category",
gap: true,
data: dates,
splitLine: {
show: true,
lineStyle: {
color: "rgba(128, 128, 128, 0.1)", // Set the color of the grid lines
width: 1, // Set the thickness of the grid lines
type: "solid", // Set the style of the grid lines
},
},
},
yAxis: [{ type: "value", name: category.name, position: "left" }],
series: seriesData.map((data) => ({
name: data.userGuid,
yAxisIndex: 0,
type: "line",
smooth: true,
symbol: "circle",
symbolSize: 5,
itemStyle: {
color: "#" + Math.floor(Math.random() * 16777215).toString(16),
},
data: data.value.map((item) => item.stepSummary.totalSteps),
})),
legend: {
data: seriesData.map((data) => data.userGuid),
title: {
text: "Participant",
},
orient: "vertical", // Place the legend on the right side
right: 10, // Adjust the distance from the right side
},
};

setChartOptions(chartOptions);
}, [seriesData]);

return (
<div>
<ReactEcharts option={chartOptions} style={{ height: "500px", width: "800px" }} opts={{ renderer: "canvas" }} />
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,33 @@ import Image from "next/image";
import { fusionOfferingFeatures } from "./data";

import { CustomLink, ButtonLink } from "~/components/ui";
import { api } from "~/config";

export const OfferingSection = ({ isResearch = false }) => {
const [submitted, setSubmitted] = useState(false);

const handleSubmit = (e: { preventDefault: () => void }) => {
const [contactName, setContactName] = useState("");
const [contactEmail, setContactEmail] = useState("");
const [contactMessage, setContacMessage] = useState("");

const handleSubmit = async (e: { preventDefault: () => void }) => {
e.preventDefault();
// Submit logic here, e.g., send form data to server
setSubmitted(true);
try {
const res = await api.post("/sendContactEmail", {
name: contactName,
email: contactEmail,
message: contactMessage,
});

if (res.status === 200) {
setSubmitted(true);
} else {
alert("Failed to send email. You can send one directly to [email protected]");
}
} catch (e) {
alert("Failed to send email. You can send one directly to [email protected]");
}
};

return (
Expand All @@ -38,7 +57,7 @@ export const OfferingSection = ({ isResearch = false }) => {
<p className="font-normal text-md text-gray-500">
Got any questions about the product or contributing to our research? We're here to help!
</p>
<form className="w-full max-w-lg mt-3" onSubmit={handleSubmit}>
<div className="w-full max-w-lg mt-3">
<div className="flex flex-wrap -m-2">
<div className="w-full p-2">
<label htmlFor="name" className="block text-gray-700">
Expand All @@ -48,6 +67,8 @@ export const OfferingSection = ({ isResearch = false }) => {
type="text"
id="name"
name="name"
value={contactName}
onChange={(e) => setContactName(e.target.value)}
placeholder="Enter your name"
className="form-input mt-1 block w-full p-2.5 border-2"
/>
Expand All @@ -60,6 +81,8 @@ export const OfferingSection = ({ isResearch = false }) => {
type="email"
id="email"
name="email"
value={contactEmail}
onChange={(e) => setContactEmail(e.target.value)}
placeholder="[email protected]"
className="form-input mt-1 block w-full p-2.5 border-2"
/>
Expand All @@ -73,6 +96,8 @@ export const OfferingSection = ({ isResearch = false }) => {
name="message"
rows={4}
cols={40}
value={contactMessage}
onChange={(e) => setContacMessage(e.target.value)}
placeholder="Type your message here..."
className="form-textarea mt-1 p-2.5 block w-full border-2"
></textarea>
Expand All @@ -89,7 +114,7 @@ export const OfferingSection = ({ isResearch = false }) => {
</ButtonLink>
</div>
</div>
</form>
</div>
</>
)}
</div>
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/components/layouts/dashboard-layout/sidebar/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const sidebarLinks = [
// },
{
icon: LayoutDashboard,
title: "Lab",
title: "Recording",
href: "/playground",
},
{
Expand All @@ -26,11 +26,11 @@ export const sidebarLinks = [
title: "Quests",
href: "/quests",
},
{
icon: Plug,
title: "Integrations",
href: "/integrations",
},
// {
// icon: Plug,
// title: "Integrations",
// href: "/integrations",
// },
];

export const appearanceModes = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FC, useEffect, useState } from "react";

import { appearanceModes } from "./data";

import { Avatar, AvatarFallback, AvatarImage, Tabs, TabsList, TabsTrigger } from "~/components/ui";
import { Avatar, AvatarFallback, AvatarImage, CustomLink, Tabs, TabsList, TabsTrigger } from "~/components/ui";
import Link from "next/link";

interface ISidebarFooterProps {
Expand All @@ -22,6 +22,8 @@ export const SidebarFooter: FC<ISidebarFooterProps> = ({ user }) => {

return (
<div className="mx-auto flex w-full flex-col items-stretch space-y-5 rounded-none md:w-64 lg:w-72">
<CustomLink store="discord" className="w-full md:w-auto" />

<Link href="/profile">
<div className="dark:border-slate-700/60 group relative flex w-full items-center justify-between rounded-md border p-2 hover:cursor-pointer hover:bg-slate-50 dark:hover:border-transparent dark:hover:bg-slate-800">
<div className="flex items-center space-x-4">
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/pages/blog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ function Blog({ posts }: any) {
overflow: "hidden",
};


return (
<MainLayout>
<Meta
meta={{
title: "Blog | Fusion - Personal Insights from your daily habits and actions",
title: "NeuroFusion Blog",
}}
/>
<div style={containerStyles}>
Expand Down
Loading

0 comments on commit 8e27f6b

Please sign in to comment.