Skip to content

Commit

Permalink
#3134: Scale nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
VassilIordanov committed Aug 10, 2020
1 parent de0c186 commit a41efc2
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 237 deletions.
71 changes: 53 additions & 18 deletions client/src/components/graphs/OrganizationalChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,26 +115,39 @@ const OrganizationalChart = ({
[data]
)
const isMain = useCallback(node => node?.uuid === data?.organization?.uuid, [
JSON.stringify(data)
data
])

const getDistance = useCallback(
const getScale = useCallback(
node => {
if (isParent(node)) {
return 1
return 0.7
} else {
let distance = 0
let scale = 1.3
let nodeIt = node
while (nodeIt && !isMain(nodeIt)) {
distance++
scale = scale / 1.4
nodeIt = getDescendant(nodeIt.parentOrg?.uuid)
}
return distance
return scale
}
},
[getDescendant, isParent, isMain]
)

const getSize = useCallback(
node => {
if (isMain(node)) {
return [400, 400]
} else if (isParent(node)) {
return [200, 200]
} else {
return [200 * getScale(node), 200 * getScale(node)]
}
},
[getScale, isMain, isParent]
)

useEffect(() => {
if (data) {
treeLayout.current = flextree()
Expand All @@ -146,10 +159,7 @@ const OrganizationalChart = ({
org => org.parentOrg?.uuid === d.uuid
)
})
.nodeSize(node => [
200 / (getDistance(node.data) + 0.5),
200 / (getDistance(node.data) + 0.5)
])
.nodeSize(node => getSize(node.data))
.spacing(10)
const tree = treeLayout.current.hierarchy(
data.organization.parentOrg || data.organization
Expand All @@ -159,7 +169,7 @@ const OrganizationalChart = ({
console.log(tree)
console.log(treeLayout)
}
}, [data, getDistance, isParent, isMain])
}, [data, isParent, getSize, isMain])

useEffect(() => {
if (!data || !root) {
Expand Down Expand Up @@ -213,24 +223,49 @@ const OrganizationalChart = ({
nodeSelect
.transition()
.duration(transitionDuration)
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr(
"transform",
d =>
`translate(${d.x},${d.y}) scale(${getScale(d.data)} ${getScale(
d.data
)})`
)

nodeSelect.exit().remove()

const nodeEnter = nodeSelect
.enter()
.append("g")
.attr("class", "org")
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr(
"transform",
d =>
`translate(${d.x},${d.y}) scale(${getScale(d.data)} ${getScale(
d.data
)})`
)

nodeEnter.append("rect").attr("rx", 7).attr("ry", 7)
nodeEnter
.append("rect")
.attr("rx", 7)
.attr("ry", 7)
.attr("x", d => -getSize(d.data)[0] / 2)
.attr("y", 15)
.attr("width", d => getSize(d.data)[0])
.attr("height", d => getSize(d.data)[1] - 100 * getScale(d.data))
// .attr("width", d => d.size[0]})
// .attr("height", d => d.size[1] - 20)
.style("fill", d =>
isMain(d.data) ? "rgba(255, 255, 255, 1)" : "rgba(230, 230, 230, 0.5)"
)
.style("stroke", d => (isMain(d.data) ? "black" : "none"))

nodeSelect
.selectAll("rect")
.attr("x", d => -200 / (getDistance(d.data) + 0.5) / 2)
.attr("x", d => -getSize(d.data)[0] / 2)
.attr("y", 15)
.attr("width", d => 200 / (getDistance(d.data) + 0.5))
.attr("height", d => 200 / (getDistance(d.data) + 0.5) - 40)
.attr("width", d => getSize(d.data)[0])
.attr("height", d => getSize(d.data)[1] - 100 * getScale(d.data))
// .attr("width", d => d.size[0]})
// .attr("height", d => d.size[1] - 20)
.style("fill", d =>
Expand Down Expand Up @@ -370,7 +405,7 @@ const OrganizationalChart = ({
} ${d.name}`
return result.length > 31 ? result.substring(0, 28) + "..." : result
})
}, [data, history, root, link, node, isMain, getDistance])
}, [data, history, root, link, node, isMain, getSize])

return (
<>
Expand Down
215 changes: 13 additions & 202 deletions client/src/pages/organizations/Laydown.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,12 @@
import AppContext from "components/AppContext"
import Fieldset from "components/Fieldset"
import OrganizationalChart from "components/graphs/OrganizationalChart"
import LinkTo from "components/LinkTo"
import { Organization, Person, Position } from "models"
import { Organization } from "models"
import PropTypes from "prop-types"
import React, { useContext, useState } from "react"
import { Button, Table } from "react-bootstrap"
import React from "react"
import ContainerDimensions from "react-container-dimensions"
import { Element } from "react-scroll"
import Settings from "settings"

const OrganizationLaydown = ({ organization }) => {
const { currentUser } = useContext(AppContext)
const [showInactivePositions, setShowInactivePositions] = useState(false)
const isSuperUser = currentUser && currentUser.isSuperUserForOrg(organization)

const numInactivePos = organization.positions.filter(
p => p.status === Position.STATUS.INACTIVE
).length

const positionsNeedingAttention = organization.positions.filter(
position => !position.person
)
const supportedPositions = organization.positions.filter(
position => positionsNeedingAttention.indexOf(position) === -1
)

return (
<Element name="laydown">
<Fieldset
Expand All @@ -34,190 +15,20 @@ const OrganizationLaydown = ({ organization }) => {
className="scroll-anchor-container"
style={{ background: "none" }}
>
<ContainerDimensions>
{({ width, height }) => (
<OrganizationalChart
label="test"
org={organization}
exportTitle={`Organization diagram for ${organization}`}
width={width}
height={height}
/>
)}
</ContainerDimensions>
</Fieldset>

<Fieldset
id="supportedPositions"
title="Supported positions"
action={
<div>
{isSuperUser && (
<LinkTo
modelType="Position"
model={Position.pathForNew({
organizationUuid: organization.uuid
})}
button
>
Create position
</LinkTo>
)}
</div>
}
>
{renderPositionTable(supportedPositions)}
{supportedPositions.length === 0 && (
<em>There are no occupied positions</em>
)}
</Fieldset>

<Fieldset
id="vacantPositions"
title="Vacant positions"
action={
<div>
{numInactivePos > 0 && (
<Button onClick={toggleShowInactive}>
{(showInactivePositions ? "Hide " : "Show ") +
numInactivePos +
" inactive position(s)"}
</Button>
)}
</div>
}
>
{renderPositionTable(positionsNeedingAttention)}
{positionsNeedingAttention.length === 0 && (
<em>There are no vacant positions</em>
)}
<ContainerDimensions>
{({ width, height }) => (
<OrganizationalChart
label="test"
org={organization}
exportTitle={`Organization diagram for ${organization}`}
width={width}
height={height}
/>
)}
</ContainerDimensions>
</Fieldset>
</Element>
)

function renderPositionTable(positions) {
let posNameHeader, posPersonHeader, otherNameHeader, otherPersonHeader
if (organization.isAdvisorOrg()) {
posNameHeader = Settings.fields.advisor.position.name
posPersonHeader = Settings.fields.advisor.person.name
otherNameHeader = Settings.fields.principal.position.name
otherPersonHeader = Settings.fields.principal.person.name
} else {
otherNameHeader = Settings.fields.advisor.position.name
otherPersonHeader = Settings.fields.advisor.person.name
posNameHeader = Settings.fields.principal.position.name
posPersonHeader = Settings.fields.principal.person.name
}
return (
<Table>
<thead>
<tr>
<th>{posNameHeader}</th>
<th>{posPersonHeader}</th>
<th>{otherPersonHeader}</th>
<th>{otherNameHeader}</th>
</tr>
</thead>
<tbody>
{Position.map(positions, position =>
position.associatedPositions.length
? Position.map(position.associatedPositions, (other, idx) =>
renderPositionRow(position, other, idx)
)
: renderPositionRow(position, null, 0)
)}
</tbody>
</Table>
)
}

function renderPositionRow(position, other, otherIndex) {
let key = position.uuid
let otherPersonCol, otherNameCol, positionPersonCol, positionNameCol
if (
position.status === Position.STATUS.INACTIVE &&
!showInactivePositions
) {
return
}

if (other) {
key += "." + other.uuid
otherNameCol = (
<td>
<LinkTo modelType="Position" model={other}>
{positionWithStatus(other)}
</LinkTo>
</td>
)

otherPersonCol = other.person ? (
<td>
<LinkTo modelType="Person" model={other.person}>
{personWithStatus(other.person)}
</LinkTo>
</td>
) : (
<td className="text-danger">Unfilled</td>
)
}

if (otherIndex === 0) {
positionNameCol = (
<td>
<LinkTo modelType="Position" model={position}>
{positionWithStatus(position)}
</LinkTo>
</td>
)
positionPersonCol =
position.person && position.person.uuid ? (
<td>
<LinkTo modelType="Person" model={position.person}>
{personWithStatus(position.person)}
</LinkTo>
</td>
) : (
<td className="text-danger">Unfilled</td>
)
}

otherPersonCol = otherPersonCol || <td />
otherNameCol = otherNameCol || <td />
positionPersonCol = positionPersonCol || <td />
positionNameCol = positionNameCol || <td />

return (
<tr key={key}>
{positionNameCol}
{positionPersonCol}
{otherPersonCol}
{otherNameCol}
</tr>
)
}

function personWithStatus(person) {
person = new Person(person)
if (person.status === Person.STATUS.INACTIVE) {
return <i>{person.toString() + " (Inactive)"}</i>
} else {
return person.toString()
}
}

function positionWithStatus(pos) {
const code = pos.code ? ` (${pos.code})` : ""
if (pos.status === Position.STATUS.INACTIVE) {
return <i>{`${pos.name}${code} (Inactive)`}</i>
} else {
return pos.name + code
}
}

function toggleShowInactive() {
setShowInactivePositions(!showInactivePositions)
}
}

OrganizationLaydown.propTypes = {
Expand Down
Loading

0 comments on commit a41efc2

Please sign in to comment.