Skip to content

Commit

Permalink
gantt view start
Browse files Browse the repository at this point in the history
  • Loading branch information
pegahvaezi committed Feb 1, 2024
1 parent 8741a67 commit ba6793f
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 0 deletions.
15 changes: 15 additions & 0 deletions web/src/components/Header/HeaderLeftPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,21 @@ const HeaderLeftPanel: React.FC<HeaderLeftPanelProps> = ({
tooltipText="Priority View"
/>
</NavLink>
{/* gantt view button */}
<NavLink
to={`/project/${projectId}/gantt`}
activeClassName="view-mode-active"
className="view-mode-link"
>
<Icon
name="align-left.svg"
size="view-mode"
className="light-grey"
withTooltip
tooltipText="Gantt View"
/>
</NavLink>

{/* <Icon name='timeline.svg' className='grey' size='view-mode' /> */}
</div>
<div className="current-project-name">{projectName}</div>
Expand Down
1 change: 1 addition & 0 deletions web/src/images/align-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
107 changes: 107 additions & 0 deletions web/src/routes/ProjectView/GanttView/GantViewTimeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useState, useEffect } from 'react'

export type GanttViewTimelineProps = {
tasks?: Array<{
name: string
startDate: Date
endDate: Date
}>
}

const dayWidth = 28 // Width for each day

const GanttViewTimeline: React.FC<GanttViewTimelineProps> = ({
tasks = [],
}) => {
// State for days in the timeline
const [days, setDays] = useState<Date[]>([])

useEffect(() => {
// Determine the start and end of the current month
const now = new Date()
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0)
const dates = []

// Loop through all days in the month and add them to state
for (
let day = new Date(startOfMonth);
day <= endOfMonth;
day.setDate(day.getDate() + 1)
) {
dates.push(new Date(day))
}

setDays(dates)
}, []) // Empty dependency array means this effect will only run once

return (
<div
className="gantt-view-timeline"
style={{ position: 'relative', overflowX: 'scroll', width: '100%' }}
>
{/* Render the days */}
<div className="gantt-view-time-display">
<div className="gantt-view-month">
{days.length > 0 &&
days[0].toLocaleDateString('default', { month: 'long' })}
</div>
<div className="gantt-view-dates">
{days.map((day, index) => (
<div
className="gantt-view-date"
key={index}
style={{
width: `${dayWidth}px`,
}}
>
{day.getDate()}
</div>
))}
</div>
</div>

{/* Render the tasks */}
<div
style={{
position: 'absolute',
top: '20px',
width: `${days.length * dayWidth}px`,
}}
>
{' '}
{/* Adjust positioning as needed */}
{tasks.map((task, index) => {
const startDayIndex = days.findIndex(
(d) => d.toDateString() === task.startDate.toDateString()
)
const endDayIndex = days.findIndex(
(d) => d.toDateString() === task.endDate.toDateString()
)
const duration = endDayIndex - startDayIndex + 1 // Include the end day in the duration
const left = startDayIndex * dayWidth
const width = duration * dayWidth

return (
<div
key={index}
style={{
position: 'absolute',
left: `${left}px`,
width: `${width}px`,
height: '20px', // Adjust height as needed
backgroundColor: 'blue', // Adjust color as needed
textAlign: 'center',
color: 'white',
}}
>
{task.name}
</div>
)
})}
</div>
</div>
)
}

export default GanttViewTimeline
60 changes: 60 additions & 0 deletions web/src/routes/ProjectView/GanttView/GanttView.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useContext } from 'react'
import './GanttView.scss'

import { LayeringAlgorithm, Profile } from '../../../types'
import ComputedOutcomeContext from '../../../context/ComputedOutcomeContext'
import { Tag } from '../../../types'
import {
ActionHashB64,
AgentPubKeyB64,
WithActionHash,
} from '../../../types/shared'
import IndentedTreeView from '../../../components/IndentedTreeView/IndentedTreeView'
import GanttViewContent from './GanttViewContent'
// import OutcomeGanttWithFilters from '../../../components/OutcomeGanttWithFilters/OutcomeGanttWithFilters'

export type GanttViewConnectorStateProps = {
whoAmI: Profile
topPriorityOutcomes: ActionHashB64[]
presentMembers: AgentPubKeyB64[]
projectMemberProfiles: Profile[]
projectTags: WithActionHash<Tag>[]
}

export type GanttViewConnectorDispatchProps = {
openExpandedView: (actionHash: ActionHashB64) => void
goToOutcome: (actionHash: ActionHashB64) => void
}

export type GanttViewProps = GanttViewConnectorStateProps &
GanttViewConnectorDispatchProps

const GanttView: React.FC<GanttViewProps> = ({
whoAmI,
topPriorityOutcomes,
presentMembers,
projectMemberProfiles,
projectTags,
openExpandedView,
goToOutcome,
}) => {
const { computedOutcomesAsTree } = useContext(ComputedOutcomeContext)

return (
<div className="gantt-view">
<GanttViewContent />
{/* <OutcomeGanttWithFilters
topPriorityOutcomes={topPriorityOutcomes}
projectTags={projectTags}
whoAmI={whoAmI}
presentMembers={presentMembers}
projectMemberProfiles={projectMemberProfiles}
computedOutcomesAsTree={computedOutcomesAsTree}
openExpandedView={openExpandedView}
goToOutcome={goToOutcome}
/> */}
</div>
)
}

export default GanttView
50 changes: 50 additions & 0 deletions web/src/routes/ProjectView/GanttView/GanttView.connector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { connect } from 'react-redux'
import { RootState } from '../../../redux/reducer'
import { openExpandedView } from '../../../redux/ephemeral/expanded-view/actions'
import selectProjectMembersPresent from '../../../redux/persistent/projects/realtime-info-signal/select'
import { ActionHashB64 } from '../../../types/shared'
import GanttView, {
GanttViewConnectorDispatchProps,
GanttViewConnectorStateProps,
} from './GanttView.component'
import { WireRecord } from '../../../api/hdkCrud'
import { Profile } from '../../../types'
import { animatePanAndZoom } from '../../../redux/ephemeral/viewport/actions'

function mapStateToProps(state: RootState): GanttViewConnectorStateProps {
const projectId = state.ui.activeProject
const whoAmIWireRecord = state.whoami || ({} as WireRecord<Profile>)
// can be undefined, based on line above, intentionally
const whoAmI = whoAmIWireRecord.entry
const projectMeta = state.projects.projectMeta[projectId]
const topPriorityOutcomes = projectMeta ? projectMeta.topPriorityOutcomes : []
const projectTagsObject = state.projects.tags[projectId] || {}
const projectMembers = state.projects.members[projectId] || {}
const projectMemberProfiles = Object.keys(projectMembers)
.map((address) => state.agents[address])
.filter((agent) => agent)

const presentMembers = projectId
? selectProjectMembersPresent(state, projectId)
: []

const projectTags = Object.values(projectTagsObject)
return {
whoAmI,
topPriorityOutcomes,
presentMembers,
projectMemberProfiles,
projectTags,
}
}
function mapDispatchToProps(dispatch): GanttViewConnectorDispatchProps {
return {
openExpandedView: (actionHash: ActionHashB64) => {
return dispatch(openExpandedView(actionHash))
},
goToOutcome: (actionHash: ActionHashB64) => {
return dispatch(animatePanAndZoom(actionHash, true))
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(GanttView)
59 changes: 59 additions & 0 deletions web/src/routes/ProjectView/GanttView/GanttView.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.gantt-view {
flex: 1;
overflow: hidden;
}

.gantt-view-content {
display: flex;
flex-direction: row;
width: 100vw;
height: 100%;

.gantt-view-content-timeline {
flex: 1;
overflow: auto;
overflow: hidden;
background-color: var(--bg-color-secondary);
}
}

.gantt-view-timeline {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
padding: 0 0 0 0;
overflow: hidden;
background-color: var(--bg-color-secondary);

.gantt-view-time-display {
// overflow-x: scroll;
display: flex;
flex-direction: column;

.gantt-view-month {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: var(--padding-0825rem);
border-bottom: var(--border-gantt);
font-size: var(--font-size-medium);
font-family: var(--font-family-primary-semibold);
}

.gantt-view-dates {
display: grid;
grid-auto-flow: column; // ensures the grid flows horizontally
overflow-x: auto;
border-bottom: var(--border-gantt);

.gantt-view-date {
width: 3rem;
padding: var(--padding-0825rem);
border-right: var(--border-gantt);
text-align: center;
font-size: var(--font-size-xsmallPlus);
}
}
}
}
40 changes: 40 additions & 0 deletions web/src/routes/ProjectView/GanttView/GanttViewContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react'
import IndentedTreeView from '../../../components/IndentedTreeView/IndentedTreeView'
import { LayeringAlgorithm } from '../../../types'
import GantViewTimeline from './GantViewTimeline'

export type GanttViewContentProps = {}

const GanttViewContent: React.FC<GanttViewContentProps> = ({}) => {
return (
// inside the div with gantt-view-timeline class, we will render the timeline

<div className="gantt-view-content">
<IndentedTreeView
outcomeTrees={[]}
projectMeta={undefined}
updateProjectMeta={function (
entry: {
name: string
image: string
isImported: boolean
creatorAgentPubKey: string
createdAt: number
passphrase: string
isMigrated: string
layeringAlgorithm: LayeringAlgorithm
topPriorityOutcomes: string[]
},
actionHash: string
): Promise<void> {
throw new Error('Function not implemented.')
}}
/>
<div className="gantt-view-content-timeline">
<GantViewTimeline />
</div>
</div>
)
}

export default GanttViewContent
2 changes: 2 additions & 0 deletions web/src/routes/ProjectView/ProjectView.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { GO_TO_OUTCOME } from '../../searchParams'
import MapView from './MapView/MapView.connector'
import PriorityView from './PriorityView/PriorityView.connector'
import TableView from './TableView/TableView.connector'
import GanttView from './GanttView/GanttView.connector'
import ConnectedExpandedViewMode from '../../components/ExpandedViewMode/ExpandedViewMode.connector'

import ComputedOutcomeContext from '../../context/ComputedOutcomeContext'
Expand Down Expand Up @@ -178,6 +179,7 @@ const ProjectViewInner: React.FC<ProjectViewInnerProps> = ({
<Route path="/project/:projectId/map" component={MapView} />
<Route path="/project/:projectId/priority" component={PriorityView} />
<Route path="/project/:projectId/table" component={TableView} />
<Route path="/project/:projectId/gantt" component={GanttView} />
<Route exact path="/project/:projectId" component={ProjectRedirect} />
</Switch>
<ConnectedExpandedViewMode
Expand Down
27 changes: 27 additions & 0 deletions web/src/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,31 @@
--text-color-toast-information: #22796f;
--text-color-toast-warning: #bf8342;
--text-color-toast-destructive: #b52c0c;

// border
--border-gantt: 1px solid #e0ddd7;
--border-tab-active: 1px solid #636058;

// padding
--padding-025rem: 0.25rem;
--padding-05rem: 0.5rem;
--padding-075rem: 0.75rem;
--padding-0825rem: 0.825rem;
--padding-1rem: 1rem;
--padding-2rem: 2rem;
--padding-3rem: 3rem;

// font-size
--font-size-xsmall: 0.75rem;
--font-size-xsmallPlus: 0.825rem;
--font-size-small: 0.875rem;
--font-size-smallPlus: 0.925rem;
--font-size-medium: 1rem;
--font-size-mediumPlus: 1.125rem;
--font-size-large: 1.25rem;
--font-size-largePlus: 1.375rem;
--font-size-xlarge: 1.5rem;
--font-size-xlargePlus: 1.625rem;


}

0 comments on commit ba6793f

Please sign in to comment.