Skip to content

Commit

Permalink
Added Planner Overview
Browse files Browse the repository at this point in the history
  • Loading branch information
anoopkarnik committed Jul 31, 2024
1 parent 5fdb894 commit e3b5dd9
Show file tree
Hide file tree
Showing 4 changed files with 338 additions and 5 deletions.
4 changes: 2 additions & 2 deletions apps/dashboard-app/actions/notion/notion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export const queryNotionDatabaseAction = async ({apiToken,database_id,filters,so
return response;
}

export const queryAllNotionDatabaseAction = async ({apiToken,database_id}:any) => {
const response = await queryAllNotionDatabase({apiToken,database_id, filters: [] })
export const queryAllNotionDatabaseAction = async ({apiToken,database_id,filters,sorts}:any) => {
const response = await queryAllNotionDatabase({apiToken,database_id, filters, sorts})
return response;
}

Expand Down
88 changes: 88 additions & 0 deletions apps/dashboard-app/actions/notion/planner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use server'

import { queryAllNotionDatabaseAction } from "./notion"

export const getCalendarSummary = async ({apiToken, calendarDbId}:any) => {
let filters:any= [
{
name: 'Completed',
type: 'checkbox',
condition: 'equals',
value: false
},
{
name: 'Not Completed',
type: 'checkbox',
condition: 'equals',
value: false
}
]
let sorts:any = [
{
name: 'Created time',
type: 'created_time',
direction: 'descending'
}
]
const calendar = await queryAllNotionDatabaseAction({apiToken, database_id: calendarDbId, filters, sorts})
const tasks = calendar.results.filter((task:any) => task['Tags'].includes('Task'))
const habits = calendar.results.filter((task:any) => task['Tags'].includes('Habit'))
const payments = calendar.results.filter((task:any) => task['Tags'].includes('Financial'))
return {tasks, habits, payments}
}

export const getTasksSummary = async ({apiToken, eisenhowerMatrixDbId}:any) => {
let filters:any= [
{
name: 'Done',
type: 'checkbox',
condition: 'equals',
value: false
}
]
let sorts:any = [
{
name: 'Created time',
type: 'created_time',
direction: 'descending'
}
]
const tasks = await queryAllNotionDatabaseAction({apiToken, database_id: eisenhowerMatrixDbId, filters, sorts})
const uiTasks = tasks.results.filter((task:any) => task['Urgent']===true && task['Important']===true)
const uniTasks = tasks.results.filter((task:any) => task['Urgent']===true && task['Important']===false)
const nuiTasks= tasks.results.filter((task:any) => task['Urgent']===false && task['Important']===true)
return {
uiTasks, uniTasks, nuiTasks
}
}

export const getWeeklyPlannerSummary = async ({apiToken, weeklyPlannerDbId}:any) => {
let filters:any= [
{
name: 'Completed',
type: 'checkbox',
condition: 'equals',
value: false
}
]
let sorts:any = [
{
name: 'Created time',
type: 'created_time',
direction: 'descending'
}
]
const weeklyPlannerTasks = await queryAllNotionDatabaseAction({apiToken, database_id: weeklyPlannerDbId, filters, sorts})
console.log('Weekly Planner Tasks', weeklyPlannerTasks)
if (weeklyPlannerTasks.results.length === 0){
return {
weeklyPlannerTasks: [],
totalHoursLeft: 0
}
}
const totalHoursLeft = weeklyPlannerTasks.results.reduce((acc:any, task:any) => acc + task['Remaining Time (in Hrs)'], 0)
return {
weeklyPlannerTasks,
totalHoursLeft
}
}
249 changes: 247 additions & 2 deletions apps/dashboard-app/app/(dashboard)/planner/_components/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,253 @@
import React from 'react'
import React, { useContext, useEffect, useState } from 'react'
import { ConnectionsContext } from '../../../../providers/connections-provider'
import HeaderCard from '@repo/ui/molecules/common/HeaderCard';
import { getCalendarSummary, getTasksSummary, getWeeklyPlannerSummary } from '../../../../actions/notion/planner';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@repo/ui/molecules/shadcn/Accordion';
import { Checkbox } from '@repo/ui/molecules/shadcn/Checkbox';
import { modifyNotionPageAction } from '../../../../actions/connections/youtube-connections';
import { Input } from '@repo/ui/molecules/shadcn/Input';
import { Button } from '@repo/ui/molecules/shadcn/Button';

const Overview = () => {

const connectionsContext = useContext(ConnectionsContext);
const apiToken = connectionsContext?.notionNode?.accessToken;
const schedulerDbId = connectionsContext?.notionNode?.schedulerDb?.id
const calendarDbId = connectionsContext?.notionNode?.calendarDb?.id
const eisenhowerMatrixDbId = connectionsContext?.notionNode?.eisenhowerMatrixDb?.id
const weeklyPlannerDbId = connectionsContext?.notionNode?.weeklyPlannerDb?.id
const [scheduledTasks, setScheduledTasks] = useState<any>([])
const [scheduledHabits, setScheduledHabits] = useState<any>([])
const [scheduledPayments, setScheduledPayments] = useState<any>([])
const [uiTasks, setUiTasks] = useState<any>([])
const [uniTasks, setUniTasks] = useState<any>([])
const [nuiTasks, setNuiTasks] = useState<any>([])
const [weeklyPlannerTasks, setWeeklyPlannerTasks] = useState<any>([])
const [totalHoursLeft, setTotalHoursLeft] = useState<any>(0)

useEffect(()=>{
const getSummary = async () => {
if(calendarDbId && eisenhowerMatrixDbId){
const {tasks, habits, payments} = await getCalendarSummary({apiToken, calendarDbId})
setScheduledTasks(tasks)
setScheduledHabits(habits)
setScheduledPayments(payments)
const {uiTasks, uniTasks, nuiTasks} = await getTasksSummary({apiToken, eisenhowerMatrixDbId})
setUiTasks(uiTasks)
setUniTasks(uniTasks)
setNuiTasks(nuiTasks)
const {weeklyPlannerTasks,totalHoursLeft} = await getWeeklyPlannerSummary({apiToken, weeklyPlannerDbId})
setWeeklyPlannerTasks(weeklyPlannerTasks.results)
setTotalHoursLeft(totalHoursLeft)
}
}
getSummary()
},[apiToken, calendarDbId, eisenhowerMatrixDbId, weeklyPlannerDbId])

const handleItemsCheckChange = async (id:string, key:string, value:boolean) => {

let properties:any = [
{
name: key,
type: 'checkbox',
value: value
}
]
await modifyNotionPageAction({apiToken,page_id: id, properties})

}

const [timers, setTimers] = useState({});

// Function to start the timer
const startTimer = (taskId:any) => {
setTimers((prevTimers) => {
const newTimers:any = { ...prevTimers };
if (!newTimers[taskId]) {
newTimers[taskId] = { startTime: Date.now(), elapsed: 0, intervalId: null };
} else {
newTimers[taskId].startTime = Date.now() - newTimers[taskId].elapsed;
}

newTimers[taskId].intervalId = setInterval(() => {
setTimers((prev:any) => ({
...prev,
[taskId]: { ...prev[taskId], elapsed: Date.now() - prev[taskId].startTime },
}));
}, 1000);

return newTimers;
});
};

// Function to stop the timer
const stopTimer = (taskId:any) => {
setTimers((prevTimers) => {
const newTimers:any = { ...prevTimers };
if (newTimers[taskId] && newTimers[taskId].intervalId) {
clearInterval(newTimers[taskId].intervalId);
newTimers[taskId].intervalId = null;
}
return newTimers;
});
};

// Helper function to format time in HH:MM:SS
const formatTime = (elapsed:any) => {
const totalSeconds = Math.floor(elapsed / 1000);
const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
const seconds = String(totalSeconds % 60).padStart(2, '0');
return `${hours}:${minutes}:${seconds}`;
};


return (
<div>Overview</div>
<div className='w-[95%] mx-[2.5%] my-6 '>
<div className='gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 my-8'>
<HeaderCard title='Scheduled Habits Left' description='Habits Left to do ' value={`${scheduledHabits.length}`}/>
<HeaderCard title='Scheduled Tasks Left' description='Tasks left to do ' value={`${scheduledTasks.length}`}/>
<HeaderCard title='Scheduled Payments Left' description='Payments left to do today' value={`${scheduledPayments.length}`}/>
<HeaderCard title='Important and Urgent Tasks Left' description='Urgent and Important Tasks Left to do' value={`${uiTasks.length}`}/>
<HeaderCard title='Important and Not Urgent Tasks Left' description='Urgent and Not Important Tasks Left to do' value={`${uniTasks.length}`}/>
<HeaderCard title='Not Important and Urgent Tasks Left' description='Important and Not Urgent Tasks Left to do' value={`${nuiTasks.length}`}/>
<HeaderCard title='Tasks Left for this week' description='Number of Tasks | Time Remaining to still complete this week' value={`${weeklyPlannerTasks?.length}`}/>
<HeaderCard title='Deep Hours worked on ' description='Deep Hours done this day | Average Deep Hours completed this week' value={`${totalHoursLeft}`}/>
</div>
<div>
<Accordion type='single' collapsible className='w-full'>
<AccordionItem value='item-1'>
<AccordionTrigger>
<div className='flex justify-between items-center w-full mr-2'>
<div> Scheduled Things to do </div>
</div>
</AccordionTrigger>
<AccordionContent>
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3'>
<div className='my-4 border-r-2 mx-2 px-2'>
<h1 className='text-2xl font-medium my-4 text-left underline'>Tasks Left</h1>
{scheduledTasks?.map((task:any) => (
<div key={task.id} className='flex items-center justify-between'>
<div>{task['Name']}</div>
<div className='flex items-center gap-2'>
<Checkbox id ='completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Completed',!task.Completed)}/>
<Checkbox id ='not completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Not Completed',!task.Completed)}/>
</div>
</div>
))}
</div>
<div className='my-4 border-r-2 mx-2 px-2'>
<h1 className='text-2xl font-medium my-4 text-left underline'>Habits Left</h1>
{scheduledHabits.map((task:any) => (
<div key={task.id} className='flex items-center justify-between'>
<div>{task['Name']}</div>
<div className='flex items-center gap-2'>
<Checkbox id ='completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Completed',!task.Completed)}/>
<Checkbox id ='not completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Not Completed',!task.Completed)}/>
</div>
</div>
))}
</div>
<div className='my-4 mx-2 px-2'>
<h1 className='text-2xl font-medium my-4 text-left underline'>Payments Left</h1>
{scheduledPayments.map((task:any) => (
<div key={task.id} className='flex items-center justify-between'>
<div>{task['Name']}</div>
<div className='flex items-center gap-2'>
<Checkbox id ='completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Completed',!task.Completed)}/>
<Checkbox id ='not completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Not Completed',!task.Completed)}/>
</div>
</div>
))}
</div>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
<Accordion type='single' collapsible className='w-full'>
<AccordionItem value='item-1'>
<AccordionTrigger>
<div className='flex justify-between items-center w-full mr-2'>
<div> Tasks to do </div>
</div>
</AccordionTrigger>
<AccordionContent>
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3'>
<div className='my-4 border-r-2 mx-2 px-2'>
<h1 className='text-2xl font-medium my-4 text-left underline'>Urgent and Important Tasks</h1>
{uiTasks?.map((task:any) => (
<div key={task.id} className='flex items-center justify-between'>
<div>{task['Task']}</div>
<div className='flex items-center gap-2'>
<Checkbox id ='completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Done',!task.Done)}/>
</div>
</div>
))}
</div>
<div className='my-4 border-r-2 mx-2 px-2'>
<h1 className='text-2xl font-medium my-4 text-left underline'>Urgent and Not Important Tasks</h1>
{uniTasks.map((task:any) => (
<div key={task.id} className='flex items-center justify-between'>
<div>{task['Task']}</div>
<div className='flex items-center gap-2'>
<Checkbox id ='completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Done',!task.Done)}/>
</div>
</div>
))}
</div>
<div className='my-4 mx-2 px-2'>
<h1 className='text-2xl font-medium my-4 text-left underline'>Not Urgent and Important Tasks</h1>
{nuiTasks.map((task:any) => (
<div key={task.id} className='flex items-center justify-between'>
<div>{task['Task']}</div>
<div className='flex items-center gap-2'>
<Checkbox id ='completed' onCheckedChange={()=> handleItemsCheckChange(task.id,'Done',!task.Done)}/>
</div>
</div>
))}
</div>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
<Accordion type='single' collapsible className='w-full'>
<AccordionItem value='item-1'>
<AccordionTrigger>
<div className='flex justify-between items-center w-full mr-2'>
<div> Weekly Planned Deep Work to do </div>
</div>
</AccordionTrigger>
<AccordionContent>
<div className='my-4 mx-2 px-2'>
<div className='flex flex-col'>
{weeklyPlannerTasks?.map((task:any) => (
<div key={task.id} className='flex items-center justify-between mt-2'>
<div>{task['Name']}</div>
<div className='grid grid-cols-4 items-center gap-4'>
<div>{task['Remaining Time (in Hrs)']}</div>
<div>{task['Total Time Spent']}</div>
<Button className='px-5 bg-green-900'onClick={() => {
if (timers[task.id]?.intervalId) {
stopTimer(task.id);
} else {
startTimer(task.id);
}
}}
>
{timers[task.id]?.intervalId ? formatTime(timers[task.id].elapsed) : 'Start'}
</Button>
<Checkbox id='completed' onCheckedChange={() => handleItemsCheckChange(task.id, 'Completed', !task.Completed)}/>
</div>
</div>
))}
</div>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>

</div>
</div>
)
}

Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/molecules/common/HeaderCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../sh

const HeaderCard = ({title,description,value}:any) => {
return (
<Card className="flex flex-col items-center justify-between ">
<Card className="flex flex-col items-start justify-between ">
<CardHeader>
<CardTitle>{title}</CardTitle>
<CardDescription>{description}</CardDescription>
Expand Down

0 comments on commit e3b5dd9

Please sign in to comment.