Skip to content

Commit

Permalink
Merge pull request #48 from socialappslab/fix/no-ticket/my-community
Browse files Browse the repository at this point in the history
fix: Render community section
  • Loading branch information
zant authored Oct 30, 2024
2 parents daf10f7 + b03be63 commit ff234cd
Show file tree
Hide file tree
Showing 13 changed files with 489 additions and 340 deletions.
86 changes: 86 additions & 0 deletions src/components/CommentBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Box } from '@mui/material';
import useAxios from 'axios-hooks';
import { deserialize, DocumentObject } from 'jsonapi-fractal';
import { useEffect, useState } from 'react';
import ThumbsUp from '@/assets/icons/thumbs-up.svg';
import useLangContext from '@/hooks/useLangContext';
import Loader from '@/themed/loader/Loader';
import Text from '@/themed/text/Text';
import { formatDateFromString } from '@/util';

interface CommentBoxProps {
postId: number;
}

interface IComment {
content: string;
createdAt: string;
id: number;
likesCount: number;
photos?: string;
postId: number;
updatedAt: string;
canDeleteByUser: boolean;
canEditByUser: boolean;
createdBy: ICommentUser;
}

interface ICommentUser {
accountId: number;
lastName: string;
userName: string;
}

const CommentBox = ({ postId }: CommentBoxProps) => {
const [{ data, loading, error }] = useAxios({
url: `/posts/${postId}`,
});
const [comments, setComments] = useState<DocumentObject[]>([]);
const langContext = useLangContext();

useEffect(() => {
if (!data) return;
const deserializedData = deserialize<{ comments: DocumentObject[] }>(data) as { comments: DocumentObject[] }[];
const first = deserializedData.shift();
if (first) setComments(first.comments);
}, [data]);

return (
<>
{loading && <Loader height="15vh" />}
{!loading &&
comments.map((commentData) => {
const comment = deserialize<IComment>(commentData) as IComment;
const acronym = `${comment.createdBy.userName[0]}${comment.createdBy.lastName[0]}`;

return (
<Box>
<Box className="flex items-center gap-4 mt-6">
<Box className="min-w-20 min-h-20 -tracking-4 bg-green-100 rounded-full flex items-center justify-center">
<Text className="mb-0 text-green-800 font-bold -tracking-4 uppercase">{acronym}</Text>
</Box>
<Box>
<Box className="flex gap-2">
<Text className="mb-0 font-semibold">
{comment.createdBy.userName} {comment.createdBy.lastName}
</Text>
<Text className="mb-0 opacity-60 max-w-max">
{formatDateFromString(langContext.state.selected, comment.createdAt)}
</Text>
</Box>
<Text className="mb-0">{comment.content}</Text>
<Box className="flex gap-2 rounded-md items-center">
<img className="self-center" src={ThumbsUp} alt="success" />
<Text className="mb-0 font-medium opacity-60">{comment.likesCount}</Text>
</Box>
</Box>
</Box>
{error && <p>Something wrong happened</p>}
</Box>
);
})}
</>
);
};

export default CommentBox;
85 changes: 85 additions & 0 deletions src/components/PostBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Box } from '@mui/material';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import Comment from '@/assets/icons/comment.svg';
import ThumbsUp from '@/assets/icons/thumbs-up.svg';
import Trash from '@/assets/icons/trash.svg';
import Text from '@/themed/text/Text';
import CommentBox from './CommentBox';

interface PostProps {
id: number;
author: string;
date: number;
location: string;
text: string;
image?: { photo_url: string };
likes: number;
comments?: number;
acronym: string;
}

const PostBox = ({ author, date, location, text, likes, image, id, comments, acronym }: PostProps) => {
const { t } = useTranslation('feed');
const [imageLoaded, setImageLoaded] = useState(false);
const [openComments, setOpenComments] = useState(false);

return (
<Box key={id} className="flex-col border-solid border-neutral-100 rounded-md p-8 flex justify-between mb-4">
<Box className="flex items-center gap-4 mb-6">
<Box className="w-20 h-20 -tracking-4 bg-green-100 rounded-full flex items-center justify-center">
<Text className="mb-0 text-green-800 font-bold -tracking-4 uppercase">{acronym}</Text>
</Box>
<Box>
<Text className="mb-0 font-semibold">{author}</Text>
<Text className="mb-0 opacity-60">
{date}{location}
</Text>
</Box>
</Box>
<Text className={`mb-0 ${image?.photo_url && 'mb-4'}`}>{text}</Text>
<Box className="rounded-lg mb-4 overflow-hidden">
{image?.photo_url && !imageLoaded && (
<div role="status" className="animate-pulse w-full">
<div className="h-96 w-full bg-lightGray opacity-50 rounded-lg" />
</div>
)}
{image?.photo_url && (
<img
src={image?.photo_url}
className="w-full object-cover"
alt="posted"
onLoad={() => setImageLoaded(true)}
/>
)}
</Box>
<Box className="flex justify-between gap-4">
<Box className="flex gap-6">
<Box className="flex gap-2 px-2 py-1 rounded-md items-center cursor-default">
<img className="self-center" src={ThumbsUp} alt="success" />
<Text className="mb-0 font-medium opacity-60">{likes}</Text>
</Box>
<Box
onClick={() => {
if (!comments) return;
setOpenComments((prev) => !prev);
}}
className={`flex gap-2 px-2 py-1 rounded-md items-center ${comments ? 'cursor-pointer hover:bg-neutral-100' : 'cursor-default'}`}
>
<img className="self-center" src={Comment} alt="success" />
<Text className="mb-0 font-medium opacity-60">
{comments} {t('post.comment')}
</Text>
</Box>
</Box>
<Box className="flex gap-2 px-2 py-1 rounded-md items-center cursor-default">
<img className="self-center" src={Trash} alt="success" />
<Text className="mb-0 font-medium opacity-60">{t('post.delete')}</Text>
</Box>
</Box>
{openComments && <CommentBox postId={id} />}
</Box>
);
};

export default PostBox;
48 changes: 48 additions & 0 deletions src/components/RiskChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Box } from '@mui/material';
import useAxios from 'axios-hooks';
import { useTranslation } from 'react-i18next';
import { ErrorResponse } from 'react-router-dom';
import useUser from '@/hooks/useUser';
import { BaseObject } from '@/schemas';
import Loader from '@/themed/loader/Loader';
import { ProgressBar } from '@/themed/progress-bar/ProgressBar';
import Title from '@/themed/title/Title';

interface HouseReport {
greenQuantity: number;
houseQuantity: number;
orangeQuantity: number;
redQuantity: number;
siteVariationPercentage: number;
visitQuantity: number;
visitVariationPercentage: number;
}

const RiskChart = () => {
const { t } = useTranslation('feed');
const user = useUser();

const [{ data, loading }] = useAxios<HouseReport, null, ErrorResponse>({
url: `reports/house_status`,
});

const label = `${(user?.team as BaseObject)?.name}: ${t('riskChart.title')}`;

return (
<Box className="border-solid border-neutral-100 rounded-md p-6 mb-4">
<Title label={label} type="subsection" className="mb-0" />
<Box className="flex flex-col mt-6">
{loading && <Loader />}
{!loading && (
<>
<ProgressBar label={t('riskChart.greenSites')} progress={data?.greenQuantity || 0} color="green-600" />
<ProgressBar label={t('riskChart.yellowSites')} progress={data?.orangeQuantity || 0} color="yellow-600" />
<ProgressBar label={t('riskChart.redSites')} progress={data?.redQuantity || 0} color="red-600" />
</>
)}
</Box>
</Box>
);
};

export default RiskChart;
22 changes: 22 additions & 0 deletions src/components/SitesReport.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Box } from '@mui/material';
import { useTranslation } from 'react-i18next';
import { ProgressBar } from '@/themed/progress-bar/ProgressBar';
import Title from '@/themed/title/Title';

const SitesReport = () => {
const { t } = useTranslation('feed');

return (
<Box className="border-solid border-neutral-100 rounded-md p-6 mb-4">
<Title label={t('sitesReport.title')} type="subsection" className="mb-0" />
<Box className="flex flex-col mt-6">
<>
<ProgressBar label={t('sitesReport.title')} progress={60} color="green-600" />
<ProgressBar label={t('sitesReport.quantity')} progress={80} color="green-800" />
</>
</Box>
</Box>
);
};

export default SitesReport;
81 changes: 81 additions & 0 deletions src/components/list/RankViewBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Box, Tab, Tabs } from '@mui/material';
import useAxios from 'axios-hooks';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import Text from '@/themed/text/Text';
import Loader from '@/themed/loader/Loader';

function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}

enum RankView {
VisitRank = 'visitRank',
GreenHouseRank = 'greenHouseRank',
}

interface Rank {
first_name: string;
last_name: string;
quantity: number;
}

interface BrigadistPerformance {
visitRank: Rank[];
greenHouseRank: Rank[];
}

const RankViewBox = () => {
const { t } = useTranslation('feed');
const [rankView, setRankView] = useState(RankView.VisitRank);

const handleChange = (_: React.SyntheticEvent, newValue: number) => {
setRankView(Object.values(RankView)[newValue] as RankView);
};

const [{ data, loading, error }] = useAxios<BrigadistPerformance>({
url: `/reports/brigadists_performance`,
});

const value = Object.values(RankView).indexOf(rankView);

return (
<Box className="border-solid border-neutral-100 rounded-md">
<Tabs value={value} onChange={handleChange} aria-label="basic tabs example">
<Tab label={t('rankView.users')} {...a11yProps(0)} className="w-1/2" />
<Tab label={t('rankView.greenHouses')} {...a11yProps(1)} className="w-1/2" />
</Tabs>
<Box className="flex flex-col p-6">
{error && <p>Error</p>}
{loading && <Loader />}
{!loading && (
<>
<Box className="flex justify-between mb-4">
<Text className="text-neutral-400 opacity-60">{t('rankView.ranking')}</Text>
<Text className="text-neutral-400 opacity-60">{t('rankView.visits')}</Text>
</Box>
<Box>
{data &&
data[rankView].map((item) => (
<Box className="flex justify-between mb-6" key={item.first_name + item.quantity}>
<Box className="flex flex-row items-center gap-3">
<Box className="rounded-full h-10 w-10 bg-neutral-100" />
<Text className="font-semibold text-neutral-400 opacity-70 mb-0">
{item.first_name} {item.last_name}
</Text>
</Box>
<Text className="font-semibold text-green-800 mb-0">{item.quantity}</Text>
</Box>
))}
</Box>
</>
)}
</Box>
</Box>
);
};

export default RankViewBox;
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"title": "City of",
"community": "My community's progress",
"city": "City of",
"sitesReport": {
"title": "Tariki Sites",
"quantity": "Amount of green containers"
},
"layout": {
"filters": "Filters",
"filter": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"title": "Ciudad de",
"community": "El progreso de mi comunidad",
"city": "Ciudad de",
"sitesReport": {
"title": "Sitios Tariki",
"quantity": "Cantidad de contenedores verdes"
},
"layout": {
"filters": "Filtros",
"filter": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"title": "Cidade de",
"community": "O progresso da minha comunidade",
"city": "Cidade de",
"sitesReport": {
"title": "Locais Tariki",
"quantity": "Quantidade de contêineres verdes"
},
"layout": {
"filters": "Filtros",
"filter": {
Expand Down
3 changes: 2 additions & 1 deletion src/layout/AppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const ADMIN_SPECIAL_PLACES = '/admin/special-places';
const ADMIN_TEAMS = '/admin/teams';

const MY_CITY = '/my-city';
const MY_COMMUNITY = '/my-community';

export function AppBar({ auth = false, signUp = false, logout }: AppBarProps) {
const { t } = useTranslation('translation');
Expand Down Expand Up @@ -126,7 +127,7 @@ export function AppBar({ auth = false, signUp = false, logout }: AppBarProps) {
</ListItemIcon>
<ListItemText primary={<Text type="menuItem">{t('menu.myCity')}</Text>} />
</ListItemButton>
<ListItemButton>
<ListItemButton component={Link} to={MY_COMMUNITY} selected={pathname.includes(MY_COMMUNITY)}>
<ListItemIcon>
<Icon type="Community" />
</ListItemIcon>
Expand Down
Loading

0 comments on commit ff234cd

Please sign in to comment.