Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/team communication #40

Merged
merged 6 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions __mock__/team-communication.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"title": "Team Communication",
"description": "This visualization showcases the conversations between team members.",
"sensors": {
"sensor-1": {
"title": "Slack",
"slack": {
"value": {
"U01HR3WBQH4": {
"characters": 1868,
Expand Down
19 changes: 19 additions & 0 deletions __mock__/team-noise-levels.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"team-noise-levels": {
"title": "Team Noise Levels",
"description": "This is a mock data for team noise levels",
"sensors": {
"meeting-room-nw": {
"title": "Meeting Room NW",
"description": "This is a mock data for meeting room NW",
"value": 50
},
"meeting-room-ne": {
"title": "Meeting Room NE",
"description": "This is a mock data for meeting room NE",
"value": 30
}

}
}
}
59 changes: 44 additions & 15 deletions components/team-communication/TeamCommunication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { useConfig } from '@/contexts/config-context';
import { useDisplay } from '@/contexts/display-context';
import { useMedia } from '@/hooks/use-media';
import { useWindowDimensions } from '@/hooks/use-window-dimensions';
import { TeamMember } from '@/types/firebase';
import { TeamMember } from '@/types/team-activity';
import {
calculatePreferredWhiteSpace,
calulcateAvailableSpace,
ExtendedTeamMember,
relativeTeamCommunicationMemberSize,
viewTeamPositions,
} from '@/utils/general';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import TeamDefs from './internal/TeamDefs';
import TeamEdges from './internal/TeamEdges';
import TeamNodes from './internal/TeamNodes';
Expand All @@ -30,7 +30,7 @@ export interface Edge {
}

const TeamCommunication = () => {
//Contexts
// Contexts
const { displayData, freeSpaceArea } = useDisplay<'team-communication'>();
const { view } = useChart();
const { metricConfigs } = useConfig();
Expand All @@ -54,15 +54,14 @@ const TeamCommunication = () => {

const delayBetweenInstance = 0.2;

//Only Support Slack
const slackData = displayData?.sensors[0].value;
const oldSlackDataRef = useRef(slackData);

// Edges & Nodes
const [edges, setEdges] = useState<Edge[]>([]);
const [nodes, setNodes] = useState<Node[]>([]);
const [positions, setPositions] = useState<Node[]>([]);

//Custom Branding
// Custom Branding
const [turnOffBranding, setTurnOffBranding] = useState(false);

useEffect(() => {
Expand All @@ -76,9 +75,9 @@ const TeamCommunication = () => {
}
}, [view]);

// First useEffect: Update positions only when view changes or on initial render
useEffect(() => {
if (slackData) {
// First, calculate the radius for each node based on messages
const totalMessages = slackData.reduce(
(acc, value) => acc + value.messages,
0
Expand All @@ -94,13 +93,13 @@ const TeamCommunication = () => {

return {
...value,
radius, // Add radius first
radius,
};
});

const positionPreparedData = viewTeamPositions({
view,
members: updatedNodes, // Pass nodes with radius
members: updatedNodes,
spaces: {
width,
height,
Expand All @@ -110,13 +109,43 @@ const TeamCommunication = () => {
options: { offBranding: turnOffBranding },
});

// Update the nodes with the new positions
setNodes(positionPreparedData);
setPositions(positionPreparedData);
}
}, [view, width, height, freeSpaceArea, query, turnOffBranding]);

// Second useEffect: Update other node properties when data changes
useEffect(() => {
if (positions.length > 0 && slackData) {
const totalMessages = slackData.reduce(
(acc, value) => acc + value.messages,
0
);

const updatedNodes = positions.map((node) => {
const matchingValue = slackData.find((value) => value.id === node.id);
if (matchingValue) {
const radius = relativeTeamCommunicationMemberSize(
matchingValue.messages,
availableSpace,
totalMessages,
defaultMemberSize
);

return {
...node,
...matchingValue,
radius,
};
} else {
return node;
}
});

oldSlackDataRef.current = slackData;
setNodes(updatedNodes);
}
}, [slackData, view, width, height, freeSpaceArea, query]);
}, [slackData, positions, availableSpace, defaultMemberSize]);

// Update edges when nodes change
useEffect(() => {
const newEdges: Edge[] = [];
nodes.forEach((node, i) => {
Expand All @@ -128,7 +157,7 @@ const TeamCommunication = () => {
const matchingKey = nodeKeys.filter((key) =>
targetKeys.includes(key)
);
if (matchingKey) {
if (matchingKey.length > 0) {
newEdges.push({
source: node,
target: targetNode,
Expand All @@ -150,7 +179,7 @@ const TeamCommunication = () => {
nodes={nodes}
/>
<TeamNodes
previousNodes={oldSlackDataRef.current}
previousNodes={nodes}
delayBetweenInstance={delayBetweenInstance}
nodes={nodes}
/>
Expand Down
2 changes: 1 addition & 1 deletion components/team-communication/internal/TeamNodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useChart } from '@/contexts/chart-context';
import { useInitialRender } from '@/hooks/use-init-render';
import { useMedia } from '@/hooks/use-media';
import { useWindowDimensions } from '@/hooks/use-window-dimensions';
import { TeamMember } from '@/types/firebase';
import {
adjustToMedia,
determineTooltipPosition,
Expand All @@ -14,6 +13,7 @@ import { easeInOut, motion } from 'framer-motion';
import { FC, useEffect, useState } from 'react';
import { Node } from '../TeamCommunication';
import Tooltip from './Tooltip';
import { TeamMember } from '@/types/team-activity';

interface ITeamNodeProps {
nodes: Node[];
Expand Down
81 changes: 79 additions & 2 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {Bucket} from "@google-cloud/storage";
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import * as puppeteer from "puppeteer";
import * as path from "path";
import {Bucket} from "@google-cloud/storage";
import {
RTDBData,
TeamCommunicationData,
TeamCommunicationEntry,
} from "./types/firebase-types";

admin.initializeApp();

Expand Down Expand Up @@ -69,6 +73,15 @@ exports.clearRealtimeDatabase = functions.pubsub
console.log(`Screenshot saved at: ${screenshotFilePath}`);

const ref = rtdb.ref(pathToClear);
const data = await ref.once("value");
const dataValue = data.val();
const preparedTeamCommunicationsData =
prepareTeamCommunicationSync(dataValue);
preparedTeamCommunicationsData.forEach(async (entry) => {
await syncTeamMembersData(entry, "team-communication");
});


await ref.remove();

await initializeRealtimeDatabase();
Expand Down Expand Up @@ -121,3 +134,67 @@ const initializeRealtimeDatabase = async () => {
});
});
};

const prepareTeamCommunicationSync =
(data: RTDBData<TeamCommunicationData>) => {
const preparedData: TeamCommunicationEntry[] = [];

const dataSource = data["team-communication"];
for (const sensorKey in dataSource.sensors) {
if (Object.prototype.hasOwnProperty
.call(dataSource.sensors, sensorKey)) {
const sensorData = dataSource.sensors[sensorKey].value;
for (const key in sensorData) {
if (Object.prototype.hasOwnProperty
.call(sensorData, key)) {
const incomingMember = sensorData[key];

// Check if the member with the same id already exists
const existingMemberIndex =
preparedData.findIndex((member) => member.id === incomingMember.id);

if (existingMemberIndex !== -1) {
const existingMember = preparedData[existingMemberIndex];

preparedData[existingMemberIndex] = {
id: incomingMember.id,
timestamp: Date.now(),
connections:
(Object.keys(existingMember.connections).length || 0) +
(Object.keys(incomingMember.connections).length || 0),
characters: (existingMember.characters || 0) +
(incomingMember.characters || 0),
messages: (existingMember.messages || 0)+
(incomingMember.messages || 0),
reactions: (existingMember.reactions || 0)+
(incomingMember.reactions || 0),
};
} else {
preparedData.push({
id: incomingMember.id,
timestamp: Date.now(),
connections: Object.keys(incomingMember.connections).length || 0,
characters: incomingMember.characters || 0,
messages: incomingMember.messages || 0,
reactions: incomingMember.reactions || 0,
});
}
}
}
}
}

return preparedData;
};

const syncTeamMembersData = async (
entry: TeamCommunicationEntry,
stat: string,
) => {
const userRef = firestore
.collection("users")
.doc(entry.id)
.collection(`${stat}-stats`)
.doc(`stat-${entry.id}`);
await userRef.set(entry);
};
3 changes: 2 additions & 1 deletion types/data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NoiseLevelsData, TeamMember } from './firebase';
import { NoiseLevelsData } from './firebase';
import { TeamMember } from './team-activity';

export interface FormtattedDataBaseType {
'team-communication': FormattedDataBase<TeamMember[]>;
Expand Down
26 changes: 3 additions & 23 deletions types/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type ConfigType = 'ANALOG' | 'DIGITAL';
import { TeamCommunicationData } from './team-activity';

//General Modular Types across all visualizations
export type ConfigType = 'ANALOG' | 'DIGITAL';
export type DisplayType = keyof DisplayDataType;

export interface Metric<T extends keyof Views> {
Expand Down Expand Up @@ -54,25 +56,3 @@ export interface DisplayDataBase<T> {
export interface NoiseLevelsData {
db: number;
}

//team-communication
export interface TeamCommunicationData {
[key: string]: TeamMember;
}

export interface TeamMember {
id: string;
characters: number;
messages: number;
name: string;
color: string;
lastMessage: number;
lastReaction: string;
reactions: number;
connections: {
[key: string]: {
messages: number;
characters: number;
};
};
}
26 changes: 26 additions & 0 deletions types/team-activity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//Team Communication
export interface TeamCommunicationData {
[key: string]: TeamMember;
}

export interface TeamMember {
id: string;
characters: number;
messages: number;
name: string;
color: string;
lastMessage: number;
lastReaction: string;
reactions: number;
connections: {
[key: string]: {
messages: number;
characters: number;
};
};
}

export type TeamCommunicationRecordTypes = 'most-messages-characters' | 'most-reactions' | 'least-messages-characters' | 'least-reactions' | 'most-connections'
export type TeamCommunicationRecord = {
type: TeamCommunicationRecordTypes;
} & TeamMember
Loading
Loading