Skip to content

Commit

Permalink
feat: update flows - add favorite - stage shapes - display user name …
Browse files Browse the repository at this point in the history
…who created the flow
  • Loading branch information
shahramk committed Sep 6, 2023
1 parent 61464d2 commit 587a16a
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 49 deletions.
77 changes: 66 additions & 11 deletions nerdlets/home/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import React, {
import { Button, Icon, nerdlet, PlatformStateContext, Spinner } from 'nr1';

import { Flow, FlowList, NoFlows } from '../../src/components';
import { useFlowLoader, useFlowWriter, useFetchUser } from '../../src/hooks';
import {
useFlowLoader,
useFlowWriter,
useFetchUser,
useFetchUserConfig,
useUpdateUserConfig,
} from '../../src/hooks';
import { MODES, UI_CONTENT } from '../../src/constants';
import { uuid } from '../../src/utils';

Expand All @@ -19,27 +25,65 @@ const HomeNerdlet = () => {
const [flows, setFlows] = useState([]);
const [currentFlowIndex, setCurrentFlowIndex] = useState(-1);
const { accountId } = useContext(PlatformStateContext);
const { user } = useFetchUser();
const userRef = useRef();
userRef.current = useFetchUser();
const [userConfig, setUserConfig] = useState({});
const { userStorageConfig } = useFetchUserConfig();
const { userStorageHandler } = useUpdateUserConfig();
const [loadingUserConfig, setLoadingUserConfig] = useState(true);

useEffect(() => {
if (
loadingUserConfig &&
userStorageConfig &&
Object.prototype.toString.call(userStorageConfig) !== '[object Set]'
) {
setUserConfig({
...userStorageConfig,
favorite_flows: new Set(
userStorageConfig.favorite_flows &&
Object.prototype.toString.call(userStorageConfig.favorite_flows) ===
'[object Array]'
? userStorageConfig.favorite_flows
: []
),
last_updated: new Date().getTime(),
});
setLoadingUserConfig(false);
}
}, [userStorageConfig]);

useEffect(() => {
if (
Object.prototype.toString.call(userConfig.favorite_flows) ===
'[object Set]'
) {
userStorageHandler({
...userConfig,
favorite_flows: Array.from(userConfig.favorite_flows),
});
}
}, [userConfig?.favorite_flows?.size]);

const {
flows: flowsData,
error: flowsError,
loading: flowsLoading,
} = useFlowLoader({ accountId });

const flowWriter = useFlowWriter({ accountId, user });
const flowWriter = useFlowWriter({ accountId, user: userRef.current.user });
const newFlowId = useRef();

const actionControlButtons = useMemo(() => {
const buttons = [];
if (mode !== MODES.EDIT) {
buttons.push({
label: UI_CONTENT.GLOBAL.BUTTON_LABEL_CREATE_FLOW,
type: Button.TYPE.PRIMARY,
iconType: Icon.TYPE.DATAVIZ__DATAVIZ__SERVICE_MAP_CHART,
onClick: () => newFlowHandler(),
});
if (currentFlowIndex > -1) {
buttons.push({
label: UI_CONTENT.GLOBAL.BUTTON_LABEL_CREATE_FLOW,
type: Button.TYPE.PRIMARY,
iconType: Icon.TYPE.DATAVIZ__DATAVIZ__SERVICE_MAP_CHART,
onClick: () => newFlowHandler(),
});
buttons.push({
label: UI_CONTENT.GLOBAL.BUTTON_LABEL_EDIT_MODE,
type: Button.TYPE.PRIMARY,
Expand Down Expand Up @@ -84,6 +128,10 @@ const HomeNerdlet = () => {
name: 'Untitled',
stages: [],
kpis: [],
createdBy: {
name: userRef.current.user.name,
timestamp: Date.now(),
},
},
});
}, []);
Expand Down Expand Up @@ -128,13 +176,20 @@ const HomeNerdlet = () => {
setMode={setMode}
flows={flows}
onSelectFlow={flowClickHandler}
user={user}
user={userRef.current.user}
/>
);
}
if (flows && flows.length) {
backToFlowsHandler();
return <FlowList flows={flows} onClick={flowClickHandler} />;
return (
<FlowList
flows={flows}
userConfig={userConfig}
setUserConfig={setUserConfig}
onClick={flowClickHandler}
/>
);
}
if (flowsLoading) {
return <Spinner />;
Expand Down
118 changes: 98 additions & 20 deletions src/components/flow-list/index.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,68 @@
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';

import PropTypes from 'prop-types';

import { TextField } from 'nr1';
import { Icon, TextField } from 'nr1';

import { getStageHeaderShape } from '../../utils';

const FlowList = ({ flows = [], onClick = () => null }) => {
const FlowList = ({
flows = [],
userConfig = {},
setUserConfig = () => null,
onClick = () => null,
}) => {
const [searchPattern, setSearchPattern] = useState('');
const [filteredFlows, setFilteredFlows] = useState([]);

useEffect(() => {
const tmpFlows = userConfig.favorite_flows.size
? flows.map((f) => ({
...f,
favorite: Boolean(userConfig.favorite_flows.has(f.id)) || false,
}))
: flows;

const sortedFlows = tmpFlows.sort(
(a, b) => Number(b.favorite || false) - Number(a.favorite || false)
);

setFilteredFlows(
flows.length && searchPattern
? flows.filter((item) =>
sortedFlows.length && searchPattern
? sortedFlows.filter((item) =>
`${item.document.name} ${item.document.stages
.map((s) => s.name)
.join(' ')}`
.toLowerCase()
.includes(searchPattern.toLowerCase())
)
: flows
: sortedFlows
);
}, [searchPattern, JSON.stringify(Array.from(userConfig.favorite_flows))]);

const renderStageShape = useCallback((stage, index) => {
const className = getStageHeaderShape(stage, '-border');

return (
<div key={index} className="stage-shape">
{className !== 'has-none-border' && (
<div
key={`border-${index}`}
className={`stage-name ${getStageHeaderShape(stage, '-border')}`}
style={{ position: 'relative' }}
title={`${stage.name}_border`}
></div>
)}
<div
key={`stage-${index}`}
className={`stage-name ${getStageHeaderShape(stage)}`}
title={stage.name}
>
<div className="name-text">{stage.name}</div>
</div>
</div>
);
}, [searchPattern]);
}, []);

return (
<div className="flows-container">
Expand All @@ -39,6 +80,7 @@ const FlowList = ({ flows = [], onClick = () => null }) => {
<div className="flowlist-container">
<div className="flowlist-header">
<div className="row">
<div className="cell col-0-format"></div>
<div className="cell col-1-format">Flow</div>
<div className="cell col-2-format">Stages</div>
</div>
Expand All @@ -48,23 +90,57 @@ const FlowList = ({ flows = [], onClick = () => null }) => {
<div
key={`flow-${flowIndex}`}
className="row body"
onClick={() => {
onClick(flow.id);
onClick={(event) => {
event.target.nodeName === 'DIV' && onClick(flow.id);
}}
>
<div className="cell cell col-1-format flow-name">
{flow.document.name}
<span
className="cell col-0-format"
onClick={() => {
setFilteredFlows(
filteredFlows.map((f, i) =>
i === flowIndex
? {
...f,
favorite: !filteredFlows[flowIndex].favorite
? true
: !filteredFlows[flowIndex].favorite,
}
: f
)
);
const ff = userConfig.favorite_flows;
!filteredFlows[flowIndex].favorite
? ff.add(filteredFlows[flowIndex].id)
: ff.delete(filteredFlows[flowIndex].id);
setUserConfig({ ...userConfig, favorite_flows: ff });
}}
>
<Icon
type={
userConfig.favorite_flows.has(flow.id)
? Icon.TYPE.PROFILES__EVENTS__FAVORITE__WEIGHT_BOLD
: Icon.TYPE.PROFILES__EVENTS__FAVORITE
}
color={
userConfig.favorite_flows.has(flow.id)
? '#f0b400'
: '#9ea5a9'
}
/>
</span>
<div className="cell col-1-format flow-info">
<div className="flow-name">{flow.document.name}</div>
<div className="createdby-user">
{`Created by: ${
flow?.document?.createdBy?.name || 'unknown user'
}`}
</div>
</div>
<div className="cell col-2-format stage-names">
{flow.document.stages.map((stage, index) => (
<div
key={`stage-${index}`}
className={`stage-name ${getStageHeaderShape(stage)}`}
title={stage.name}
>
<div className="name-text">{stage.name}</div>
</div>
))}
{flow.document.stages.map((stage, index) =>
renderStageShape(stage, index)
)}
</div>
</div>
))}
Expand All @@ -76,6 +152,8 @@ const FlowList = ({ flows = [], onClick = () => null }) => {

FlowList.propTypes = {
flows: PropTypes.array,
userConfig: PropTypes.object,
setUserConfig: PropTypes.object,
onClick: PropTypes.func,
};

Expand Down
Loading

0 comments on commit 587a16a

Please sign in to comment.