Skip to content
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@mui/x-charts": "^6.18.4",
"construct-style-sheets-polyfill": "3.1.0",
"date-fns": "^3.0.6",
"moment": "^2.30.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-json-tree": "^0.18.0",
Expand Down
3 changes: 2 additions & 1 deletion src/pages/sidepanel/SidePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { Route, Routes, useNavigate, useLocation } from 'react-router-dom';
import BottomNavigation from '@root/src/pages/sidepanel/components/BottomNavigation';

Expand Down Expand Up @@ -220,6 +220,7 @@ const SidePanel: React.FC<SidePanelProps> = ({ key, isEnabled }) => {
profile={currentProfile}
getter={profileTab}
setter={setProfileTab}
tagConfig={tagConfig}
/>
}
/>
Expand Down
6 changes: 4 additions & 2 deletions src/pages/sidepanel/sections/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { Box, Stack, Tab, Tabs, CircularProgress } from '@mui/material';
import CustomTabPanel from '@root/src/pages/sidepanel/components/CustomTabPanel';
import ProfileDetail from '@root/src/pages/sidepanel/sections/ProfileDetail';
import ProfileSummary from '@root/src/pages/sidepanel/sections/ProfileSummary';
import { TagConfigModel } from '@root/src/shared/models/tagConfigModel';

interface ProfileTabProps {
profile: any;
profileIsLoading: boolean;
getter: number;
setter: Dispatch<SetStateAction<number>>;
tagConfig: TagConfigModel;
}

const Profile: React.FC<ProfileTabProps> = ({ profileIsLoading, profile, getter, setter }) => {
const Profile: React.FC<ProfileTabProps> = ({ profileIsLoading, profile, getter, setter, tagConfig }) => {
const handleSetTab = (event, newValue) => {
setter(newValue);
};
Expand All @@ -31,7 +33,7 @@ const Profile: React.FC<ProfileTabProps> = ({ profileIsLoading, profile, getter,
<CircularProgress color="secondary" />
</Box>
) : (
<ProfileSummary profile={profile} />
<ProfileSummary profile={profile} tagConfig={tagConfig} />
)}
</CustomTabPanel>
<CustomTabPanel value={getter} index={1}>
Expand Down
169 changes: 165 additions & 4 deletions src/pages/sidepanel/sections/ProfileSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import React, { useEffect, useState } from 'react';
import { Box, Button, Chip, Divider, LinearProgress, Stack, Typography } from '@mui/material';
import React, { useCallback, useEffect, useState } from 'react';
import {
Box,
Button,
Chip,
Divider,
LinearProgress,
Stack,
Typography,
Grid,
Table,
TableContainer,
TableBody,
TableRow,
TableCell,
Collapse,
Tooltip,
} from '@mui/material';
import { makeStyles } from '@mui/styles';
import { Lock } from '@mui/icons-material';
import { ExpandMore, HelpOutline, Lock } from '@mui/icons-material';
import SimpleTable from '@root/src/pages/sidepanel/components/SimpleTable';
import { TagConfigModel } from '@root/src/shared/models/tagConfigModel';

interface BarStylesProps {
backgroundGradient: string;
Expand All @@ -19,6 +36,23 @@ const barStyles = makeStyles(() => ({
}));
interface ProfileSummaryTabProps {
profile: any;
tagConfig: TagConfigModel;
}

export interface ContentEntity {
created?: string;
fetched?: string;
updated?: string;
description?: string;
longDescription?: string;
httpstatus?: string;
language?: string;
primaryImage?: string;
url?: string;
collections?: string[];
// topics are a map[string]number
topics?: Record<string, number>;
confidence?: number;
}

const HighlightBox: React.FC<{ headline: string; cta?: React.ReactNode; value: React.ReactNode }> = ({
Expand Down Expand Up @@ -92,14 +126,130 @@ const CustomBarChart: React.FC<CustomBarChartProps> = ({ data, color1, color2 }:
);
};

const ProfileSummary: React.FC<ProfileSummaryTabProps> = ({ profile }) => {
const RecommendationTile = ({ item }: { item: ContentEntity }) => {
const [open, setOpen] = useState(false);

return (
<Grid container spacing={1} borderRadius={1} bgcolor={'#FFF'} mb={2}>
<Grid item xs={12} onClick={() => setOpen(!open)}>
<Box display="flex" alignItems="center" justifyContent="space-between">
<Typography variant={'body1'} width={'90%'}>
<a href={item?.url}>{item?.url}</a>
</Typography>
<ExpandMore />
</Box>
</Grid>
<Collapse in={open}>
<TableContainer>
<Table>
<TableBody>
<TableRow key={'url'}>
<TableCell align="left" size="small">
<Typography variant={'body2'}>URL</Typography>
</TableCell>
<TableCell align="right" size="small">
<Typography variant={'body2'}>
<a href={item?.url}>{item?.url}</a>
</Typography>
</TableCell>
</TableRow>
<TableRow key={'description'}>
<TableCell align="left" size="small">
<Typography variant={'body2'}>Description</Typography>
</TableCell>
<TableCell align="right" size="small">
<Typography variant={'body2'}>
{item?.description || item?.longDescription || 'No description available'}
</Typography>
</TableCell>
</TableRow>
<TableRow key={'topics'}>
<TableCell align="left" size="small">
<Typography variant={'body2'}>Topics</Typography>
</TableCell>
<TableCell align="right" size="small">
{item.topics &&
Object.keys(item.topics).map((topic, index) => (
<Chip key={index} label={topic} style={{ marginRight: 1, marginTop: 1 }} />
))}
</TableCell>
</TableRow>
<TableRow key={'image'}>
<TableCell align="left">
<Typography variant={'body2'}>Image</Typography>
</TableCell>
<TableCell align="right">
<img src={item.primaryImage} alt={'Not Available'} width={'100vw'} />
</TableCell>
</TableRow>
<TableRow key={'confidence'}>
<TableCell align="left" size="small">
<Box display="flex" alignItems="center">
<Typography variant={'body2'} display="inline">
Confidence
</Typography>
<Tooltip title="The confidence score of the content item to the user">
<HelpOutline fontSize="small" />
</Tooltip>
</Box>
</TableCell>
<TableCell align="right" size="small">
<Typography variant={'body2'}>{Math.round(item.confidence * 100) || 0}</Typography>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Collapse>
</Grid>
);
};

const ProfileSummary: React.FC<ProfileSummaryTabProps> = ({ profile, tagConfig }) => {
const [hasContent, setHasContent] = useState(false);
const [hasScores, setHasScores] = useState(false);
const [totalAttributes, setTotalAttributes] = useState(0);
const [scores, setScores] = useState([]);
const [affinities, setAffinities] = useState<{ [key: string]: number }>({});
const [computedAttributes, setComputedAttributes] = useState<{ [key: string]: string }>({});

const [recommendations, setRecommendations] = useState<ContentEntity[]>([]);
const recommendAPI = useCallback(async () => {
if (!profile || !tagConfig || !profile.data) {
return;
}
const accountID = tagConfig?.cid?.[0];
const uid = profile?.data?._uid as string;
if (!accountID || !uid) {
return;
}

const response = await fetch(`https://api.lytics.io/api/content/recommend/${accountID}/user/_uid/${uid}`);
const json = await response.json();
// for each item in the data array in JSON, convert to a ContentEntity
const contentEntities = json.data.map(item => {
const entity: ContentEntity = {
created: item.created,
fetched: item.fetched,
updated: item.updated,
description: item.description,
longDescription: item.long_description,
httpstatus: item.httpstatus,
primaryImage: item.primary_image,
url: item?.url,
topics: item?.global,
confidence: item.confidence,
};
return entity;
});
setRecommendations(contentEntities);
return;
}, [profile, tagConfig]);

useEffect(() => {
recommendAPI();
}, [profile, tagConfig, recommendAPI]);

const appendScore = (scoresArray, profileData, propertyName, label) => {
const propertyValue = profileData?.user?.[propertyName];

Expand Down Expand Up @@ -301,6 +451,17 @@ const ProfileSummary: React.FC<ProfileSummaryTabProps> = ({ profile }) => {
</Box>
),
},
{
label: 'Content Recommendations',
position: 'top',
fancyValue: (
<>
{recommendations.map((item, index) => (
<RecommendationTile key={index} item={item} />
))}
</>
),
},
]}
/>
<Divider sx={{ mt: 0.5 }} />
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5058,6 +5058,11 @@ mlly@^1.2.0, mlly@^1.4.2:
pkg-types "^1.0.3"
ufo "^1.3.0"

moment@^2.30.1:
version "2.30.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==

[email protected]:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
Expand Down
Loading