diff --git a/zubhub_backend/zubhub/activities/views.py b/zubhub_backend/zubhub/activities/views.py
index 7a7f015b7..8e371b94e 100644
--- a/zubhub_backend/zubhub/activities/views.py
+++ b/zubhub_backend/zubhub/activities/views.py
@@ -1,29 +1,28 @@
-from django.shortcuts import render
-from django.utils.translation import ugettext_lazy as _
-from rest_framework.decorators import api_view
-from rest_framework.response import Response
-from rest_framework.generics import (
- ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView)
-from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly, AllowAny
-from .permissions import IsStaffOrModeratorOrEducator, IsOwner, IsStaffOrModerator
-from django.shortcuts import get_object_or_404
-from .models import *
-from .serializers import *
-from django.db import transaction
-from django.core.exceptions import PermissionDenied
from django.contrib.auth.models import AnonymousUser
+from django.db import transaction
+from django.shortcuts import get_object_or_404
+from rest_framework.generics import (
+ CreateAPIView,
+ DestroyAPIView,
+ ListAPIView,
+ RetrieveAPIView,
+ UpdateAPIView,
+)
+from rest_framework.permissions import AllowAny, IsAuthenticated
+from .models import Activity
+from .permissions import IsOwner, IsStaffOrModerator, IsStaffOrModeratorOrEducator
+from .serializers import ActivitySerializer
class ActivityListAPIView(ListAPIView):
-
serializer_class = ActivitySerializer
permission_classes = [AllowAny]
def get_queryset(self):
all = Activity.objects.all()
return all
-
+
class UserActivitiesAPIView(ListAPIView):
"""
@@ -33,10 +32,11 @@ class UserActivitiesAPIView(ListAPIView):
serializer_class = ActivitySerializer
permission_classes = [IsAuthenticated, IsOwner]
-
+
def get_queryset(self):
return self.request.user.activities_created.all()
+
class ActivityDetailsAPIView(RetrieveAPIView):
"""
Fetch Activity details.
@@ -47,28 +47,29 @@ class ActivityDetailsAPIView(RetrieveAPIView):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
- permission_classes = [IsAuthenticated]
+ permission_classes = [AllowAny]
def get_object(self):
queryset = self.get_queryset()
pk = self.kwargs.get("pk")
obj = get_object_or_404(queryset, pk=pk)
-
+
if obj:
with transaction.atomic():
if isinstance(self.request.user, AnonymousUser):
obj.views_count += 1
obj.save()
else:
- if not self.request.user in obj.views.all():
+ if self.request.user not in obj.views.all():
obj.views.add(self.request.user)
obj.views_count += 1
obj.save()
return obj
-
+
else:
raise Exception()
+
class PublishedActivitiesAPIView(ListAPIView):
"""
Fetch list of published activities by any user.
@@ -77,17 +78,18 @@ class PublishedActivitiesAPIView(ListAPIView):
serializer_class = ActivitySerializer
permission_classes = [AllowAny]
-
+
def get_queryset(self):
- limit = self.request.query_params.get('limit', 10000)
+ limit = self.request.query_params.get("limit", 10000)
try:
limit = int(limit)
except ValueError:
limit = 10
- return Activity.objects.filter(publish= True)[:limit]
-
+ return Activity.objects.filter(publish=True)[:limit]
+
+
class UnPublishedActivitiesAPIView(ListAPIView):
"""
Fetch list of unpublished activities by authenticated staff member.
@@ -100,24 +102,29 @@ class UnPublishedActivitiesAPIView(ListAPIView):
permission_classes = [IsAuthenticated, IsStaffOrModerator]
def get_queryset(self):
- return Activity.objects.filter(publish= False)
+ return Activity.objects.filter(publish=False)
+
class ActivityCreateAPIView(CreateAPIView):
"""
Create new Activity.\n
"""
+
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = [IsAuthenticated, IsStaffOrModeratorOrEducator]
+
class ActivityUpdateAPIView(UpdateAPIView):
"""
Update activity.
"""
+
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = [IsAuthenticated, IsOwner]
+
class ActivityDeleteAPIView(DestroyAPIView):
"""
Delete a activity and related objects from database.
@@ -126,9 +133,10 @@ class ActivityDeleteAPIView(DestroyAPIView):
Requires activity id.
Returns {details: "ok"}
"""
+
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
- permission_classes = [IsAuthenticated, IsOwner]
+ permission_classes = [IsAuthenticated, IsOwner, IsStaffOrModeratorOrEducator]
def delete(self, request, *args, **kwargs):
activity = self.get_object()
@@ -150,11 +158,11 @@ class ToggleSaveAPIView(RetrieveAPIView):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = [IsAuthenticated]
-
+
def get_object(self):
pk = self.kwargs.get("pk")
obj = get_object_or_404(self.get_queryset(), pk=pk)
-
+
if self.request.user in obj.saved_by.all():
obj.saved_by.remove(self.request.user)
obj.save()
@@ -170,15 +178,14 @@ class togglePublishActivityAPIView(RetrieveAPIView):
Requires activity id.
Returns updated activity.
"""
+
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = [IsAuthenticated, IsStaffOrModerator]
-
def get_object(self):
-
pk = self.kwargs.get("pk")
- obj = get_object_or_404(self.get_queryset(), pk=pk)
+ obj = get_object_or_404(self.get_queryset(), pk=pk)
obj.publish = not obj.publish
obj.save()
return obj
diff --git a/zubhub_frontend/zubhub/public/locales/en/translation.json b/zubhub_frontend/zubhub/public/locales/en/translation.json
index 983199552..29bc747bb 100644
--- a/zubhub_frontend/zubhub/public/locales/en/translation.json
+++ b/zubhub_frontend/zubhub/public/locales/en/translation.json
@@ -1098,7 +1098,8 @@
"mediaServerError": "Sorry media server is down we couldn't upload your files! try again later",
"uploadError": "error occurred while downloading file : "
}
- }
+ },
+ "tooltipMore": " more"
},
"activityDetails": {
@@ -1112,11 +1113,18 @@
"contributors": "CONTRIBUTORS"
},
"made": "Made by",
+ "inspired": {
+ "recreated": "Re-created",
+ "times": "times"
+ },
"activity": {
"creator": {
"follow": "Follow",
"unfollow": "Unfollow"
},
+ "introduction": "Introduction",
+ "categories": "Categories",
+ "classGrade": "Class Grade",
"description": "Description",
"materials": "Materials Used",
"category": "Category",
@@ -1124,7 +1132,10 @@
"none": "None",
"build": "Let's Make This Project",
- "pdf": "Download Pdf",
+ "pdf": {
+ "downloading": "Downloading...",
+ "download": "Download PDF"
+ },
"create": {
"dialog": {
"primary": "Create Activity",
@@ -1132,6 +1143,10 @@
"proceed": "Proceed",
"success": "activity Created successfully",
"forbidden": "You must be staff monitor ao educator to be able to create a new activity "
+ },
+ "modal": {
+ "success": "Congratulations your Activity has been successfully created!",
+ "share": "Share your activity with the world. Post it on the following platforms:"
}
},
"edit": {
@@ -1159,14 +1174,19 @@
"label": "Publish"
},
"unpublish": {
- "label": "UnPublish"
+ "label": "Unpublish"
}
},
"saveButton": {
"label": "save button",
"save": "save",
"unsave": "unsave"
- }
+ },
+ "footer": {
+ "introductionText": "Did you like this activity?",
+ "buttonLabel": "Create it!",
+ "moreActivitiesTitle": "More Activities"
+ }
},
"breadCrumb":{
"link":{
diff --git a/zubhub_frontend/zubhub/src/assets/js/colors.js b/zubhub_frontend/zubhub/src/assets/js/colors.js
index 0cdb70aa2..96477fcf4 100644
--- a/zubhub_frontend/zubhub/src/assets/js/colors.js
+++ b/zubhub_frontend/zubhub/src/assets/js/colors.js
@@ -1,22 +1,23 @@
export const colors = {
- primary: '#02B7C4',
- "primary-01": "#E5F8F9",
- secondary: '#DC3545',
- tertiary: '#FECB00',
- 'tertiary-dark': '#C18D30',
- black: '#292535',
- gray: '#7E7E7E',
- light: '#C4C4C4',
- white: '#fff',
- green: '#22C55E',
- red: '#f44336',
- 'blue-light': '#00B8C433',
- 'blue-dark': '#7BA8AB',
- 'blue-pale': '#DBECFF'
-}
+ primary: '#02B7C4',
+ 'primary-01': '#E5F8F9',
+ secondary: '#DC3545',
+ tertiary: '#FECB00',
+ 'tertiary-dark': '#C18D30',
+ black: '#292535',
+ gray: '#7E7E7E',
+ light: '#C4C4C4',
+ white: '#fff',
+ green: '#22C55E',
+ red: '#f44336',
+ 'blue-light': '#00B8C433',
+ 'blue-dark': '#7BA8AB',
+ 'blue-pale': '#DBECFF',
+ border: '#7E5B4B',
+};
export const borders = {
- borderRadius: 20,
- borderRadiusMd: 8,
- borderRadiusSm: 4,
-}
\ No newline at end of file
+ borderRadius: 20,
+ borderRadiusMd: 8,
+ borderRadiusSm: 4,
+};
diff --git a/zubhub_frontend/zubhub/src/assets/js/muiTheme.js b/zubhub_frontend/zubhub/src/assets/js/muiTheme.js
index 53022e34f..048e7cc86 100644
--- a/zubhub_frontend/zubhub/src/assets/js/muiTheme.js
+++ b/zubhub_frontend/zubhub/src/assets/js/muiTheme.js
@@ -32,4 +32,17 @@ export const theme = createTheme({
},
},
},
+ categoryColors: {
+ Animations: '#FCB07F',
+ Art: '#F8D991',
+ Science: '#FBC9B3',
+ Coding: '#65B4BD',
+ Electronics: '#F1D27C',
+ Toys: '#FAC5C2',
+ Games: '#6065A4',
+ Mechanical: '#F571AE',
+ Music: '#F1FC73',
+ Robotics: '#A66CA9',
+ Structures: '#FAE393',
+ },
});
diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/components/activity/activityStyle.js b/zubhub_frontend/zubhub/src/assets/js/styles/components/activity/activityStyle.js
index bbbeaf037..db0a820c8 100644
--- a/zubhub_frontend/zubhub/src/assets/js/styles/components/activity/activityStyle.js
+++ b/zubhub_frontend/zubhub/src/assets/js/styles/components/activity/activityStyle.js
@@ -1,15 +1,14 @@
-export const style = theme => ({
+export const style = () => ({
activityCardContainer: {
position: 'relative',
- // maxWidth: '350px',
- // minWidth: '300px',
- height: '95%',
+ width: '100%',
+ textAlign: 'left',
},
activityCard: {
maxWidth: '100%',
- borderRadius: '15px',
+ height: '33em',
+ borderRadius: '20px',
position: 'relative!important',
- height: '100%',
},
opacity: {
backgroundColor: 'black',
@@ -29,55 +28,21 @@ export const style = theme => ({
},
mediaBoxStyle: {
width: '100%',
- height: '17em',
+ height: '13em',
position: 'relative',
padding: '2%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
- activityTagsBox: {
- position: 'absolute',
- top: '10px',
- right: '10%',
- display: 'flex',
- },
- activityTagPill: {
- backgroundColor: 'white',
- color: 'var(--text-color2)',
- border: '1px solid var(--text-color2)',
- '&:hover': {
- backgroundColor: 'var(--text-color2)',
- color: 'white',
- border: '1px solid white',
- },
- },
- activityTagsShowMore: {
- '&:hover': {
- backgroundColor: 'white',
- color: 'var(--text-color2)',
- border: '1px solid white',
- },
- },
- tagsShowMoreIconContainer: {
- //position: 'absolute',
- },
- tagsShowMoreList: {
- position: 'absolute',
- right: '0%',
- backgroundColor: 'white',
- maxHeight: '12em',
- overflow: 'auto',
- borderRadius: '10px',
- },
-
activityCardContent: {
width: '100%',
- position: 'relative',
- },
- activityCardInfoBox: {
- height: '100%',
+ padding: '16px',
+ '&:last-child': {
+ paddingBottom: '12px',
+ },
display: 'flex',
+ flexDirection: 'column',
justifyContent: 'space-between',
},
projectsCount: {
@@ -92,10 +57,67 @@ export const style = theme => ({
marginLeft: '5px',
},
activityTitle: {
- fontSize: '1.1rem',
- fontWeight: '900',
+ fontSize: '1.3rem',
+ fontWeight: 700,
color: 'var(--text-color1)',
- // width: '80%',
- textAlign: '-webkit-auto',
+ },
+ activityDescription: {
+ height: '48px',
+ margin: '8px 0',
+ textOverflow: 'ellipsis',
+ overflow: 'hidden',
+ display: '-webkit-box',
+ '-webkit-line-clamp': 2,
+ '-webkit-box-orient': 'vertical',
+ },
+ activityCategoryContainer: {
+ margin: '12px 0',
+ display: 'flex',
+ flexWrap: 'nowrap',
+ gap: '8px',
+ },
+ activityCategory: {
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ fontSize: '0.9em',
+ padding: '2px 10px',
+ border: '1px solid #7E5B4B',
+ borderRadius: '10em',
+ background: '#F1D27C',
+ },
+ footer: {
+ marginTop: 10,
+ display: 'flex',
+ flexDirection: 'row',
+ flexWrap: 'nowrap',
+ overflow: 'hidden',
+ },
+ captionStyle: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ captionIconStyle: {
+ backgroundColor: '#eee',
+ padding: '2px 7px',
+ borderRadius: 25,
+ justifyContent: 'space-between',
+ fontWeight: '600',
+ display: 'flex',
+ alignItems: 'center',
+ marginRight: '1em',
+ '& svg': {
+ fill: 'rgba(0,0,0,0.54)',
+ marginRight: '0.5em',
+ fontSize: '1.1rem',
+ },
+ },
+ date: {
+ fontSize: '0.9rem',
+ fontWeight: '600',
+ marginLeft: 'auto',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
},
});
diff --git a/zubhub_frontend/zubhub/src/assets/js/utils/constants.js b/zubhub_frontend/zubhub/src/assets/js/utils/constants.js
index 5476630d4..c89f13c01 100644
--- a/zubhub_frontend/zubhub/src/assets/js/utils/constants.js
+++ b/zubhub_frontend/zubhub/src/assets/js/utils/constants.js
@@ -7,6 +7,12 @@
*/
export const BASE_TAGS = ['staff', 'moderator', 'group', 'creator'];
+export const USER_TAGS = {
+ staff: 'staff',
+ creator: 'creator',
+ educator: 'educator',
+};
+
export const site_mode = {
PUBLIC: 1,
PRIVATE: 2,
diff --git a/zubhub_frontend/zubhub/src/components/activity/activity.jsx b/zubhub_frontend/zubhub/src/components/activity/activity.jsx
index 870e9665c..99fc2c93b 100644
--- a/zubhub_frontend/zubhub/src/components/activity/activity.jsx
+++ b/zubhub_frontend/zubhub/src/components/activity/activity.jsx
@@ -1,48 +1,27 @@
-import React, { useState } from 'react';
+import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import clsx from 'clsx';
import { makeStyles } from '@mui/styles';
-import BookmarkIcon from '@mui/icons-material/Bookmark';
-import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder';
-
-import {
- Card,
- CardActions,
- CardContent,
- CardMedia,
- Typography,
- Box,
- List,
- ListItem,
- ListItemText,
- Grid,
- Fab,
-} from '@mui/material';
-
+import { Card, CardActions, CardContent, CardMedia, Typography, Box } from '@mui/material';
+import VisibilityIcon from '@mui/icons-material/Visibility';
+import EmojiObjectsIcon from '@mui/icons-material/EmojiObjects';
+import { style } from '../../assets/js/styles/components/activity/activityStyle';
import { getActivities, activityToggleSave, setActivity } from '../../store/actions/activityActions';
import commonStyles from '../../assets/js/styles';
-import Creator from '../creator/creator';
-import { toggleSave } from './activityScripts';
-import { style } from '../../assets/js/styles/components/activity/activityStyle';
+import { dFormatter } from '../../assets/js/utils/scripts';
+import Categories from '../categories/Categories';
+import Creators from '../creators/Creators';
const useCommonStyles = makeStyles(commonStyles);
const useStyles = makeStyles(style);
function Activity(props) {
const { activity, t } = { ...props };
- const [tagsShowMore, setTagsShowMore] = useState(false);
const classes = useStyles();
const common_classes = useCommonStyles();
- const topMarginCoefficient = activity.creator?.length < 6 ? 2 : 1;
-
return (
- {activity.creators?.length > 0
- ? activity.creators.map((creator, index) => (
-
- ))
- : ''}
-
-
- {activity.tags?.length > 0
- ? activity.tags.slice(0, 3).map(tag => (
-
- {tag.name}
-
- ))
- : ''}
- {activity.tags?.length > 3 ? (
- setTagsShowMore(true)}
- onMouseOut={() => setTagsShowMore(false)}
- >
-
- {['+', activity.tags.length - 3].join('')}
-
-
- ) : (
- ''
- )}
-
-
- {tagsShowMore ? (
-
setTagsShowMore(true)}
- onMouseLeave={() => setTagsShowMore(false)}
- >
-
- {activity.tags?.map(tag => (
-
-
-
- ))}
-
-
- ) : (
- ''
- )}
- toggleSave(e, activity.id, props.auth, props.navigate, props.activityToggleSave, t)}
+
+ {activity.title}
+
+
- {props.auth && activity.saved_by.includes(props.auth.id) ? (
-
- ) : (
-
- )}
-
-
-
-
- {activity.title}
+ {activity.introduction.replace(/(<([^>]+)>)/gi, '')}
+
+ {activity.category.length > 0 && }
+
+
+
+
+ {activity.views_count}
-
-
- props.setActivity(activity)}
+
-
- {`${t('activities.LinkedProjects')} `}{' '}
- {` ${activity.inspired_projects.length}`}
-
-
-
-
+ {activity.inspired_projects.length}
+
+
+
+ {`
+ ${dFormatter(activity.created_on).value}
+ ${t(`date.${dFormatter(activity.created_on).key}`)}
+ ${t('date.ago')}
+ `}
+
+
diff --git a/zubhub_frontend/zubhub/src/components/categories/Categories.jsx b/zubhub_frontend/zubhub/src/components/categories/Categories.jsx
new file mode 100644
index 000000000..7d90ddebc
--- /dev/null
+++ b/zubhub_frontend/zubhub/src/components/categories/Categories.jsx
@@ -0,0 +1,24 @@
+import { makeStyles, useTheme } from '@mui/styles';
+import Chip from '@mui/material/Chip';
+import { styles } from './categories.styles';
+
+const Categories = ({ categories }) => {
+ const classes = makeStyles(styles)();
+ const theme = useTheme();
+ return (
+
+ {categories?.map(category => (
+
+ ))}
+
+ );
+};
+
+export default Categories;
diff --git a/zubhub_frontend/zubhub/src/components/categories/categories.styles.js b/zubhub_frontend/zubhub/src/components/categories/categories.styles.js
new file mode 100644
index 000000000..1f562a4ba
--- /dev/null
+++ b/zubhub_frontend/zubhub/src/components/categories/categories.styles.js
@@ -0,0 +1,18 @@
+import { colors } from '../../assets/js/colors';
+
+export const styles = () => ({
+ container: {
+ border: '2px olid red',
+ margin: '12px 0',
+ display: 'flex',
+ flexWrap: 'nowrap',
+ gap: '8px',
+ },
+ chip: {
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ fontSize: '0.9em',
+ border: `1px solid ${colors.border}`,
+ borderRadius: '10em',
+ },
+});
diff --git a/zubhub_frontend/zubhub/src/components/creators/Creators.jsx b/zubhub_frontend/zubhub/src/components/creators/Creators.jsx
new file mode 100644
index 000000000..e0cdc80cd
--- /dev/null
+++ b/zubhub_frontend/zubhub/src/components/creators/Creators.jsx
@@ -0,0 +1,88 @@
+import { makeStyles } from '@mui/styles';
+import { Link } from 'react-router-dom';
+import { Typography, Box, Avatar, Tooltip } from '@mui/material';
+import clsx from 'clsx';
+import { useTranslation } from 'react-i18next';
+import commonStyles from '../../assets/js/styles';
+import { creatorsStyles } from './creators.styles';
+
+const useCommonStyles = makeStyles(commonStyles);
+const useStyles = makeStyles(creatorsStyles);
+
+const Creators = ({ creators }) => {
+ const classes = useStyles();
+ const common_classes = useCommonStyles();
+ const { t } = useTranslation();
+
+ return (
+ <>
+ {creators.length === 1 ? (
+ creators.map((creator, index) => (
+
+
+
+
+
+
+ {creator.username}
+
+ {creator.tags.map((tag, index) => (
+
+ {tag}
+
+ ))}
+
+
+
+
+ ))
+ ) : (
+
+
+ {creators.slice(0, 3).map((creator, index) => (
+ 3
+ ? `${creators.length - 2} ${t('activities.tooltipMore')}`
+ : creator.username
+ }
+ placement="bottom"
+ arrow
+ className={classes.creatorUsernameTooltip}
+ style={{ zIndex: index }}
+ >
+ {index === 2 && creators.length > 3 ? (
+ {`+${creators.length - 2}`}
+ ) : (
+
+ )}
+
+ ))}
+
+
+
+ {creators[creators.length - 1].username}
+
+ {creators[creators.length - 1].tags.map((tag, index) => (
+
+ {tag}
+
+ ))}
+
+
+ )}
+ >
+ );
+};
+
+export default Creators;
diff --git a/zubhub_frontend/zubhub/src/components/creators/creators.styles.js b/zubhub_frontend/zubhub/src/components/creators/creators.styles.js
new file mode 100644
index 000000000..9cf879e44
--- /dev/null
+++ b/zubhub_frontend/zubhub/src/components/creators/creators.styles.js
@@ -0,0 +1,50 @@
+export const creatorsStyles = () => ({
+ creatorBox: {
+ margin: '15px 0 0.5em 0',
+ display: 'flex',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ flexWrap: 'nowrap',
+ gap: '15px',
+ overflow: 'hidden',
+ },
+ twoCreatorBox: {
+ minWidth: '4em',
+ transition: '0.4s',
+ display: 'grid',
+ gridTemplateColumns: '1.5em 1.5em',
+ '&:hover': {
+ gridTemplateColumns: '3.2em 3.2em',
+ },
+ },
+ multipleCreatorBox: {
+ minWidth: '5.5em',
+ transition: '0.4s',
+ display: 'grid',
+ gridTemplateColumns: '1.5em 1.5em 1.5em',
+ '&:hover': {
+ gridTemplateColumns: '3.2em 3.2em 3.2em',
+ },
+ },
+ creatorAvatar: {
+ boxShadow: `0 3px 5px 2px rgba(0, 0, 0, .12)`,
+ background: 'white',
+ color: 'black',
+ },
+ creatorUsernameTooltip: {
+ marginRight: 'auto',
+ fontWeight: '400',
+ },
+ creatorUsername: {
+ flex: 1,
+ textOverflow: 'ellipsis',
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ fontWeight: '600',
+ fontSize: '1.1em',
+ },
+ creatorTag: {
+ fontWeight: '500',
+ fontSize: '0.9rem',
+ },
+});
diff --git a/zubhub_frontend/zubhub/src/components/social_share_buttons/socialShareButtons.jsx b/zubhub_frontend/zubhub/src/components/social_share_buttons/socialShareButtons.jsx
index c0e1b4b3e..ea5ee3222 100644
--- a/zubhub_frontend/zubhub/src/components/social_share_buttons/socialShareButtons.jsx
+++ b/zubhub_frontend/zubhub/src/components/social_share_buttons/socialShareButtons.jsx
@@ -13,7 +13,7 @@ import { colors } from '../../assets/js/colors';
import styles from '../../assets/js/styles/components/social_share_buttons/socialShareButtonsStyles';
const useStyles = makeStyles(styles);
-const SocialButtons = ({ facebook, whatsapp, link, withColor, containerStyle = {} }) => {
+const SocialButtons = ({ facebook, whatsapp, link, withColor, containerStyle = {}, styleOverrides }) => {
const { t } = useTranslation();
const classes = useStyles();
const url = window.location.href;
@@ -21,10 +21,10 @@ const SocialButtons = ({ facebook, whatsapp, link, withColor, containerStyle = {
return (
<>
-
+
{(showAll || facebook) && (
window.open(
`https://www.facebook.com/sharer/sharer.php?u=${url}"e=${t('projectDetails.socialShare.fbwa')}`,
@@ -38,7 +38,7 @@ const SocialButtons = ({ facebook, whatsapp, link, withColor, containerStyle = {
{(showAll || whatsapp) && (
window.open(`https://api.whatsapp.com/send?text=${t('projectDetails.socialShare.fbwa')} ${url}`)
}
@@ -50,7 +50,7 @@ const SocialButtons = ({ facebook, whatsapp, link, withColor, containerStyle = {
{(showAll || link) && (
{
navigator.clipboard
.writeText(url)
diff --git a/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetails.styles.js b/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetails.styles.js
index 8367438cd..423ab2aff 100644
--- a/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetails.styles.js
+++ b/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetails.styles.js
@@ -1,87 +1,135 @@
import { colors } from '../../assets/js/colors';
-export const activityDefailsStyles = theme => ({
- container: {
- margin: '2em 24px',
- [theme.breakpoints.down('378')]: {
- marginTop: '3em 24px',
+export const activityDetailsStyles = theme => ({
+ mainContainer: {
+ [theme.breakpoints.down('sm')]: {
+ margin: '0 24px',
},
- // borderRadius: 8,
- // backgroundColor: colors.white,
- // padding: 24
- },
- descriptionBodyStyle: {
- marginBottom: '0.7em',
- color: 'rgba(0, 0, 0, 0.54)',
- '& .ql-editor': {
- fontSize: '1.01rem',
- fontFamily: 'Raleway,Roboto,sans-serif',
- padding: '4px 0',
- lineHeight: 1.9,
+ [theme.breakpoints.down('xs')]: {
+ margin: '0 12px',
},
},
- socialButtons: {
- backgroundColor: colors.primary,
- width: '100%',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- margin: '16px 0',
- borderRadius: 8,
- },
- moreTextTitle: {
- marginTop: 50,
- marginBottom: 30,
- fontSize: 22,
- fontWeight: 'bold',
- color: colors.black,
+ signedOutMainContainer: {
+ padding: '2em 12em',
+ [theme.breakpoints.down('sm')]: {
+ padding: '2em 0',
+ },
+ [theme.breakpoints.down('xs')]: {
+ padding: '3em 0',
+ },
},
card: {
borderRadius: 8,
backgroundColor: colors.white,
padding: 24,
+ marginBottom: 40,
+ [theme.breakpoints.down('xs')]: {
+ padding: '24px 16px',
+ },
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 16,
},
- creatorProfileStyle: {
- width: '100%',
+ headerFlex: {
display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
alignItems: 'center',
- marginBottom: '1em',
- '& a': {
- display: 'flex',
- alignItems: 'center',
- },
- [theme.breakpoints.down('500')]: {
- width: '100%',
- // justifyContent: 'space-between',
- },
},
- actionBoxButtonStyle: {
- color: 'white',
- '& MuiFab-root:hover': {
- color: '#F2F2F2',
+ headerTitle: {
+ fontWeight: 700,
+ },
+ headerButton: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 4,
+ padding: '0 1.5em',
+ height: '2.5em',
+ '& .MuiButton-label': {
+ gap: '0.5em',
},
- '& svg': {
- fill: 'white',
+ },
+ headerIconBox: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '1em',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ [theme.breakpoints.down('400')]: {
+ gap: '0.5em',
+ fontSize: '0.9em',
},
- '& svg:hover': {
- fill: '#F2F2F2',
+ },
+ headerIconText: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '0.5em',
+ fontSize: '1em',
+ [theme.breakpoints.down('400')]: {
+ '& .MuiSvgIcon-root': {
+ fontSize: '1.2em',
+ },
},
},
- closed: {
- height: 0,
- transition: '0.4s',
- overflow: 'hidden',
+ creatorBox: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: 8,
+ },
+ avatar: {
+ boxShadow: `0 3px 5px 2px rgba(0, 0, 0, .12)`,
+ },
+ creatorUsername: {
+ fontWeight: 500,
+ fontSize: 16,
+ textTransform: 'capitalize',
+ color: colors.black,
+ },
+ cardTitle: {
+ fontSize: '1.2em',
+ fontWeight: 600,
+ },
+ descriptionBodyStyle: {
+ '& .ql-editor': {
+ fontSize: '1.1em',
+ fontFamily: 'Raleway,Roboto,sans-serif',
+ lineHeight: 1.9,
+ margin: 0,
+ padding: 0,
+ '& ol': {
+ padding: 0,
+ },
+ },
},
- expandableMargin: {
- // marginBottom: 10,
- marginTop: 30,
+ classGrade: {
+ width: 'fit-content',
+ border: `1px solid ${colors.primary}`,
},
- expanded: {
- transition: '0.4s',
- height: 'fit-content',
+ footer: {
display: 'flex',
flexDirection: 'column',
- gap: 32,
- marginTop: 20,
+ gap: '2em',
+ textAlign: 'center',
+ },
+ footerTitle: {
+ fontSize: 22,
+ fontWeight: 700,
+ },
+ menuItemIcon: {
+ minWidth: '2em',
+ },
+ dangerButton: {
+ color: colors.secondary,
+ },
+});
+
+export const socialButtonsStyleOverrides = () => ({
+ containerStyle: {
+ display: 'flex',
+ },
+ outlined: {
+ border: 'none',
},
});
diff --git a/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx b/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx
index 636033227..e842eb11d 100644
--- a/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx
+++ b/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx
@@ -10,36 +10,46 @@ import {
Menu,
MenuItem,
Typography,
+ Chip,
+ ListItemIcon,
+ ListItemText,
} from '@mui/material';
-import { CloseOutlined, MoreVert } from '@mui/icons-material';
-import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder';
-import VisibilityIcon from '@mui/icons-material/Visibility';
+import {
+ CloseOutlined,
+ MoreVert,
+ Visibility as VisibilityIcon,
+ EmojiObjects as EmojiObjectsIcon,
+ Share as ShareIcon,
+} from '@mui/icons-material';
import clsx from 'clsx';
import React, { useEffect, useRef, useState } from 'react';
import ReactConfetti from 'react-confetti';
import { useTranslation } from 'react-i18next';
-import { FiShare } from 'react-icons/fi';
+import { FiShare, FiDownload, FiEdit, FiTrash2 } from 'react-icons/fi';
+import { MdPublish, MdFileDownloadOff } from 'react-icons/md';
import ReactQuill from 'react-quill';
import { useSelector } from 'react-redux';
+import { Link } from 'react-router-dom';
import { useReactToPrint } from 'react-to-print';
import Html2Pdf from 'html2pdf.js';
-
import ZubHubAPI from '../../api';
import { colors } from '../../assets/js/colors';
-import { ClapBorderIcon } from '../../assets/js/icons/ClapIcon';
import styles from '../../assets/js/styles/index';
-import { Collapsible, CustomButton, Gallery, Modal, Pill } from '../../components';
+import { CustomButton, Gallery, Modal } from '../../components';
import Activity from '../../components/activity/activity';
import SocialButtons from '../../components/social_share_buttons/socialShareButtons';
import { getUrlQueryObject } from '../../utils.js';
-import { activityDefailsStyles } from './ActivityDetails.styles';
+import { dFormatter } from '../../assets/js/utils/scripts';
+import { activityDetailsStyles, socialButtonsStyleOverrides } from './ActivityDetails.styles';
+import Categories from '../../components/categories/Categories.jsx';
+import { USER_TAGS } from '../../assets/js/utils/constants';
const API = new ZubHubAPI(); // TODO: move api request to redux action
const authenticatedUserActivitiesGrid = { xs: 12, sm: 6, md: 6 };
const unauthenticatedUserActivitiesGrid = { xs: 12, sm: 6, md: 3 };
export default function ActivityDetailsV2(props) {
- const classes = makeStyles(activityDefailsStyles)();
+ const classes = makeStyles(activityDetailsStyles)();
const commonClasses = makeStyles(styles)();
const { t } = useTranslation();
@@ -84,7 +94,11 @@ export default function ActivityDetailsV2(props) {
};
const handleEdit = () => {
- props.navigate(`${props.location.pathname}/edit`);
+ props.history.push(`${props.location.pathname}/edit`);
+ };
+
+ const handlePublish = () => {
+ API.activityTogglePublish({ token: auth.token, id: activity.id }).then(() => window.location.reload());
};
const handleDownload = useReactToPrint({
@@ -112,174 +126,151 @@ export default function ActivityDetailsV2(props) {
});
return (
-
+
{open ?
: null}
-
-
- {activity?.title}
-
-
-
-
-
-
-
- {creator?.username}
-
-
-
- Educator
-
+
+
+
+
+
+
+
+ {creator?.username}
+
+
+ {creator?.tags.includes(USER_TAGS.educator) && (
+
+ {USER_TAGS.educator}
+
+ )}
+
-
-
-
-
-
-
- Create this Project
+
+
+ {t('activityDetails.activity.create.dialog.primary')}
+
+
+
+
+ {activity?.title}
+
+ {(activity.creators?.some(creator => creator.id === auth.id) || auth.tags.includes(USER_TAGS.staff)) && (
+
+ )}
+
+
+
+
+ {activity.views_count}
+
+
+
+
+ {t('activityDetails.inspired.recreated')}
+ {activity.views_count}
+ {t('activityDetails.inspired.times')}
+
+
+
+ {`
+ ${dFormatter(activity.created_on).value}
+ ${t(`date.${dFormatter(activity.created_on).key}`)}
+ ${t('date.ago')}
+ `}
+
+
+ {activity.images?.length > 0 &&
}
+
- {isDownloading ? 'Downloading...' : 'Download PDF'}
+
+ {isDownloading ? t('activityDetails.activity.pdf.downloading') : t('activityDetails.activity.pdf.download')}
+
-
- handleSetState(toggleLike(e, props, project.id))}
- >
- {/* {project.likes.includes(props.auth.id) ? (
-
- ) : ( */}
-
- {/* )} */}
-
- handleSetState(toggleSave(e, props, project.id))}
- >
- {/* {project.saved_by.includes(props.auth.id) ? (
-
- ) : ( */}
-
- {/* )} */}
-
-
-
-
-
-
-
-
-
-
- {activity.images?.length > 0 && img.image?.file_url)} />}
+
+
+ {t('activityDetails.activity.introduction')}
+
-
-
- {activity.category?.length > 0 && (
-
-
- {activity.category?.map((cat, index) => (
-
- ))}
-
-
- )}
-
- {activity.class_grade && (
-
-
-
- )}
-
- {activity.materials_used && (
-
- {activity.materials_used_image && }
+ {activity.images?.length > 0 && img.image?.file_url)} />}
+
+
+ {t('activityDetails.activity.categories')}
+
+ {activity.category?.length > 0 && }
+
+
+ {t('activityDetails.activity.classGrade')}
+
+ {activity.class_grade && }
+
+
+ {t('activityDetails.activity.materials')}
+
+ {activity.materials_used && (
-
- )}
-
- {activity.making_steps?.map((step, index) => (
-
- {step.image?.length > 0 && img?.file_url)} />}
- {step.description && (
+ )}
+ {activity.materials_used_image && }
+ {activity.making_steps?.map(step => (
+ <>
+
+
+ {`Step ${step?.step_order}: ${step.title}`}
+
- )}
-
- ))}
-
-
-
-
- Did you like this activity?
-
- Be the first to create it
-
- Create It!
-
-
-
-
- More Activities
+ {step.image?.length > 0 && img?.file_url)} />}
+ >
+ ))}
+
+
+
+
+ {t('activityDetails.footer.moreActivitiesTitle')}
-
-
- {moreActivities.map((activity, index) => (
+
+ {moreActivities.slice(0, 2).map((activity, index) => (
- handleSetState(updateProjects(res, state, props, toast))}
- {...props}
- />
+
))}
-
- {/* */}
} maxWidth="xs" open={open} onClose={toggleDialog}>
@@ -290,14 +281,12 @@ export default function ActivityDetailsV2(props) {
-
- Congratulations your Activity has been successfully created!
+
+ {t('activityDetails.activity.create.modal.success')}
-
- Share your activity with the world. Post it on the following platforms:
-
+ {t('activityDetails.activity.create.modal.share')}
@@ -307,8 +296,10 @@ export default function ActivityDetailsV2(props) {
);
}
-const AnchorElemt = ({ onEdit, onDelete, isLoading = false }) => {
+const AnchorElemt = ({ onEdit, onDelete, onPublish, isLoading = false, activity, auth }) => {
const [anchorEl, setAnchorEl] = useState(null);
+ const classes = makeStyles(activityDetailsStyles)();
+ const { t } = useTranslation();
const open = Boolean(anchorEl);
const handleClick = event => {
@@ -329,6 +320,37 @@ const AnchorElemt = ({ onEdit, onDelete, isLoading = false }) => {
handleClose();
};
+ const handlePublish = () => {
+ onPublish();
+ handleClose();
+ };
+
+ const ActionButtons = () => {
+ if (activity.creators?.some(creator => creator.id === auth.id)) {
+ return (
+
+
+
+
+ {t('activityDetails.activity.edit.label')}
+
+ );
+ } else if (auth.tags.includes(USER_TAGS.staff)) {
+ return (
+
+
+ {activity?.publish ? : }
+
+
+ {activity?.publish
+ ? t('activityDetails.activity.unpublish.label')
+ : t('activityDetails.activity.publish.label')}
+
+
+ );
+ }
+ };
+
return (
<>
{
'aria-labelledby': `basic-button`,
}}
>
- Edit
-
- Delete
+
+
+
+
+
+ {t('activityDetails.activity.delete.label')}
+
+
+ >
+ );
+};
+
+const ShareButton = () => {
+ const socialClasses = makeStyles(socialButtonsStyleOverrides)();
+ const [anchorEl, setAnchorEl] = useState(null);
+
+ const open = Boolean(anchorEl);
+ const handleClick = event => {
+ setAnchorEl(event.currentTarget);
+ };
+
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+
+ return (
+ <>
+
+
+
+
>
);
diff --git a/zubhub_frontend/zubhub/src/views/profile/Profile.jsx b/zubhub_frontend/zubhub/src/views/profile/Profile.jsx
index e4498a5f8..8bbe16c18 100644
--- a/zubhub_frontend/zubhub/src/views/profile/Profile.jsx
+++ b/zubhub_frontend/zubhub/src/views/profile/Profile.jsx
@@ -53,6 +53,7 @@ import styles from '../../assets/js/styles/views/profile/profileStyles';
import commonStyles from '../../assets/js/styles';
import ProjectsDraftsGrid from '../../components/projects_drafts/ProjectsDraftsGrid';
import UserActivitylog from '../../components/user_activitylog/UserActivitylog';
+import { USER_TAGS } from '../../assets/js/utils/constants.js';
const useStyles = makeStyles(styles);
const useCommonStyles = makeStyles(commonStyles);
@@ -129,7 +130,7 @@ function Profile(props) {
setNextPage(nextPageExist);
});
} catch (error) {
- console.log(error);
+ console.log(error); // eslint-disable-line no-console
}
}, [page]);
@@ -184,17 +185,28 @@ function Profile(props) {
{profile.username}
- {sortTags(profile.tags).map(tag => (
+ {props.location.state?.prevPath.split('/')[1] === 'activities' ? (
- {tag}
+ {USER_TAGS.educator}
- ))}
+ ) : (
+ sortTags(profile.tags).map(tag => (
+
+ {tag}
+
+ ))
+ )}
{props.auth.username === profile.username ? (
<>