diff --git a/AUTHORS b/AUTHORS index 39d4d2d7..00abdd9a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,12 +1,8 @@ Project maintainers: - Giuseppe Del Campo + Giuseppe Del Campo Marco Aceti - Manuele Lucchi - Contributors: - Giuseppe Roberti <> - Davide Busolin <> - Roberto Pinotti <> \ No newline at end of file + https://github.com/StudentiUniMi/website/graphs/contributors \ No newline at end of file diff --git a/components/Atoms/Contributors.tsx b/components/Atoms/Contributors.tsx deleted file mode 100644 index 54c31c12..00000000 --- a/components/Atoms/Contributors.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Text, Persona, TooltipHost, ITooltipHostStyles, TooltipDelay, useTheme } from '@fluentui/react'; -import Row from 'react-bootstrap/Row'; -import { Container } from 'react-bootstrap'; -import Contributor from '../../models/Contributor'; -import { getContributors } from '../../services/Requests' -import LocalizationService from "../../services/LocalizationService"; - -const calloutPropsContributor = { gapSpace: 3 }; -const hostStyles: Partial = { root: { display: 'inline-block' } }; - -const Contributors = () => { - var theme = useTheme(); - const locale = LocalizationService.strings(); - const contributors: Contributor[] = getContributors(); - - return ( -
- -
{locale?.contributors.header2}
- -
- - { - contributors.map((x, _) => - <> - - null} text={x.username} className="mb-1" /> - -   - - ) - } - -
-
-
- ) -} - -export default Contributors; \ No newline at end of file diff --git a/components/Atoms/ErrorMessage.tsx b/components/Atoms/ErrorMessage.tsx index 3fd1047d..29ec582d 100644 --- a/components/Atoms/ErrorMessage.tsx +++ b/components/Atoms/ErrorMessage.tsx @@ -6,10 +6,11 @@ import React from 'react'; import LocalizationService from "../../services/LocalizationService"; +import JsxParser from 'react-jsx-parser'; +import Image from 'next/image'; import { semibold } from '../../services/Fonts'; -import { Text, Image, Icon, Link, useTheme } from '@fluentui/react'; +import { Text, Icon, Link, useTheme } from '@fluentui/react'; import { Container } from 'react-bootstrap'; -import JsxParser from 'react-jsx-parser'; interface Props { error: boolean }; @@ -37,7 +38,14 @@ const ErrorMessage = (props: Props) => { {locale?.errorOccured}
- Not found + Not found
diff --git a/components/Atoms/GroupTypes.tsx b/components/Atoms/GroupTypes.tsx index ce556ea2..f4222495 100644 --- a/components/Atoms/GroupTypes.tsx +++ b/components/Atoms/GroupTypes.tsx @@ -1,7 +1,8 @@ -import { Text, Image, useTheme, mergeStyleSets } from '@fluentui/react'; +import { Text, useTheme, mergeStyleSets } from '@fluentui/react'; import { Container } from "react-bootstrap"; import { semibold } from "services/Fonts"; import { CSSProperties } from 'react'; +import Image from 'next/image'; import GroupType from "models/GroupType"; import GroupTypesData from '../../data/GroupTypes.json'; import LocalizationService from 'services/LocalizationService'; @@ -16,7 +17,7 @@ const GroupTypes = (props: Props) => { var theme = useTheme(); var language: string = LocalizationService.getLanguage() as string; - const groupTypes: Array = props.page === "courses" ? GroupTypesData.filter(g => g.name['it'] !== "Associazioni studentesche") : GroupTypesData; + const groupTypes: Array = props.page === "courses" ? GroupTypesData.filter(g => g.href !== '/courses') : GroupTypesData; const groupTypesStyle: CSSProperties = { justifyContent: 'center', @@ -50,11 +51,17 @@ const GroupTypes = (props: Props) => {
- {groupTypes.map((g:GroupType, i: number) => ( + {groupTypes.map((g: GroupType, i: number) => (
-
- {g.name[language]} +
+ {g.name[language]}
{g.name[language]}
diff --git a/components/Atoms/Message.tsx b/components/Atoms/Message.tsx index 8eeb196f..aeb4942b 100644 --- a/components/Atoms/Message.tsx +++ b/components/Atoms/Message.tsx @@ -4,9 +4,10 @@ * @author Giuseppe Del Campo */ -import { Text, Image, Icon, useTheme } from '@fluentui/react'; +import { Text, Icon, useTheme } from '@fluentui/react'; import { semibold } from '../../services/Fonts'; import { Container } from 'react-bootstrap'; +import Image from 'next/image'; interface Props { text: string }; @@ -17,7 +18,14 @@ const Message = (props: Props) => { const InfoMessage = () => (
- Not found + Not found
diff --git a/components/Atoms/PrivacyPolicyDialog.tsx b/components/Atoms/PrivacyPolicyDialog.tsx index b9de2e1b..69596423 100644 --- a/components/Atoms/PrivacyPolicyDialog.tsx +++ b/components/Atoms/PrivacyPolicyDialog.tsx @@ -1,6 +1,7 @@ -import { Text, useTheme, IIconProps, DialogType, Dialog, DialogFooter, PrimaryButton, Image, Checkbox, DefaultButton } from '@fluentui/react'; +import { Text, useTheme, IIconProps, DialogType, Dialog, DialogFooter, PrimaryButton, Checkbox, DefaultButton } from '@fluentui/react'; import { semibold } from '../../services/Fonts'; import { useContext, useState } from 'react'; +import Image from 'next/image'; import LocalizationService from 'services/LocalizationService'; import GlobalContext from 'services/GlobalContext'; import JsxParser from 'react-jsx-parser'; @@ -32,7 +33,13 @@ const PrivacyPolicyDialog = () => {
- Privacy policy + Privacy policy
diff --git a/components/Courses/AdminsList.tsx b/components/Courses/AdminsList.tsx index 693e86b7..a20f27bc 100644 --- a/components/Courses/AdminsList.tsx +++ b/components/Courses/AdminsList.tsx @@ -3,13 +3,14 @@ import { Persona, PersonaSize, Link, Text, useTheme } from '@fluentui/react'; import { Container } from 'react-bootstrap'; import { semibold } from '../../services/Fonts'; import { Icon } from '@fluentui/react'; -import React, { useEffect, useState } from "react"; +import React, { CSSProperties, useContext, useEffect, useState } from "react"; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import ErrorMessage from "../Atoms/ErrorMessage"; import Message from '../Atoms/Message'; import LocalizationService from "../../services/LocalizationService"; import Chip from 'components/Atoms/Chip'; +import GlobalContext from 'services/GlobalContext'; interface Props { admins: Admin[], errorLoadingAdmins: boolean }; @@ -20,6 +21,19 @@ const AdminsList = (props: Props) => { let admins: Admin[] = props.admins; let errorLoadingAdmins: boolean = props.errorLoadingAdmins; + const { isHeaderPinned } = useContext(GlobalContext); + + const subHeader: CSSProperties = { + backgroundColor: theme.palette.neutralLighter, + borderTop: `1px solid ${theme.palette.neutralQuaternary}`, + borderBottom: `1px solid ${theme.palette.neutralQuaternary}`, + padding: '10px 0px', + position: 'sticky', + top: isHeaderPinned ? 44 : 0, + transition: 'top 0.2s ease-in-out 0s', + zIndex: 2 + }; + const [domLoaded, setDomLoaded] = useState(false); const buildAdminsNumberString = (n: number) => { @@ -44,7 +58,7 @@ const AdminsList = (props: Props) => { return (
-
+
diff --git a/components/Courses/DegreeInformations.tsx b/components/Courses/DegreeInformations.tsx index ac46a1b1..2ffacc41 100644 --- a/components/Courses/DegreeInformations.tsx +++ b/components/Courses/DegreeInformations.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { CSSProperties, useContext } from "react"; import { semibold } from '../../services/Fonts'; import { Text, Icon, useTheme } from '@fluentui/react'; import { Container } from 'react-bootstrap'; @@ -7,6 +7,7 @@ import Message from '../Atoms/Message'; import ItemsGroup, { Item } from "components/Atoms/ItemsGroup"; import { DegreeInformation } from "models/DegreeInformation"; import Chip from "components/Atoms/Chip"; +import GlobalContext from "services/GlobalContext"; interface Props { degreeInformations: any[] }; @@ -15,6 +16,8 @@ const DegreeInformations= (props: Props) => { const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); + const { isHeaderPinned } = useContext(GlobalContext); + const degreeInformations: Array = props.degreeInformations; const items: Array = degreeInformations?.map((x) => ({ @@ -23,6 +26,17 @@ const DegreeInformations= (props: Props) => { iconName: x.icon })); + const subHeader: CSSProperties = { + backgroundColor: theme.palette.neutralLighter, + borderTop: `1px solid ${theme.palette.neutralQuaternary}`, + borderBottom: `1px solid ${theme.palette.neutralQuaternary}`, + padding: '10px 0px', + position: 'sticky', + top: isHeaderPinned ? 44 : 0, + transition: 'top 0.2s ease-in-out 0s', + zIndex: 2 + }; + const buildDegreeInformationsNumberString = (n: number) => { if (n === 0) { switch (language!) { @@ -43,7 +57,7 @@ const DegreeInformations= (props: Props) => { return (
-
+
diff --git a/components/Courses/GroupItem.tsx b/components/Courses/GroupItem.tsx index 6000c3e8..964b24da 100644 --- a/components/Courses/GroupItem.tsx +++ b/components/Courses/GroupItem.tsx @@ -1,4 +1,4 @@ -import { Text, Icon, TooltipHost } from '@fluentui/react'; +import { Text, Icon, TooltipHost, IContextualMenuItem } from '@fluentui/react'; import { Card, ICardTokens } from "@fluentui/react-cards"; import { FontWeights, ITextStyles, Link, Persona, useTheme } from '@fluentui/react'; import { semibold } from '../../services/Fonts'; @@ -26,7 +26,7 @@ const CourseItem = (props: Props) => { const theme = useTheme(); const locale = LocalizationService.strings(); const { isPolicyAccepted, togglePolicyDialog } = useContext(GlobalContext); - let data = props.data; + const data = props.data; const cfuStyle: ITextStyles = { root: { fontWeight: FontWeights.semibold, color: theme.palette.themeDark } }; const professorBox = { display: 'flex', alignItems: 'center', backgroundColor: theme.palette.neutralLighter, padding: "2px 6px", borderRadius: 3 }; @@ -47,6 +47,8 @@ const CourseItem = (props: Props) => { var semesterText: string | null = null; var mainText: JSX.Element | null = null; + const isInviteLinkEmpty = data.course.group?.invite_link === "" || data.course.group?.invite_link === null; + /* Groups data initialization */ if (data.course.group !== null) { /* Avatar image inizialization (personaUrl) */ @@ -62,7 +64,7 @@ const CourseItem = (props: Props) => { } /* Telegram Group initialization */ - if (data.course.group.invite_link !== "" && data.course.group.invite_link !== null) { + if (!isInviteLinkEmpty) { telegramLink = ( preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} @@ -102,14 +104,9 @@ const CourseItem = (props: Props) => { /* CFU inizialization */ switch (data.course.cfu) { case 0: - cfuText = null; - break; case null: cfuText = null; break; - case undefined: - cfuText = <>N/A CFU; - break; default: cfuText = <>{data.course.cfu} CFU; break; @@ -129,48 +126,50 @@ const CourseItem = (props: Props) => { /* Year inizialization */ switch (data?.year) { - case 0: /* Insegnamento di un corso di laurea senza anno */ + case 0: // Insegnamento di un corso di laurea senza anno + case -1: // Gruppo principale yearText = null; break; - case -1: /* Gruppo principale */ - yearText = null; - break; - case -2: /* Complementare */ + case -2: // Complementare yearText = `${locale?.courses.complementary}`; break; - case undefined: /* Errore o non disponibile */ - yearText = "N/A"; - break; default: yearText = `${data.year}° ${locale?.courses.year}`; break; - } + }; /* Semester inizialization */ if (data.semester === -1 || data.semester === null || data.semester === 0) { semesterText = null; - } else if (data.semester === undefined) { - semesterText = 'N/A'; } else if (data.semester !== null) { semesterText = `${data.semester}° ${locale?.courses.semester}`; } /* Websites inizialization */ - let websites: any[] | undefined = []; + let websites: Array = []; if ((data.course.links ?? []).length !== 0) { - websites = data.course.links.map( - (e, i) => { - return { - key: i, - text: e.name, - onClick: () => redirectToLink(e.url), - iconProps: { iconName: 'ChromeBackMirrored' } - }; - } + websites = data.course.links.map((website, index: number) => { + return { + key: index.toString(), + text: website.name, + onClick: () => redirectToLink(website.url), + iconProps: { iconName: 'ChromeBackMirrored' } + }; + } ); } + const menuProps: IContextualMenuProps = { + onDismiss: ev => { + if (ev && 'shiftKey' in ev) { + ev.preventDefault(); + } + }, + items: websites, + directionalHintFixed: true, + }; + /* Wiki initialization */ if (data.year !== -1) { wikiLink = ( @@ -185,17 +184,6 @@ const CourseItem = (props: Props) => { ); } - const menuProps: IContextualMenuProps = { - // For example: disable dismiss if shift key is held down while dismissing - onDismiss: ev => { - if (ev && 'shiftKey' in ev) { - ev.preventDefault(); - } - }, - items: websites as any[], - directionalHintFixed: true, - }; - return ( @@ -203,15 +191,15 @@ const CourseItem = (props: Props) => { - - {data.year !== -1 && + + {data.year !== -1 &&
- { professor !== null && + {professor !== null &&
{professor} -
} +
} {cfuText} @@ -220,9 +208,9 @@ const CourseItem = (props: Props) => { } - {data.year === -1 && } - {yearText !== null && } - {semesterText !== null && } + {data.year === -1 && } + {yearText !== null && } + {semesterText !== null && } @@ -231,7 +219,7 @@ const CourseItem = (props: Props) => { {telegramLink} - { data.year !== -1 && + {data.year !== -1 && { ); }; -export default CourseItem; \ No newline at end of file +export default CourseItem; diff --git a/components/Courses/GroupList.tsx b/components/Courses/GroupList.tsx index c5bb039c..15d3d53a 100644 --- a/components/Courses/GroupList.tsx +++ b/components/Courses/GroupList.tsx @@ -1,8 +1,9 @@ -import React, { FormEvent, MouseEvent, useCallback, useEffect, useRef, useState } from "react"; +import React, { CSSProperties, FormEvent, MouseEvent, useCallback, useContext, useEffect, useRef, useState } from "react"; import { Text, Toggle, Icon, IRectangle, List, TextField, Dropdown, IDropdownOption, mergeStyleSets, useTheme } from "@fluentui/react"; import { Container } from 'react-bootstrap'; import { semibold } from '../../services/Fonts'; import { Degree, CourseDegree } from '../../models/Models'; +import * as Scroll from 'react-scroll'; import ErrorMessage from "../Atoms/ErrorMessage"; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; @@ -10,6 +11,7 @@ import GroupItem from './GroupItem'; import LocalizationService from "../../services/LocalizationService"; import Message from '../Atoms/Message'; import Chip from "components/Atoms/Chip"; +import GlobalContext from "services/GlobalContext"; interface Props { degree?: Degree, @@ -42,6 +44,8 @@ const CourseList= (props: Props) => { var theme = useTheme(); const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); + + const { isHeaderPinned } = useContext(GlobalContext); const [filtersToggle, setFiltersToggle] = useState(false); @@ -49,6 +53,17 @@ const CourseList= (props: Props) => { const rowHeight = useRef(0); const rowsPerPage = useRef(0); const MAX_ROW_HEIGHT = 265; + + const subHeader: CSSProperties = { + backgroundColor: theme.palette.neutralLighter, + borderTop: `1px solid ${theme.palette.neutralQuaternary}`, + borderBottom: `1px solid ${theme.palette.neutralQuaternary}`, + padding: '10px 0px', + position: 'sticky', + top: isHeaderPinned ? 44 : 0, + transition: 'top 0.2s ease-in-out 0s', + zIndex: 2 + }; var classNames = mergeStyleSets({ listGrid: { @@ -59,6 +74,18 @@ const CourseList= (props: Props) => { margin: '1px' } }); + + var Element = Scroll.Element; + var scroller = Scroll.scroller; + + const scrollToGroupsFilters = () => { + scroller.scrollTo('groups-filters', { + duration: 500, + delay: 100, + smooth: true, + offset: -100 + }); + }; const getItemCountForPage = useCallback((itemIndex?: number, surfaceRect?: IRectangle) => { if (itemIndex === 0) { @@ -105,7 +132,7 @@ const CourseList= (props: Props) => { if (nameFilter !== "") { filteredCourses = filteredCourses.filter(x => x.course.name.toLocaleLowerCase().includes(nameFilter.toLocaleLowerCase())); } if (semesterFilter !== 0) { filteredCourses = filteredCourses.filter(x => x.semester === semesterFilter); } if (yearFilter !== 0) { filteredCourses = filteredCourses.filter(x => x.year === yearFilter); } - + const toggleGroupsFilters = (_ev: MouseEvent, checked?: boolean) => { setFiltersToggle(checked!); resetGroupsFilters(); @@ -121,6 +148,10 @@ const CourseList= (props: Props) => { resetGroupsFilters(); }, [props.degree]); + useEffect(() => { + if (filtersToggle) scrollToGroupsFilters(); + }, [filtersToggle]); + const buildGroupsNumberString = (n: number) => { if (n === 0) { switch (language!) { @@ -141,7 +172,7 @@ const CourseList= (props: Props) => { return (
-
+
@@ -180,39 +211,42 @@ const CourseList= (props: Props) => {
- { filtersToggle ? -
- - - - - - { + { filtersToggle ? + /* @ts-ignore */ + +
+ + + + + + { + + } + + - } - - - - - -
: <> + +
+
+ : <> } { diff --git a/components/Groups/AnnouncementsGroup.tsx b/components/Groups/AnnouncementsGroup.tsx index 76915a4c..b4ad51aa 100644 --- a/components/Groups/AnnouncementsGroup.tsx +++ b/components/Groups/AnnouncementsGroup.tsx @@ -6,22 +6,24 @@ import { Text, Icon, FontWeights, ITextStyles, Persona, Link, TooltipHost, IIconProps, useTheme, PrimaryButton } from '@fluentui/react'; import { Card, ICardTokens } from "@fluentui/react-cards"; -import { preventDefault, preventVisibleHref } from 'services/Utils'; +import { formatLowerNumber, preventDefault, preventVisibleHref } from 'services/Utils'; import { semibold } from '../../services/Fonts'; import { useContext } from 'react'; -import Group from '../../models/Group' +import { ExtraGroup } from 'models/Models'; import Chip from '../Atoms/Chip'; import LocalizationService from "../../services/LocalizationService"; import JsxParser from 'react-jsx-parser'; import GlobalContext from 'services/GlobalContext'; -interface Props { data: Group }; +interface Props { data: ExtraGroup }; const AnnouncementsGroup = (props: Props) => { const theme = useTheme(); const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); - const data = props.data; + + const group = props.data; + const { isPolicyAccepted, togglePolicyDialog } = useContext(GlobalContext); const helpfulTextStyles: ITextStyles = { root: { fontWeight: FontWeights.regular, } }; @@ -30,8 +32,11 @@ const AnnouncementsGroup = (props: Props) => { const telegramGroupIcon: IIconProps = { iconName: 'Send' }; const calloutProps = { gapSpace: 5 }; - let desc = data.description![language!]; - let name = data.name![language!]; + const name = group.name[language!]; + const description = group.description[language!]; + const imageUrl = `https://studentiunimi-groups-propics.marcoaceti.workers.dev/${group.id}.png`; + + const isInviteLinkEmpty = group.invite_link === "" || group.invite_link === null; /* PrimaryText inizialization */ let primaryText: JSX.Element = ( @@ -45,10 +50,6 @@ const AnnouncementsGroup = (props: Props) => { ); - // Group image initialization - var imageUrl: string; - imageUrl = '/images/university_groups/' + data.image; - return ( @@ -63,8 +64,8 @@ const AnnouncementsGroup = (props: Props) => { bgColor={theme.palette.neutralLight} className="m-1" /> - {data.users && { /> } - " + desc} /> + " + description} /> - { - (() => { - if (data.href !== "" && data.href !== null) { - return ( - preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} - iconProps={telegramGroupIcon} - style={{ justifyContent: 'center', marginLeft: 'auto', marginRight: 'auto', marginTop: '3px' }} - disabled={data.href === "" || data.href === null} - allowDisabledFocus> - {locale?.telegramGroup} - - ); - } - })() + {!isInviteLinkEmpty && + preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} + iconProps={telegramGroupIcon} + style={{ justifyContent: 'center', marginLeft: 'auto', marginRight: 'auto', marginTop: '3px' }} + disabled={isInviteLinkEmpty} + allowDisabledFocus> + {locale?.telegramGroup} + } - ); diff --git a/components/Groups/Groups.tsx b/components/Groups/Groups.tsx index 99850ae8..bad57031 100644 --- a/components/Groups/Groups.tsx +++ b/components/Groups/Groups.tsx @@ -1,10 +1,11 @@ -import React from 'react'; +import { useRef, useCallback } from 'react'; import { List, IRectangle, mergeStyleSets } from "@fluentui/react"; -import { getGroups, getGroupsAnnouncements, getStudentsAssociations } from '../../services/Requests'; +import { ExtraGroup } from 'models/Models'; import UniversityGroup from './UniversityGroup'; -import Group from '../../models/Group'; import AnnouncementsGroup from './AnnouncementsGroup'; import StudentsAssociation from './StudentsAssociation'; +import Message from 'components/Atoms/Message'; +import LocalizationService from 'services/LocalizationService'; export enum GroupsType { ANNOUNCEMENTS = "ANNOUNCEMENTS", @@ -13,27 +14,18 @@ export enum GroupsType { }; interface Props { + groups: Array, groupsType: GroupsType }; const Groups = (props: Props) => { - const columnCount = React.useRef(0); - const rowHeight = React.useRef(0); - const rowsPerPage = React.useRef(0); + const locale = LocalizationService.strings(); + const columnCount = useRef(0); + const rowHeight = useRef(0); + const rowsPerPage = useRef(0); const MAX_ROW_HEIGHT = 280; - const groups: Group[] = (() => { - switch(props.groupsType) { - case GroupsType.UNIVERSITY: - return getGroups(); - case GroupsType.ANNOUNCEMENTS: - return getGroupsAnnouncements(); - case GroupsType.ASSOCIATION: - return getStudentsAssociations(); - default: - return []; - } - })(); + const groups: Array = props.groups; var classNames = mergeStyleSets({ listGrid: { @@ -45,9 +37,8 @@ const Groups = (props: Props) => { } }); - const getItemCountForPage = React.useCallback((itemIndex?: number, surfaceRect?: IRectangle) => { + const getItemCountForPage = useCallback((itemIndex?: number, surfaceRect?: IRectangle) => { if (itemIndex === 0) { - /* rowHeight.current = Math.floor(surfaceRect!.width / columnCount.current) */ columnCount.current = Math.ceil(surfaceRect!.width / MAX_ROW_HEIGHT); rowHeight.current = MAX_ROW_HEIGHT; rowsPerPage.current = surfaceRect!.height / MAX_ROW_HEIGHT; @@ -55,11 +46,11 @@ const Groups = (props: Props) => { return columnCount.current * rowsPerPage.current; }, []); - const getPageHeight = React.useCallback((): number => { + const getPageHeight = useCallback((): number => { return rowHeight.current * rowsPerPage.current; }, []); - const getCell = (e?: Group, _index?: number, _isScrolling?: boolean) => { + const getCell = (e?: ExtraGroup, _index?: number, _isScrolling?: boolean) => { return (
{( () => { @@ -76,20 +67,30 @@ const Groups = (props: Props) => { })()}
) - } + }; return ( -
- -
- ) + <> + { + groups?.length === 0 + ? +
+ +
+ : +
+ +
+ } + + ); }; export default Groups; \ No newline at end of file diff --git a/components/Groups/StudentsAssociation.tsx b/components/Groups/StudentsAssociation.tsx index c1079125..8a6eba1a 100644 --- a/components/Groups/StudentsAssociation.tsx +++ b/components/Groups/StudentsAssociation.tsx @@ -1,5 +1,6 @@ /** * Students association component. + * This uses nullable properties in ExtraGroup model such as button_name, external_url and image_url. * * @author Giuseppe Del Campo */ @@ -9,19 +10,21 @@ import { Card, ICardTokens } from "@fluentui/react-cards"; import { preventDefault, preventVisibleHref } from 'services/Utils'; import { semibold } from '../../services/Fonts'; import { useContext } from 'react'; -import Group from '../../models/Group' +import { ExtraGroup } from 'models/Models'; import Chip from '../Atoms/Chip'; import LocalizationService from "../../services/LocalizationService"; import JsxParser from 'react-jsx-parser'; import GlobalContext from 'services/GlobalContext'; -interface Props { data: Group }; +interface Props { data: ExtraGroup }; const StudentsAssociation = (props: Props) => { const theme = useTheme(); const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); - const data = props.data; + + const studentsAssociation = props.data; + const { isPolicyAccepted, togglePolicyDialog } = useContext(GlobalContext); const helpfulTextStyles: ITextStyles = { root: { fontWeight: FontWeights.regular, } }; @@ -30,8 +33,10 @@ const StudentsAssociation = (props: Props) => { const telegramGroupIcon: IIconProps = { iconName: 'Send' }; const calloutProps = { gapSpace: 5 }; - let desc = data.description![language!]; - let name = data.name![language!]; + const name = studentsAssociation.name[language!]; + const description = studentsAssociation.description[language!]; + + const isInviteLinkEmpty = studentsAssociation.external_url === "" || studentsAssociation.external_url === null; /* PrimaryText inizialization */ let primaryText: JSX.Element = ( @@ -45,14 +50,10 @@ const StudentsAssociation = (props: Props) => { ); - // Group image initialization - var imageUrl: string; - imageUrl = '/images/university_groups/' + data.image; - return ( - primaryText} text={name} /> + primaryText} text={name} /> @@ -64,27 +65,21 @@ const StudentsAssociation = (props: Props) => { className="m-1" /> + - " + desc} /> + " + description} /> - { - (() => { - if (data.href !== "" && data.href !== null) { - return ( - preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} - iconProps={telegramGroupIcon} - style={{ justifyContent: 'center', marginLeft: 'auto', marginRight: 'auto', marginTop: '3px' }} - disabled={data.href === "" || data.href === null} - allowDisabledFocus> - {locale?.homepage.section3.part3.title} - - ); - } - })() + {!isInviteLinkEmpty && + preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} + iconProps={telegramGroupIcon} + style={{ justifyContent: 'center', marginLeft: 'auto', marginRight: 'auto', marginTop: '3px' }} + disabled={isInviteLinkEmpty} + allowDisabledFocus> + {studentsAssociation.button_name![language!]} + } - ); diff --git a/components/Groups/UniversityGroup.tsx b/components/Groups/UniversityGroup.tsx index 5bfe87b7..a3e01f39 100644 --- a/components/Groups/UniversityGroup.tsx +++ b/components/Groups/UniversityGroup.tsx @@ -6,22 +6,24 @@ import { Text, Icon, FontWeights, ITextStyles, Persona, Link, TooltipHost, IIconProps, useTheme, PrimaryButton } from '@fluentui/react'; import { Card, ICardTokens } from "@fluentui/react-cards"; -import { preventDefault, preventVisibleHref } from 'services/Utils'; +import { formatLowerNumber, preventDefault, preventVisibleHref } from 'services/Utils'; import { semibold } from '../../services/Fonts'; import { useContext } from 'react'; -import Group from '../../models/Group' +import { ExtraGroup } from 'models/Models'; import Chip from '../Atoms/Chip'; import LocalizationService from "../../services/LocalizationService"; import JsxParser from 'react-jsx-parser'; import GlobalContext from 'services/GlobalContext'; -interface Props { data: Group }; +interface Props { data: ExtraGroup }; const AdditionalGroup = (props: Props) => { const theme = useTheme(); const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); - const data = props.data; + + const group = props.data; + const { isPolicyAccepted, togglePolicyDialog } = useContext(GlobalContext); const helpfulTextStyles: ITextStyles = { root: { fontWeight: FontWeights.regular, } }; @@ -30,8 +32,11 @@ const AdditionalGroup = (props: Props) => { const telegramGroupIcon: IIconProps = { iconName: 'Send' }; const calloutProps = { gapSpace: 5 }; - let desc = data.description![language!]; - let name = data.name![language!]; + const name = group.name[language!]; + const description = group.description[language!]; + const imageUrl = `https://studentiunimi-groups-propics.marcoaceti.workers.dev/${group.id}.png`; + + const isInviteLinkEmpty = group.invite_link === "" || group.invite_link === null; /* PrimaryText inizialization */ let primaryText: JSX.Element = ( @@ -45,10 +50,6 @@ const AdditionalGroup = (props: Props) => { ); - // Group image initialization - var imageUrl: string; - imageUrl = '/images/university_groups/' + data.image; - return ( @@ -63,26 +64,29 @@ const AdditionalGroup = (props: Props) => { bgColor={theme.palette.neutralLight} className="m-1" /> + {group.user_count && } - " + desc} /> + " + description} /> - { - (() => { - if (data.href !== "" && data.href !== null) { - return ( - preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} - iconProps={telegramGroupIcon} - style={{ justifyContent: 'center', marginLeft: 'auto', marginRight: 'auto', marginTop: '3px' }} - disabled={data.href === "" || data.href === null} - allowDisabledFocus> - {locale?.telegramGroup} - - ); - } - })() + {!isInviteLinkEmpty && + preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} + iconProps={telegramGroupIcon} + style={{ justifyContent: 'center', marginLeft: 'auto', marginRight: 'auto', marginTop: '3px' }} + disabled={isInviteLinkEmpty} + allowDisabledFocus> + {locale?.telegramGroup} + } diff --git a/components/Header/Header.tsx b/components/Header/Header.tsx index 348ed77b..add4e6e2 100644 --- a/components/Header/Header.tsx +++ b/components/Header/Header.tsx @@ -1,29 +1,27 @@ import { FontSizes, Text, - Image, FontWeights, ITextStyles, Link, useTheme } from '@fluentui/react'; +import Image from 'next/image'; +import { CSSProperties, useContext } from 'react'; import HeaderMenu from './HeaderMenu'; import Headroom from 'react-headroom'; +import GlobalContext from 'services/GlobalContext'; const Header = () => { var theme = useTheme(); - const headerZIndex = 2; + const { setIsHeaderPinned } = useContext(GlobalContext); - const logoProperties = { - width: 25, - height: 25, - marginTop: 3 - }; + const headerZIndex = 2; const logoFileName = 'unimi150.png'; - const headerStyle = { + const headerStyle: CSSProperties = { zIndex: headerZIndex, backgroundColor: theme.palette.white, borderBottom: '1px solid', @@ -39,22 +37,27 @@ const Header = () => { } }; + const onHeaderPin = () => setIsHeaderPinned(true); + const onHeaderUnpin = () => setIsHeaderPinned(false); + return ( <> {/* @ts-ignore */} - +
- + diff --git a/components/Home/Faqs.tsx b/components/Home/Faqs.tsx index 40f73d33..84036cb4 100644 --- a/components/Home/Faqs.tsx +++ b/components/Home/Faqs.tsx @@ -3,7 +3,7 @@ import JsxParser from 'react-jsx-parser'; import Chip from '../Atoms/Chip'; import { Container } from 'react-bootstrap'; import { Link, Text, useTheme, mergeStyleSets } from '@fluentui/react'; -import { semibold } from '../../services/Fonts'; +import { bold, semibold } from '../../services/Fonts'; import { getFaqs } from '../../services/Requests'; import { Accordion, @@ -65,7 +65,7 @@ const Faqs = () => {
- {locale?.homepage.faqsSection.header} + {locale?.homepage.faqsSection.header}
diff --git a/components/Home/Landing.tsx b/components/Home/Landing.tsx index 502ad42a..91448f2c 100644 --- a/components/Home/Landing.tsx +++ b/components/Home/Landing.tsx @@ -1,11 +1,12 @@ -import { Text, Image } from '@fluentui/react'; +import { Text } from '@fluentui/react'; import { Container } from 'react-bootstrap'; +import { bold } from 'services/Fonts'; import DegreesMarquee from './DegreesMarquee'; import React from 'react'; import LocalizationService from "../../services/LocalizationService"; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; -import { bold } from 'services/Fonts'; +import Image from 'next/image'; interface Props { degrees: string[] @@ -13,7 +14,6 @@ interface Props { const Landing = (props: Props) => { const locale = LocalizationService.strings(); - const logoProperties = { width: 200, height: 200, display: 'inline-block' }; let stringDegrees: string[] = props.degrees; return ( @@ -22,7 +22,14 @@ const Landing = (props: Props) => {
- +
diff --git a/components/Home/Section1.tsx b/components/Home/Section1.tsx index c663422e..fe4e0cf7 100644 --- a/components/Home/Section1.tsx +++ b/components/Home/Section1.tsx @@ -1,4 +1,4 @@ -import { Text, IIconProps, PrimaryButton, Image, useTheme, Link } from '@fluentui/react'; +import { Text, IIconProps, PrimaryButton, useTheme, Link } from '@fluentui/react'; import { bold, semibold } from '../../services/Fonts'; import { Container } from 'react-bootstrap'; import Col from 'react-bootstrap/Col'; @@ -6,20 +6,36 @@ import Row from 'react-bootstrap/Row'; import LocalizationService from "../../services/LocalizationService"; import Chip from '../Atoms/Chip'; import JsxParser from 'react-jsx-parser'; +import Image from 'next/image'; +import { CSSProperties } from 'react'; const FirstSection = () => { var theme = useTheme(); const locale = LocalizationService.strings(); - const buttonStyle = { maxWidth: '120px', boxShadow: theme.effects.elevation8 }; - const buttonIconProps: IIconProps = { iconName: 'GoChevronRight', styles: { root: { fontSize: 14 } } }; - const cardStyle = { backgroundColor: theme.palette.neutralLighterAlt, padding: '20px', borderRadius: '10px', overflow: 'hidden', minHeight: 200 }; - const cardImageStyle = { width: 165, height: 165 }; + + const buttonStyle = { + maxWidth: '120px', + boxShadow: theme.effects.elevation8 + }; + + const buttonIconProps: IIconProps = { + iconName: 'GoChevronRight', + styles: { root: { fontSize: 14 } } + }; + + const cardStyle: CSSProperties = { + backgroundColor: theme.palette.neutralLighterAlt, + padding: '20px', + borderRadius: '10px', + overflow: 'hidden', + minHeight: 200 + }; return (
-
{locale?.homepage.section2.title}
+
{locale?.homepage.section2.title}
@@ -35,9 +51,15 @@ const FirstSection = () => {
- Degree groups + Degree groups -
+
{
- General groups + General groups -
+
{
- Telematic services + Telematic services -
+
{ - Network main redirects + Network main redirects
- {locale?.homepage.section3.header} + {locale?.homepage.section3.header}
diff --git a/components/Home/Section3.tsx b/components/Home/Section3.tsx index 69af8dfd..b5b3343f 100644 --- a/components/Home/Section3.tsx +++ b/components/Home/Section3.tsx @@ -1,6 +1,6 @@ import { Text, IIconProps, Icon, DefaultButton, useTheme } from '@fluentui/react'; import { useContext } from 'react'; -import { semibold } from '../../services/Fonts'; +import { bold, semibold } from '../../services/Fonts'; import { Container } from 'react-bootstrap'; import { preventDefault, preventVisibleHref } from 'services/Utils'; import Col from 'react-bootstrap/Col'; @@ -20,7 +20,7 @@ const ThirdSection = () => { return (
-
{locale?.homepage.additionalServicesSection.header}
+
{locale?.homepage.additionalServicesSection.header}
diff --git a/components/Home/Section4.tsx b/components/Home/Section4.tsx index 2e6c3a90..98b9ca04 100644 --- a/components/Home/Section4.tsx +++ b/components/Home/Section4.tsx @@ -1,6 +1,7 @@ -import { Text, IIconProps, PrimaryButton, Image, useTheme } from '@fluentui/react'; -import { semibold } from '../../services/Fonts'; +import { Text, IIconProps, PrimaryButton, useTheme } from '@fluentui/react'; +import { bold, semibold } from '../../services/Fonts'; import { Container } from 'react-bootstrap'; +import Image from 'next/image'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import LocalizationService from "../../services/LocalizationService"; @@ -9,18 +10,27 @@ const FourthSection = () => { var theme = useTheme(); const locale = LocalizationService.strings(); const buttonStyle = { maxWidth: '200px', boxShadow: theme.effects.elevation8 }; - const buttonIconProps: IIconProps = { iconName: 'GoChevronRight', styles: { root: { fontSize: 14 } } }; + const buttonIconProps: IIconProps = { + iconName: 'GoChevronRight', + styles: { root: { fontSize: 14 } } + }; return (
-
{locale?.homepage.adminsRepresentativesSection.header}
+
{locale?.homepage.adminsRepresentativesSection.header}
- Representatives -
+ Representatives +
{locale?.homepage.adminsRepresentativesSection.col1.title}
@@ -36,8 +46,14 @@ const FourthSection = () => { - Administrators -
+ Administrators +
{locale?.homepage.adminsRepresentativesSection.col2.title}
diff --git a/components/Home/UnimiaStudentiUnimi.tsx b/components/Home/UnimiaStudentiUnimi.tsx index 3ca46041..28a4167f 100644 --- a/components/Home/UnimiaStudentiUnimi.tsx +++ b/components/Home/UnimiaStudentiUnimi.tsx @@ -1,6 +1,7 @@ -import { Text, IIconProps, PrimaryButton, Image, useTheme } from '@fluentui/react'; -import { semibold } from '../../services/Fonts'; +import { Text, IIconProps, PrimaryButton, useTheme } from '@fluentui/react'; +import { bold, semibold } from '../../services/Fonts'; import { Container } from 'react-bootstrap'; +import Image from 'next/image'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import LocalizationService from "../../services/LocalizationService"; @@ -17,10 +18,10 @@ const UnimiaStudentiUnimi = () => { -
{locale?.homepage.unimiaSection.text1}
+
{locale?.homepage.unimiaSection.text1}
- {locale?.homepage.unimiaSection.text2} + {locale?.homepage.unimiaSection.text2}
@@ -38,7 +39,13 @@ const UnimiaStudentiUnimi = () => { - Unimia redirects + Unimia redirects diff --git a/components/Home/Wikipedia.tsx b/components/Home/Wikipedia.tsx index 35abbf5c..b4414aba 100644 --- a/components/Home/Wikipedia.tsx +++ b/components/Home/Wikipedia.tsx @@ -1,8 +1,9 @@ -import { Text, IIconProps, PrimaryButton, Image, useTheme } from '@fluentui/react'; +import { Text, IIconProps, PrimaryButton, useTheme } from '@fluentui/react'; import { useContext } from 'react'; -import { semibold } from '../../services/Fonts'; +import { bold, semibold } from '../../services/Fonts'; import { Container } from 'react-bootstrap'; import { preventDefault, preventVisibleHref } from 'services/Utils'; +import Image from 'next/image'; import GlobalContext from 'services/GlobalContext'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; @@ -22,14 +23,20 @@ const Wiki = () => { - StudentiUniMi Wikipedia + StudentiUniMi Wikipedia -
{locale?.homepage.wikipediaSection.text1}
+
{locale?.homepage.wikipediaSection.text1}
- {locale?.homepage.wikipediaSection.text2} + {locale?.homepage.wikipediaSection.text2}
diff --git a/components/University/RepresentativesList.tsx b/components/University/RepresentativesList.tsx index ba4c93e2..f4a3b07d 100644 --- a/components/University/RepresentativesList.tsx +++ b/components/University/RepresentativesList.tsx @@ -13,7 +13,7 @@ const RapresentativesList = (props: Props) => { const locale = LocalizationService.strings(); return ( -
+
{ props.loadingRepresentatives || props.errorLoadingRepresentatives ? : props.data.length === 0 ? diff --git a/data/Contributors.json b/data/Contributors.json deleted file mode 100644 index cac3d7be..00000000 --- a/data/Contributors.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { "user_id": 0, "username": "robertopinotti", "description": "" }, - { "user_id": 0, "username": "roberti42", "description": "" }, - { "user_id": 0, "username": "Spacer_ASC", "description": "" }, - { "user_id": 0, "username": "B3rsa", "description": "" }, - { "user_id": 0, "username": "PiroBert", "description": "" }, - { "user_id": 0, "username": "mark99_19", "description": "" }, - { "user_id": 0, "username": "aleclericil", "description": "" } -] \ No newline at end of file diff --git a/data/GroupTypes.json b/data/GroupTypes.json index 5caa1622..eddea9c4 100644 --- a/data/GroupTypes.json +++ b/data/GroupTypes.json @@ -1,4 +1,4 @@ -[ + [ { "name": { "it": "Gruppi dei corsi di laurea", diff --git a/data/groups/Announcements.json b/data/groups/Announcements.json deleted file mode 100644 index b39b6be3..00000000 --- a/data/groups/Announcements.json +++ /dev/null @@ -1,54 +0,0 @@ -[ - { - "name": { - "it": "Compravendita libri e appunti", - "en": "Buying and selling books and notes" - }, - "description": { - "it": "Dedicato a chi cerca o offre libri e appunti per i vari corsi didattici.", - "en": "Dedicated to those looking for or offering books and notes for the various didactic courses." - }, - "image": "materiali.png", - "href": "https://t.me/+u_IBzIREMZI4NThk", - "users": "1000+" - }, - { - "name": { - "it": "Ripetizioni", - "en": "Private lessons" - }, - "description": { - "it": "Dedicato a chi cerca o offre ripetizioni. Le discussioni vanno fatte in privato, qui ci vanno soltanto annunci.", - "en": "Dedicated to those looking for or offering repetitions. Discussions must be done in private, only announcements go here." - }, - "image": "ripetizioni.png", - "href": "https://t.me/joinchat/a_aLt47Z8lAyMjBk", - "users": "500+" - }, - { - "name": { - "it": "Alloggi", - "en": "Apartments" - }, - "description": { - "it": "Dedicato a chi cerca alloggi o vuole creare un annuncio a riguardo.", - "en": "Dedicated to ads about residence, house or room finding." - }, - "image": "alloggi.jpg", - "href": "https://t.me/joinchat/xJP5VPIBboxiNjI0", - "users": "700+" - }, - { - "name": { - "it": "Annunci di lavoro", - "en": "Job Ads" - }, - "description": { - "it": "Gruppo che permette di proporre lavoro rivolto a studenti.", - "en": "Group to offer job (only for students)." - }, - "image": "lavoro.png", - "href": "https://t.me/joinchat/If3OTD06OCMwMWU0", - "users": "800+" - } -] \ No newline at end of file diff --git a/data/groups/Groups.json b/data/groups/Groups.json deleted file mode 100644 index a437d401..00000000 --- a/data/groups/Groups.json +++ /dev/null @@ -1,146 +0,0 @@ -[ - { - "name": { - "it": "Chat generale", - "en": "Main chat" - }, - "description": { - "it": "Dedicato a discussioni e chiarimenti inerenti all'Università degli Studi di Milano.", - "en": "Dedicated to discussions and any clarifications regarding University of Milan." - }, - "image": "studentiunimi.png", - "href": "https://t.me/unimichat" - }, - { - "name": { - "it": "Pre-matricole, ammissioni e immatricolazioni", - "en": "Admissions and enrollment" - }, - "description": { - "it": "Dedicato agli studenti che vogliono immatricolarsi in UniMi e cercano informazioni generali a riguardo.", - "en": "Dedicated to students who want to enroll in UniMi and are looking for general information about it." - }, - "image": "matricole.jpg", - "href": "https://t.me/joinchat/XLY5KwP1a1Q1MzZk" - }, - { - "name": { - "it": "Tirocini e tesi", - "en": "Traineeships, internships and thesis" - }, - "description": { - "it": "Gruppo per eventuali domande, chiarimenti e discussioni riguardo tirocini e tesi.", - "en": "Group dedicated to discussions about traineeships, internships and thesis." - }, - "image": "tirocini.jpg", - "href": "https://t.me/joinchat/iYmrpYmRk-FkNjI8" - }, - { - "name": { - "it": "Sondaggi, progetti e iniziative universitarie", - "en": "University surveys, projects and initiatives" - }, - "description": { - "it": "Gruppo per mandare sondaggi, progetti o iniziative che riguardano il mondo universitario.", - "en": "Group for sending surveys, projects or initiatives concerning the university world." - }, - "image": "sondaggi.png", - "href": "https://t.me/+-vPGOKTElAIyNzlk" - }, - { - "name": { - "it": "Studio all'estero", - "en": "Study abroad " - }, - "description": { - "it": "Gruppo dedicato a discussioni sui programmi di studio all'estero.", - "en": "Group dedicated to discussions about study abroad." - }, - "image": "erasmus.png", - "href": "https://t.me/joinchat/3Xp8K8xWnQ9hMDBk" - }, - { - "name": { - "it": "Studenti internazionali", - "en": "International students" - }, - "description": { - "it": "Gruppo dedicato agli studenti internazionali che hanno intenzione di immatricolarsi in UniMi; chat solo in inglese.", - "en": "Group aimed to International students coming to UniMi; chat only in english." - }, - "image": "international_students.png", - "href": "https://t.me/joinchat/fO8pB1iSBwphYzA0" - }, - { - "name": { - "it": "FOR24 Formazione Insegnanti", - "en": "FOR24 Teacher Training" - }, - "description": { - "it": "Gruppo dedicato a discussioni sul percorso formativo FOR24 ", - "en": "Group dedicated to discussions about training course F24" - }, - "image": "f24.jpg", - "href": "https://t.me/joinchat/s17FkitmVcA2NjU0" - }, - { - "name": { - "it": "Off-topic", - "en": "Off-topic" - }, - "description": { - "it": "Chat off-topic per parlare di qualsiasi cosa.", - "en": "Off-topic chat to discuss about anything." - }, - "image": "free_talk.jpg", - "href": "https://t.me/+FhIHaLyOckUyNTM0" - }, - { - "name": { - "it": "Recupero OFA", - "en": "Math OFA" - }, - "description": { - "it": "Gruppo collegato al corso di Matematica del continuo, dedicato agli ofa e i suoi argomenti.", - "en": "Additional learning requirements group for Mathematical Analysis." - }, - "image": "ofa.png", - "href": "https://t.me/joinchat/-s_MHXbJvfxmYWJk" - }, - { - "name": { - "it": "Test di Inglese", - "en": "English OFA" - }, - "description": { - "it": "Gruppo dedicato a discussioni inerenti al test di inglese (uguale sia per triennali che magistrali).", - "en": "Additional learning requirements group for English (B1) proficiency." - }, - "image": "inglese.jpg", - "href": "https://t.me/joinchat/gWd38sctY6U4Mjg0" - }, - { - "name": { - "it": "Artisti e Musica", - "en": "Artists and Music" - }, - "description": { - "it": "Gruppo dedicato ad artisti e musicisti dell’Università degli Studi di Milano.", - "en": "Group dedicated to artists and musicians from the University of Milan." - }, - "image": "music.png", - "href": "https://t.me/+mWyuQFz5QkhkNzRk" - }, - { - "name": { - "it": "Unirole", - "en": "UniRole" - }, - "description": { - "it": "Dedicato ai giochi di ruolo.", - "en": "Role-playing game chat." - }, - "image": "giochi_di_ruolo.png", - "href": "https://t.me/joinchat/BnUK_g-KHlIX_SrIieU8CA" - } -] \ No newline at end of file diff --git a/data/groups/StudentsAssociations.json b/data/groups/StudentsAssociations.json deleted file mode 100644 index 32f765cd..00000000 --- a/data/groups/StudentsAssociations.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "name": { - "it": "MUG - Milan University Gamers", - "en": "MUG - Milan University Gamers" - }, - "description": { - "it": "Dai un'occhiata all'associazione studentesca \"Milan University Gamers\".", - "en": "Take a look at the university association \"Milan University Gamers\"." - }, - "image": "mug_logo.png", - "href": "https://discord.gg/9qUGtnKYUU" - } -] \ No newline at end of file diff --git a/models/Admin.ts b/models/Admin.ts deleted file mode 100644 index fe60bbec..00000000 --- a/models/Admin.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface Admin -{ - username?: string, - year?: string, - user_id?: number - cdl?: string, -} \ No newline at end of file diff --git a/models/Contributor.ts b/models/Contributor.ts deleted file mode 100644 index 8e731d2e..00000000 --- a/models/Contributor.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default interface Contributor -{ - user_id?: number, - username?: string, - description?: string -} \ No newline at end of file diff --git a/models/Course.ts b/models/Course.ts deleted file mode 100644 index 527d7e4a..00000000 --- a/models/Course.ts +++ /dev/null @@ -1,19 +0,0 @@ -export default interface Website { - link?:string, - etichetta?:string -} - -export default interface Course -{ - id?:string, - cdl?: string, - name?:string, - anno?:number, - semestre?:number, - cfu?:number, - gruppo?:string, - websites:any[], - drive?:string, - wiki?:string, - chat_id?:number -} \ No newline at end of file diff --git a/models/Faq.ts b/models/Faq.ts index 38c54aaa..7a6943e2 100644 --- a/models/Faq.ts +++ b/models/Faq.ts @@ -1,4 +1,6 @@ +import { LocalizedField } from "./Models" + export default interface Faq { - question: any - answer: any -} \ No newline at end of file + question: LocalizedField + answer: LocalizedField +}; \ No newline at end of file diff --git a/models/Group.ts b/models/Group.ts deleted file mode 100644 index 0cb289e1..00000000 --- a/models/Group.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default interface Group -{ - name?: any - description?: any - image?: string - href?: string, - users?: string -} \ No newline at end of file diff --git a/models/Models.ts b/models/Models.ts index 25bb4986..911fe9f7 100644 --- a/models/Models.ts +++ b/models/Models.ts @@ -71,6 +71,21 @@ export interface Group { invite_link: string }; +export interface ExtraGroup extends Group { + name: LocalizedField, + description: LocalizedField, + user_count: number, + button_name: LocalizedField | null, + image_url: string | null + external_url: string | null +}; + +export interface ExtraGroups { + university_groups: Array, + announcement_groups: Array, + student_associations: Array +}; + export interface Website { name: string, url: string diff --git a/models/NetworkMember.ts b/models/NetworkMember.ts index 6b09c292..6feb02c3 100644 --- a/models/NetworkMember.ts +++ b/models/NetworkMember.ts @@ -1,6 +1,6 @@ export default interface CanMember { - user_id?: number | null, - username?: string, - name?: string, + user_id: number | null, + username: string, + name: string, delega?: string -} \ No newline at end of file +}; \ No newline at end of file diff --git a/models/Redirect.ts b/models/Redirect.ts deleted file mode 100644 index 6a7d08f6..00000000 --- a/models/Redirect.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default interface Redirect { - name?: any, - description?: any, - link?: string, - icon?: string -} \ No newline at end of file diff --git a/models/Representative.ts b/models/Representative.ts deleted file mode 100644 index 53f21f75..00000000 --- a/models/Representative.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default interface Representative -{ - id: string, - name?: string, - surname?: string, - username?: string, - year?: string, - cdl?: string, - user_id: number -} \ No newline at end of file diff --git a/models/Service.ts b/models/Service.ts index 27e7db67..683990c0 100644 --- a/models/Service.ts +++ b/models/Service.ts @@ -1,8 +1,10 @@ +import { LocalizedField } from "./Models" + export default interface Service { - id?:string - name?: any - link?: string - icon?: string - description?: any -} \ No newline at end of file + id:string + name: LocalizedField + description: LocalizedField + link: string + icon: string +}; \ No newline at end of file diff --git a/pages/404.tsx b/pages/404.tsx index cbb5450c..5902a9ef 100644 --- a/pages/404.tsx +++ b/pages/404.tsx @@ -1,6 +1,7 @@ import { Container } from 'react-bootstrap'; -import { DefaultButton, PrimaryButton, Text, useTheme, Image } from '@fluentui/react'; +import { DefaultButton, PrimaryButton, Text, useTheme } from '@fluentui/react'; import LocalizationService from 'services/LocalizationService'; +import Image from 'next/image'; const FourOhFour = () => { var theme = useTheme(); @@ -20,7 +21,14 @@ const FourOhFour = () => {
- Not found + Not found
diff --git a/pages/500.tsx b/pages/500.tsx index 15fe0739..7291eaa8 100644 --- a/pages/500.tsx +++ b/pages/500.tsx @@ -1,6 +1,7 @@ import { Container } from 'react-bootstrap'; -import { DefaultButton, PrimaryButton, Text, useTheme, Image } from '@fluentui/react'; +import { DefaultButton, PrimaryButton, Text, useTheme } from '@fluentui/react'; import { NextSeo } from 'next-seo'; +import Image from 'next/image'; import LocalizationService from 'services/LocalizationService'; const FiveHundred = () => { @@ -43,14 +44,21 @@ const FiveHundred = () => {
{locale?.serverErrorPage.title} -
+
{locale?.serverErrorPage.description}
- Not found + Not found
- +
{ - + + {/* Main webapp structure */} diff --git a/pages/_document.tsx b/pages/_document.tsx index 7255e0b3..a856f9f3 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -23,7 +23,7 @@ export default class MyDocument extends Document { render() { return ( - +
diff --git a/pages/courses/[slug].tsx b/pages/courses/[slug].tsx index b335f859..e9988835 100644 --- a/pages/courses/[slug].tsx +++ b/pages/courses/[slug].tsx @@ -221,6 +221,18 @@ export const getServerSideProps: GetServerSideProps = async ( { params }) => { const degreeSlug = params!.slug as unknown as string; + // Handle underscore old slugs + const [fixedSlug, hasReplaced] = replaceUnderscore(degreeSlug); + + if (hasReplaced) { + return { + redirect: { + destination: `/courses/${fixedSlug}`, + permanent: false, + }, + }; + } + let degreeResult = await getVerboseDegreeBySlug(degreeSlug); if (degreeResult.error) errors.degree = true; @@ -234,7 +246,7 @@ export const getServerSideProps: GetServerSideProps = async ( { params }) => { let degreeInformations = getDegreeInformations(degreeSlug); - /* Add Main Group to the loaded degree */ + // Add Main Group to the loaded degree let degree = degreeResult.value; if (degree?.group !== null && degree?.group.invite_link !== '' && degree?.group.invite_link !== null) { let mainDegreeGroup: CourseDegree = { @@ -270,4 +282,9 @@ export const getServerSideProps: GetServerSideProps = async ( { params }) => { }; }; +const replaceUnderscore = (str: string): [string, boolean] => { + const replaced = str.replace(/_/g, "-"); + return [replaced, replaced !== str]; +}; + export default Course; \ No newline at end of file diff --git a/pages/courses/index.tsx b/pages/courses/index.tsx index 8d0aa593..290d94e6 100644 --- a/pages/courses/index.tsx +++ b/pages/courses/index.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import { Text, Image, Dialog, DialogType, DialogFooter, SearchBox, ISearchBoxStyles } from '@fluentui/react'; +import { Text, Dialog, DialogType, DialogFooter, SearchBox, ISearchBoxStyles } from '@fluentui/react'; import { IconButton, IIconProps, ITooltipHostStyles, Link, PrimaryButton, TooltipHost, useTheme } from '@fluentui/react'; import { Container } from 'react-bootstrap'; import { useRouter } from 'next/router'; @@ -8,6 +8,7 @@ import { bold, semibold } from '../../services/Fonts'; import { ISuggestionItem } from '../../components/Courses/Autocomplete_types'; import { useBoolean } from "@fluentui/react-hooks"; import { NextSeo } from 'next-seo'; +import { LocalizedField } from "models/Models"; import * as Scroll from 'react-scroll'; import * as animationData from '../../components/Courses/Lottie/128040-searching.json'; import Lottie from 'react-lottie'; @@ -17,7 +18,7 @@ import Marquee from "react-fast-marquee"; import Chip from "components/Atoms/Chip"; import DegreesResult from "components/Courses/DegreesResult"; import GroupTypes from "components/Atoms/GroupTypes"; -import { LocalizedField } from "models/Models"; +import Image from 'next/image'; const Courses = () => { var theme = useTheme(); @@ -288,7 +289,13 @@ const Courses = () => {
- Server error + Server error
diff --git a/pages/groups.tsx b/pages/groups.tsx index 6b27de5f..eb9f2ddd 100644 --- a/pages/groups.tsx +++ b/pages/groups.tsx @@ -1,24 +1,37 @@ -import { Text, Image, useTheme, Separator, Link } from '@fluentui/react'; +import { Text, useTheme, Separator, Link } from '@fluentui/react'; import { Container } from 'react-bootstrap'; import { bold, semibold } from '../services/Fonts'; import { NextSeo } from 'next-seo'; import { useContext } from 'react'; import { preventDefault, preventVisibleHref } from 'services/Utils'; +import { GetServerSideProps } from 'next'; +import { getExtraGroups } from 'services/Requests'; +import { ExtraGroup, ExtraGroups } from 'models/Models'; import GroupsList, { GroupsType } from '../components/Groups/Groups'; import LocalizationService from "../services/LocalizationService"; -import Col from 'react-bootstrap/Col'; -import Row from 'react-bootstrap/Row'; -import Chip from 'components/Atoms/Chip'; import GlobalContext from 'services/GlobalContext'; import JsxParser from 'react-jsx-parser'; import GroupTypes from 'components/Atoms/GroupTypes'; +import FiveHundred from './500'; -const Groups = () => { +interface Props { + extraGroups: ExtraGroups, + extraGrousError: boolean +}; + +const Groups = (props: Props) => { var theme = useTheme(); const locale = LocalizationService.strings(); var language: string = LocalizationService.getLanguage() as string; + const { isPolicyAccepted, togglePolicyDialog } = useContext(GlobalContext); + const universityGroups: Array = props.extraGroups?.university_groups ?? []; + const announcementsGroups: Array = props.extraGroups?.announcement_groups ?? []; + const studentsAssociations: Array = props.extraGroups?.student_associations ?? []; + + if (props.extraGrousError) return ; + return ( <> {
-
+

- {locale?.groups.title} + {locale?.groups.title}

+
+ {locale?.groups.universityGroups.description2}{' '} + + preventDefault(e, isPolicyAccepted) && togglePolicyDialog()}> + {locale?.services.text4} + + +
+
- - -
- General groups -
- - - -
-
- - - -
-
- {locale?.groups.universityGroups.title} -
-
- {locale?.groups.universityGroups.description} -
-
- {locale?.groups.universityGroups.description2}{' '} - - preventDefault(e, isPolicyAccepted) && togglePolicyDialog()}> - {locale?.services.text4} - - -
-
- -
+
+
+

+ + + +

+
+
+ {locale?.groups.universityGroups.description} +
+
- +
- - -
-
- - - -
-
- {locale?.groups.announcementsGroups.title} -
-
- {locale?.groups.announcementsGroups.description} -
-
- {locale?.groups.announcementsGroups.description2} -
-
- - - -
- Announcements groups -
- -
+
+
+

+ + + +

+
+
+ {locale?.groups.announcementsGroups.title} +
+
+ +
+
- +
- - -
- Students associations -
- - - -
-
- - - -
-
- {locale?.groups.studentsAssociations.title} -
-
- {locale?.groups.studentsAssociations.description} -
- -
- -
+
+
+ +
+
+ {locale?.groups.studentsAssociations.description} +
+ +
- +
@@ -169,4 +155,18 @@ const Groups = () => { ); }; +export const getServerSideProps: GetServerSideProps = async () => { + const extraGroupsResult = await getExtraGroups(); + let extraGroupsError = false; + + if (extraGroupsResult.error) extraGroupsError = true; + + return { + props: { + extraGroups: extraGroupsResult.value ?? null, + extraGrousError: extraGroupsError + } + }; +}; + export default Groups; \ No newline at end of file diff --git a/pages/organization.tsx b/pages/organization.tsx index 6a5558b7..12bba8c1 100644 --- a/pages/organization.tsx +++ b/pages/organization.tsx @@ -5,35 +5,26 @@ import { Container } from 'react-bootstrap'; import { Persona, PersonaSize } from '@fluentui/react'; import { getNetworkMembers, getDevelopers } from '../services/Requests'; import { IIconProps } from '@fluentui/react'; -import { bold, semibold } from '../services/Fonts'; +import { bold, regular, semibold } from '../services/Fonts'; import { resetIds } from '@fluentui/react'; import { NextSeo } from 'next-seo'; -import Lottie from 'react-lottie'; -import * as lottieOrganization from '../components/Organization/Lottie/73386-problem-solving-team.json'; import Developer from 'models/Developer'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import LocalizationService from "../services/LocalizationService"; +import JsxParser from 'react-jsx-parser'; const Organization: NextPage = () => { var theme = useTheme(); const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); + const networkMembers = getNetworkMembers(); const developers: Array = getDevelopers(); const [domLoaded, setDomLoaded] = useState(false); const icon: IIconProps = { iconName: 'AiOutlineFilePdf' }; - const organizationOptions = { - loop: true, - autoplay: true, - animationData: lottieOrganization, - rendererSettings: { - preserveAspectRatio: 'xMidYMid slice' - } - }; - resetIds(); useEffect(() => { setDomLoaded(true); }, []); @@ -66,72 +57,62 @@ const Organization: NextPage = () => {
-
+
+
+

+ + + +

+
- - - {/* @ts-ignore */} - - - - -
-

- {locale?.aboutUs.text1} -

-
- -
- {locale?.aboutUs.text2} -
- -
- {locale?.aboutUs.text3} -
- -
- - {locale?.aboutUs.button.text1} - -
- -
+
+ + + +
+ + + {locale?.aboutUs.button.text1} +
- {locale?.aboutUs.header1} + {locale?.aboutUs.header1}
- -
- { domLoaded && {networkMembers[0].name}} - secondaryText={networkMembers[0].username} - onRenderSecondaryText={() => @{networkMembers[0].username}} - /> } + +
+ {domLoaded && + {networkMembers[0].name}} + secondaryText={networkMembers[0].username} + onRenderSecondaryText={() => @{networkMembers[0].username}} + />}
- {locale?.aboutUs.header2} + {locale?.aboutUs.header2}
{ - domLoaded && (networkMembers.slice(1, networkMembers.length)).map((x,i) => + domLoaded && (networkMembers.slice(1, networkMembers.length)).map((x, i) => <>
@@ -149,19 +130,19 @@ const Organization: NextPage = () => { ) } - +
- {locale?.contributors.header1} + {locale?.contributors.header1}
{ - domLoaded && developers.map((x:any, i:number) => + domLoaded && developers.map((x: Developer, i: number) => <>
@@ -191,14 +172,14 @@ const Organization: NextPage = () => { {locale?.aboutUs.contact.title} {locale?.aboutUs.contact.description} - +
- -
+
diff --git a/pages/rules.tsx b/pages/rules.tsx index 1156f158..acb10a71 100644 --- a/pages/rules.tsx +++ b/pages/rules.tsx @@ -1,19 +1,44 @@ -import React from "react"; -import { Text, Link, Icon, Image, useTheme } from '@fluentui/react'; +import React, { CSSProperties, useContext } from "react"; +import { Text, Link, Icon, useTheme } from '@fluentui/react'; import { Container } from 'react-bootstrap'; import { bold, semibold } from '../services/Fonts'; import { NextSeo } from 'next-seo'; import LocalizationService from "../services/LocalizationService"; import JsxParser from 'react-jsx-parser'; +import GlobalContext from "services/GlobalContext"; const Rules = () => { const theme = useTheme(); const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); - const imageProperties = { display: 'inline-block', width: 240 }; - const subHeader = { backgroundColor: theme.palette.neutralLighter, padding: '10px 0px' }; - const finalBox = { backgroundColor: theme.palette.neutralLighter }; + const { isHeaderPinned } = useContext(GlobalContext); + + const subHeader: CSSProperties = { + backgroundColor: theme.palette.neutralLighter, + borderTop: `1px solid ${theme.palette.neutralQuaternary}`, + borderBottom: `1px solid ${theme.palette.neutralQuaternary}`, + padding: '10px 0px', + position: 'sticky', + top: isHeaderPinned ? 44 : 0, + transition: 'top 0.2s ease-in-out 0s' + }; + + const finalBox: CSSProperties = { + backgroundColor: theme.palette.neutralLighter + }; + + const iconStyle: CSSProperties = { + color: theme.palette.white, + fontSize: 18, + display: 'flex' + }; + + const iconWrap: CSSProperties = { + padding: 6, + backgroundColor: theme.palette.themePrimary, + borderRadius: 20 + }; return ( <> @@ -56,14 +81,16 @@ const Rules = () => {
-
- - - {locale?.rules.rules.title} +
+ + + + + {locale?.rules.rules.title}
-
+
{locale?.rules.rules.toxicBehaviour.title} @@ -144,14 +171,16 @@ const Rules = () => {
-
+
- - {locale?.rules.measures.title} + + + + {locale?.rules.measures.title}
-
+
{locale?.rules.measures.description1} @@ -174,14 +203,16 @@ const Rules = () => {
-
+
- - {locale?.rules.advices.title} + + + + {locale?.rules.advices.title}
-
+
    {locale?.rules.advices.list1.map(x =>
  • {x}
  • )} @@ -197,9 +228,8 @@ const Rules = () => {
    - Rules final section
    - + {locale?.rules.lastSection.title1} diff --git a/pages/services.tsx b/pages/services.tsx index 4f02ed3d..0f917fda 100644 --- a/pages/services.tsx +++ b/pages/services.tsx @@ -1,11 +1,9 @@ -import { useContext, useState } from 'react'; +import { CSSProperties, useContext, useState } from 'react'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import LocalizationService from "../services/LocalizationService"; import GlobalContext from 'services/GlobalContext'; -import Lottie from 'react-lottie'; -import * as lottieMap from '../components/Services/Lottie/73243-happy-students-studying.json'; -import { Text, DocumentCardPreview, IDocumentCardPreviewProps, Link, Pivot, PivotItem, FontSizes, useTheme } from '@fluentui/react'; +import { Text, DocumentCardPreview, IDocumentCardPreviewProps, Link, Pivot, PivotItem, FontSizes, useTheme, mergeStyleSets } from '@fluentui/react'; import { NextSeo } from 'next-seo'; import { Container } from 'react-bootstrap'; import { Card, ICardTokens } from '@fluentui/react-cards'; @@ -18,19 +16,23 @@ const Services = () => { var theme = useTheme(); const locale = LocalizationService.strings(); var language: string | undefined = LocalizationService.getLanguage(); - const { isPolicyAccepted, togglePolicyDialog } = useContext(GlobalContext); + + const { isPolicyAccepted, togglePolicyDialog, isHeaderPinned } = useContext(GlobalContext); const redirects = getRedirects(); const guides = getGuides(); const tools = getTools(); - const servicesOptions = { - loop: true, - autoplay: true, - animationData: lottieMap, - rendererSettings: { - preserveAspectRatio: 'xMidYMid slice' - } + const subHeader: CSSProperties = { + backgroundColor: theme.palette.white, + border: `1px solid ${theme.palette.neutralQuaternary}`, + borderRadius: 3, + position: 'sticky', + top: isHeaderPinned ? 44 : 0, + transition: 'top 0.2s ease-in-out 0s', + zIndex: 1, + maxWidth: 1340, + margin: '0 auto' }; const [selectedSubSection, setSelectedSubSection] = useState("redirects"); @@ -43,7 +45,7 @@ const Services = () => { } }; - let cardProps = (iconName?: string): IDocumentCardPreviewProps => { + const cardProps = (iconName?: string): IDocumentCardPreviewProps => { return { previewImages: [ { @@ -57,6 +59,21 @@ const Services = () => { styles: { previewIcon: { backgroundColor: theme.palette.neutralLighter }, root: { borderBottom: '0px' } }, } }; + + const hoverStyle = mergeStyleSets({ + root: { + display: 'inline-block', + width: '100%', + transition: '0.1s all ease', + border: `1px solid rgba(255, 255, 255, 0)`, + selectors: { + ':hover': { + backgroundColor: theme.palette.neutralLight, + border: `1px solid ${theme.palette.neutralQuaternaryAlt}` + } + }, + }, + }); return ( <> @@ -86,60 +103,44 @@ const Services = () => { />
    -
    +
    +
    +

    + +

    +
    + +
    + {locale?.services.text2} +
    + +
    + + {locale?.services.text3} preventDefault(e, isPolicyAccepted) && togglePolicyDialog()}> + {locale?.services.text4} + + +
    +
    +
    - - -
    -

    - -

    -
    - -
    - {locale?.services.text2} -
    - -
    - - {locale?.services.text3} preventDefault(e, isPolicyAccepted) && togglePolicyDialog()}> - {locale?.services.text4} - - -
    - -
    -
    - {locale?.services.selectSubSection} -
    - - - - - - -
    - - - - {/* @ts-ignore */} - - -
    +
    + {locale?.services.selectSubSection} +
    - +
    + + + + +
    @@ -153,7 +154,7 @@ const Services = () => { {redirects.map((x, i) => - + @@ -181,7 +182,7 @@ const Services = () => { {guides.map((x, i) => - + @@ -209,7 +210,12 @@ const Services = () => { {tools.map((x, i) => - preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} className="text-decoration-none"> + preventDefault(e, isPolicyAccepted) && togglePolicyDialog()} + className="text-decoration-none" + > diff --git a/public/images/university_groups/advent_of_code.jpg b/public/images/university_groups/advent_of_code.jpg deleted file mode 100644 index c1a1d0de..00000000 Binary files a/public/images/university_groups/advent_of_code.jpg and /dev/null differ diff --git a/public/images/university_groups/alloggi.jpg b/public/images/university_groups/alloggi.jpg deleted file mode 100644 index 78b8ab7e..00000000 Binary files a/public/images/university_groups/alloggi.jpg and /dev/null differ diff --git a/public/images/university_groups/codin_game.jpg b/public/images/university_groups/codin_game.jpg deleted file mode 100644 index 5c4cd603..00000000 Binary files a/public/images/university_groups/codin_game.jpg and /dev/null differ diff --git a/public/images/university_groups/erasmus.png b/public/images/university_groups/erasmus.png deleted file mode 100644 index 367c0f20..00000000 Binary files a/public/images/university_groups/erasmus.png and /dev/null differ diff --git a/public/images/university_groups/f24.jpg b/public/images/university_groups/f24.jpg deleted file mode 100644 index b7c58945..00000000 Binary files a/public/images/university_groups/f24.jpg and /dev/null differ diff --git a/public/images/university_groups/free_talk.jpg b/public/images/university_groups/free_talk.jpg deleted file mode 100644 index 3406f72b..00000000 Binary files a/public/images/university_groups/free_talk.jpg and /dev/null differ diff --git a/public/images/university_groups/giochi_di_ruolo.png b/public/images/university_groups/giochi_di_ruolo.png deleted file mode 100644 index 29b6aa71..00000000 Binary files a/public/images/university_groups/giochi_di_ruolo.png and /dev/null differ diff --git a/public/images/university_groups/inglese.jpg b/public/images/university_groups/inglese.jpg deleted file mode 100644 index c5233dd1..00000000 Binary files a/public/images/university_groups/inglese.jpg and /dev/null differ diff --git a/public/images/university_groups/international_students.png b/public/images/university_groups/international_students.png deleted file mode 100644 index ae6232be..00000000 Binary files a/public/images/university_groups/international_students.png and /dev/null differ diff --git a/public/images/university_groups/lavoro.png b/public/images/university_groups/lavoro.png deleted file mode 100644 index 39490647..00000000 Binary files a/public/images/university_groups/lavoro.png and /dev/null differ diff --git a/public/images/university_groups/materiali.png b/public/images/university_groups/materiali.png deleted file mode 100644 index a7a68d60..00000000 Binary files a/public/images/university_groups/materiali.png and /dev/null differ diff --git a/public/images/university_groups/matricole.jpg b/public/images/university_groups/matricole.jpg deleted file mode 100644 index 48622f76..00000000 Binary files a/public/images/university_groups/matricole.jpg and /dev/null differ diff --git a/public/images/university_groups/mug_logo.png b/public/images/university_groups/mug_logo.png deleted file mode 100644 index 907b6e56..00000000 Binary files a/public/images/university_groups/mug_logo.png and /dev/null differ diff --git a/public/images/university_groups/music.png b/public/images/university_groups/music.png deleted file mode 100644 index 31bc395d..00000000 Binary files a/public/images/university_groups/music.png and /dev/null differ diff --git a/public/images/university_groups/ofa.png b/public/images/university_groups/ofa.png deleted file mode 100644 index df696b58..00000000 Binary files a/public/images/university_groups/ofa.png and /dev/null differ diff --git a/public/images/university_groups/ripetizioni.png b/public/images/university_groups/ripetizioni.png deleted file mode 100644 index d2f3527d..00000000 Binary files a/public/images/university_groups/ripetizioni.png and /dev/null differ diff --git a/public/images/university_groups/sondaggi.png b/public/images/university_groups/sondaggi.png deleted file mode 100644 index d9ee5640..00000000 Binary files a/public/images/university_groups/sondaggi.png and /dev/null differ diff --git a/public/images/university_groups/studentiunimi.png b/public/images/university_groups/studentiunimi.png deleted file mode 100644 index 055b485a..00000000 Binary files a/public/images/university_groups/studentiunimi.png and /dev/null differ diff --git a/public/images/university_groups/tirocini.jpg b/public/images/university_groups/tirocini.jpg deleted file mode 100644 index 52d36f9f..00000000 Binary files a/public/images/university_groups/tirocini.jpg and /dev/null differ diff --git a/public/seo/university.png b/public/seo/university.png index 8e6d78fd..b9a031a4 100644 Binary files a/public/seo/university.png and b/public/seo/university.png differ diff --git a/services/GlobalContext.tsx b/services/GlobalContext.tsx index 7bd0574f..6b88a4b1 100644 --- a/services/GlobalContext.tsx +++ b/services/GlobalContext.tsx @@ -13,11 +13,13 @@ export interface Context { darkTheme: PartialTheme, isPolicyAccepted: boolean, isPolicyDialogOpen: boolean, + isHeaderPinned: boolean, changeTheme: (theme: string) => void, changePalette: (paletteId: string) => void, changeLanguage: (language: string) => void, acceptPrivacyPolicy: () => void, - togglePolicyDialog: () => void + togglePolicyDialog: () => void, + setIsHeaderPinned: (isHeaderPinned: boolean) => void }; const defaultState = { @@ -26,13 +28,15 @@ const defaultState = { language: "it", lightTheme: {}, darkTheme: {}, + isHeaderPinned: true, isPolicyAccepted: false, isPolicyDialogOpen: false, changeTheme: () => {}, changePalette: () => {}, changeLanguage: () => {}, acceptPrivacyPolicy: () => {}, - togglePolicyDialog: () => {} + togglePolicyDialog: () => {}, + setIsHeaderPinned: () => {} }; const GlobalContext = createContext(defaultState); @@ -47,6 +51,8 @@ export const GlobalProvider = ({ children }: any) => { if (cookies.theme === undefined) setCookie("theme", Theme.LIGHT, { path: "/", expires: date }); if (cookies.palette === undefined) setCookie("palette", "a", { path: "/", expires: date }); if (cookies.isPolicyAccepted === undefined) setCookie("isPolicyAccepted", false, { path: "/", expires: policyDate }); + + const [isHeaderPinned, setIsHeaderPinned] = useState(true); const [isPolicyAccepted, setIsPolicyAccepted] = useState(cookies["isPolicyAccepted"] === 'true' ? true : false ?? false); const [isPolicyDialogOpen, setIsPolicyDialogOpen] = useState(false); @@ -107,11 +113,13 @@ export const GlobalProvider = ({ children }: any) => { darkTheme, isPolicyAccepted, isPolicyDialogOpen, + isHeaderPinned, changeTheme, changePalette, changeLanguage, acceptPrivacyPolicy, - togglePolicyDialog + togglePolicyDialog, + setIsHeaderPinned }}> {children} diff --git a/services/LocalizationService.ts b/services/LocalizationService.ts index b34e266d..a54e0eed 100644 --- a/services/LocalizationService.ts +++ b/services/LocalizationService.ts @@ -297,7 +297,7 @@ class LocalizationService { groups: { title: "Quale tipologia di gruppi cerchi?", universityGroups: { - label: "Gruppi universitari", + label: `Gruppi universitari`, title: "Unisciti ai nostri gruppi universitari", description: "Parla e discuti con altri studenti riguardo tutti i topic di cui hai bisogno.", description2: "Pensi manchi qualche gruppo in particolare?", @@ -306,17 +306,17 @@ class LocalizationService { } }, announcementsGroups: { - label: "Gruppi per gli annunci", - title: "Posta un annuncio sui nostri gruppi appositi", + label: `Gruppi per gli annunci`, + title: "Posta un annuncio sui nostri gruppi appositi.", description: "Abbiamo creato dei gruppi dedicati esclusivamente alla pubblicazione di annunci sia di ricerca che di offerta, come ad esempio per i libri ed appunti e le ripetizioni.", - description2: "Per postare un annuncio segui il template che trovi nei messaggi fissati quando entri.", + description2: `Per postare un annuncio segui il template che trovi nei messaggi fissati quando entri.`, card: { type: 'Gruppo annunci' } }, studentsAssociations: { label: "Associazioni studentesche", - title: "Dai un'occhiata alle associazioni studentesche UniMi", + title: `Dai un'occhiata alle associazioni studentesche UniMi`, description: "Non sempre gli studenti sanno quante associazioni studentesche ci sono lì fuori!", description2: "Se vorresti la tua associazione studentesca aggiunta nella lista scrivi ad un membro dello staff.", card: { @@ -326,8 +326,8 @@ class LocalizationService { users: 'Utenti' }, services: { - text1: "Tutti i servizi, in un\'unica pagina.", - text2: "Abbiamo realizzato una pagina per centralizzare tutti i collegamenti inerenti all'Università degli Studi di Milano e rendere disponibili anche le guide, strumenti e servizi telematici che abbiamo realizzato e messo a disposizione.", + text1: "Tutti i servizi, in un\'unica pagina", + text2: "Abbiamo realizzato una pagina per centralizzare tutti i collegamenti inerenti all'Università degli Studi di Milano e rendere disponibili anche le guide, strumenti e servizi telematici che abbiamo realizzato.", text3: "Pensi che manchi qualcosa?", text4: 'Faccelo sapere!', selectSubSection: "Seleziona la categoria che ti interessa", @@ -386,7 +386,7 @@ class LocalizationService { text2: 'Hai contribuito allo sviluppo del network e vorresti comparire in questa lista? Scrivi in privato a @giuseppetm.' }, aboutUs: { - text1: "Siamo un'organizzazione senza fini di lucro, apolitica, ovvero apartitica, e neutrale.", + text1: `Siamo una organizzazione senza fini di lucro, apolitica, ovvero apartitica, e neutrale.`, text2: "Il nostro obiettivo è quello di offrire servizi telematici agli studenti dell'Università degli Studi di Milano.", text3: "Qui è possibile vedere tutte le persone che fanno parte del Network StudentiUniMi.", button: { text1: 'Statuto', text2: "Dai un'occhiata al nostro statuto!" }, @@ -695,7 +695,7 @@ class LocalizationService { groups: { title: "What type of groups are you looking for?", universityGroups: { - label: "University groups", + label: `University groups`, title: "Join the university groups of our Network", description: "Talk and discuss with other students about all the topics you need.", description2: "Do you think any particular group is missing?", @@ -704,17 +704,17 @@ class LocalizationService { } }, announcementsGroups: { - label: "Announcements groups", - title: "Post an ad on our dedicated groups", + label: `Announcements groups`, + title: "Post an ad on our dedicated groups.", description: "We have created groups dedicated exclusively to posting both want and offer ads, such as for books and notes and private lessons.", - description2: "To post an ad follow the template you find in the posts set when you enter.", + description2: `To post an ad follow the template that you can find in the pinned messages set when you enter.`, card: { type: 'Announcements group' } }, studentsAssociations: { label: "Students associations", - title: "Take a look at UniMi Students Associations", + title: `Take a look at the UniMi student associations`, description: "Students don't always know how many student associations are out there!", description2: "If you would like your student association added to the list write to a staff member.", card: { @@ -724,7 +724,7 @@ class LocalizationService { users: 'Users' }, services: { - text1: "All services, in one central place.", + text1: "All services, in one central place", text2: "We have created a page to centralize all the connections relating to the University of Milan and also make available our guides and telematic services we have created.", text3: "Do you think something is missing?", text4: 'Let us know!', @@ -784,7 +784,7 @@ class LocalizationService { text2: 'Did you contribute to the development of the Network and you would like to appear in this list? Send a private message to @giuseppetm.' }, aboutUs: { - text1: 'We are a non-profit organization, neutral and not affiliated to any political party.', + text1: `We are a nonprofit, apolitical, meaning nonpartisan, and neutral organization.`, text2: "Our goal is to provide online services to the students at the University of Milan.", text3: "Here you can see all the people within the Network StudentiUniMi.", button: { text1: 'Statute', text2: "You can read our statute here!" }, @@ -826,7 +826,7 @@ class LocalizationService { groups: { title: "Tutti i gruppi | Network StudentiUniMi", description: "Gruppi universitari, per gli annunci e associazioni studentesche dell'Università degli Studi di Milano." }, services: { title: "Servizi e link rapidi | Network StudentiUniMi", description: "Tutti i servizi e i link rapidi alle risorse dell'Università degli Studi di Milano: iscrizioni esami, verbalizzazioni voti, webmail, e molto altro. Un'alternativa veloce a UNIMIA." }, rules: { title: "Regolamento dei gruppi | Network StudentiUniMi", description: "Il regolamento ufficiale dei gruppi Telegram del Network StudentiUniMi, il più grande network studentesco dell'Università degli Studi di Milano." }, - university: { title: "Informazioni dall'Ateneo e rappresentanti | Network StudentiUniMi", description: "Rimani aggiornato con tutte le informazioni e i rappresentanti dell'Università degli Studi di Milano, offerto dal Network StudentiUniMi." }, + university: { title: "Informazioni dall'Ateneo, graduatorie, mappe universitarie e rappresentanti | Network StudentiUniMi", description: "Rimani aggiornato con tutte le informazioni, graduatorie, mappe universitarie e i rappresentanti dell'Università degli Studi di Milano, offerto dal Network StudentiUniMi." }, organization: { title: "Chi siamo | Network StudentiUniMi", description: "Chi siamo? Scopri l'organizzazione dietro il Network StudentiUniMi, il più grande network studentesco dell'Università degli Studi di Milano." }, degreeLoaded: { title1: 'Gruppi e risorse di ', title2: ' | Network StudentiUniMi', description1: 'Tutte le risorse e i link dei gruppi Telegram di ', description2: " dell'Università degli Studi di Milano, offerti dal Network StudentiUniMi." }, notFound: { title: "La pagina che stai cercando non esiste | Network StudentiUniMi", description: "Uh oh, non riusciamo a trovare la pagina che stai cercando. Forse puoi provare a tornare alla homepage e cercare da lì." }, @@ -841,7 +841,7 @@ class LocalizationService { groups: { title: "Groups | Network StudentiUniMi", description: "University groups, for announcements and student associations of the University of Milan." }, services: { title: "Services | Network StudentiUniMi", description: "All services and rapid links to the University of Milan's resources: exams, grades, webmail and much more. A very fast alternative UNIMIA replacement." }, rules: { title: "Groups rules | Network StudentiUniMi", description: "The official rules of the StudentiUniMi Network, the largest student network of the University of Milan." }, - university: { title: "University informations and redirects | Network StudentiUniMi", description: "Stay up-to-date with all the informations and representatives of the University of Milan, offered by the StudentiUniMi Network." }, + university: { title: "University informations, rankings, maps and representatives | Network StudentiUniMi", description: "Stay up-to-date with all the informations and representatives of the University of Milan, offered by the StudentiUniMi Network." }, organization: { title: "Organization | Network StudentiUniMi", description: "Who are we? Discover the organization behind the StudentiUniMi Network, the largest network of the University of Milan." }, degreeLoaded: { title1: 'Groups and resources of ', title2: ' | Network StudentiUniMi', description1: 'All the resources and links of the Telegram groups of ', description2: ' of the University of Milan, offered by StudentiUniMi Network.' }, notFound: { title: "The page you were looking for does not exist | Network StudentiUniMi", description: "Uh oh, we can't seem to find the page you're looking for. Maybe you can try going to the homepage and look again from there." }, diff --git a/services/Requests.ts b/services/Requests.ts index 52f6802f..ad83d61c 100644 --- a/services/Requests.ts +++ b/services/Requests.ts @@ -7,25 +7,19 @@ /* Old Models (to be replaced when remaining APIs are implemented) */ import Service from '../models/Service'; -import Contributor from '../models/Contributor'; import Faq from '../models/Faq'; import NetworkMember from '../models/NetworkMember'; import Developer from 'models/Developer'; import UniversityLink from 'models/UniversityLink'; -import Group from 'models/Group'; import { DegreeInformation, TempDegree } from 'models/DegreeInformation'; /* Updated models */ -import { Department, Degree, VerboseDegree, CourseDegree, Representative, Admin } from '../models/Models'; +import { Department, Degree, VerboseDegree, CourseDegree, Representative, Admin, ExtraGroups } from '../models/Models'; /* Data (these will be replaced by api soon) */ -import groups from '../data/groups/Groups.json'; -import announcementsGroups from '../data/groups/Announcements.json'; -import studentsAssociations from '../data/groups/StudentsAssociations.json'; import redirectsData from '../data/services/Redirects.json'; import guidesData from '../data/services/Guides.json'; import toolsData from '../data/services/Tools.json'; -import Contributors from '../data/Contributors.json'; import Faqs from '../data/Faqs.json'; import NetworkMembers from '../data/NetworkMembers.json'; import Developers from '../data/Developers.json'; @@ -33,15 +27,16 @@ import UniversityLinks from '../data/UniversityLinks.json'; import DegreeInformations from '../data/DegreeInformations.json'; /* Endpoints */ -const api_endpoint = process.env.NEXT_PUBLIC_API_URL || 'https://api.studentiunimi.it/api'; -const departments_endpoint = '/departments'; -const degrees_endpoint = '/degrees'; -const degree_endpoint = '/degree'; -const courses_endpoint = '/courses'; -const representatives_endpoint = '/representatives'; -const typingDegrees_endpoint = '/typing-degrees'; -const searchDegrees_endpoint = '/search-degrees'; -const admins_endpoint = '/admins'; +const apiEndpoint = process.env.NEXT_PUBLIC_API_URL || 'https://api.studentiunimi.it/api'; +const departmentsEndpoint = '/departments'; +const degreesEndpoint = '/degrees'; +const degreeEndpoint = '/degree'; +const coursesEndpoint = '/courses'; +const representativesEndpoint = '/representatives'; +const typingDegreesEndpoint = '/typing-degrees'; +const searchDegreesEndpoint = '/search-degrees'; +const adminsEndpoint = '/admins'; +const featuredGroupsEndpoint = '/featured-groups'; /* Main class to build response */ class Result @@ -86,7 +81,7 @@ async function getAsync(path: string) : Promise> * This function retrieves the existing departments. */ export async function getDepartments(): Promise>> { - return getAsync>(api_endpoint + departments_endpoint); + return getAsync>(apiEndpoint + departmentsEndpoint); }; /** @@ -94,7 +89,7 @@ export async function getDepartments(): Promise>> { * @param departmentKey Key or parameter to query by department */ export async function getDegrees(departmentKey: string): Promise>> { - return getAsync>(`${api_endpoint}${degrees_endpoint}?dep_id=${departmentKey}`); + return getAsync>(`${apiEndpoint}${degreesEndpoint}?dep_id=${departmentKey}`); }; /** @@ -102,7 +97,7 @@ export async function getDegrees(departmentKey: string): Promise>> { - return getAsync>(`${api_endpoint}${courses_endpoint}?deg_id=${degreeKey}`); + return getAsync>(`${apiEndpoint}${coursesEndpoint}?deg_id=${degreeKey}`); }; /** @@ -110,7 +105,7 @@ export async function getCourses(degreeKey: string): Promise>> { - return getAsync>(`${api_endpoint}${representatives_endpoint}?dep_id=${departmentKey}`); + return getAsync>(`${apiEndpoint}${representativesEndpoint}?dep_id=${departmentKey}`); }; /** @@ -118,7 +113,7 @@ export async function getRepresentatives(departmentKey: string): Promise> { - return getAsync(`${api_endpoint}${degree_endpoint}?slug=${degreeSlug}`); + return getAsync(`${apiEndpoint}${degreeEndpoint}?slug=${degreeSlug}`); }; /** @@ -126,28 +121,36 @@ export async function getVerboseDegreeBySlug(degreeSlug: string): Promise> { - return getAsync(`${api_endpoint}${degree_endpoint}?pk=${degreeID}`); + return getAsync(`${apiEndpoint}${degreeEndpoint}?pk=${degreeID}`); }; /** * This function retrieves an array of string referred to Degree names (used in Homepage). */ export async function getStringDegrees(): Promise>> { - return getAsync>(`${api_endpoint}${typingDegrees_endpoint}`) + return getAsync>(`${apiEndpoint}${typingDegreesEndpoint}`) }; /** * This function retrieves existing degrees (search-box api). */ export async function getDegreesForSearchBox(searchText: string): Promise> { - return getAsync>(`${api_endpoint}${searchDegrees_endpoint}?q=${searchText}`); + return getAsync>(`${apiEndpoint}${searchDegreesEndpoint}?q=${searchText}`); }; /** * This function retrieves admins of a certain degree. */ export async function getDegreeAdmins(degreeSlug: string): Promise>> { - return getAsync>(`${api_endpoint}${admins_endpoint}?slug=${degreeSlug}`); + return getAsync>(`${apiEndpoint}${adminsEndpoint}?slug=${degreeSlug}`); +}; + +/** + * This function retrieves university groups, announcements groups and students associations. + * @returns {ExtraGroups} Extra Groups + */ +export async function getExtraGroups(): Promise> { + return getAsync(`${apiEndpoint}${featuredGroupsEndpoint}`); }; @@ -155,27 +158,18 @@ export async function getDegreeAdmins(degreeSlug: string): Promise => Contributors; // not used at the moment - export const getFaqs = (): Array => Faqs; /* Courses section - Degree informations (static data at the moment) */ const getTempDegrees = (): Array => DegreeInformations; export const getDegreeInformations = (degreeSlug: string): Array => (getTempDegrees()).find((tempDegree: TempDegree) => tempDegree.slug === degreeSlug)?.degreeInformations ?? []; -/* Groups section */ -export const getGroups = (): Array => groups; - -export const getGroupsAnnouncements = (): Array => announcementsGroups; - -export const getStudentsAssociations = (): Array => studentsAssociations; - /* Services section */ -export const getRedirects = (): Array => redirectsData; +export const getRedirects = (): Array => redirectsData as unknown as Array; -export const getGuides = (): Array => guidesData; +export const getGuides = (): Array => guidesData as unknown as Array; -export const getTools = (): Array => toolsData; +export const getTools = (): Array => toolsData as unknown as Array; /* University section */ export const getUniversityLinks = (): Array => UniversityLinks; diff --git a/services/Utils.ts b/services/Utils.ts index 2db3f893..470dea25 100644 --- a/services/Utils.ts +++ b/services/Utils.ts @@ -103,7 +103,19 @@ export const preventDefault = (e: any, isPolicyAccepted: boolean = false) => { export const preventVisibleHref = (isPolicyAccepted: boolean = false, href: string) => { if (!isPolicyAccepted) return undefined; return href; -} +}; + + +/** + * Function to adapt users_count (example: 5131 users => 5100+ users) + * @param {number} num + * @returns {number} result + */ +export const formatLowerNumber = (num: number): number => { + const mult = Math.pow(10, Math.floor(Math.log10(num))); + return Math.floor(num / mult) * mult; +}; + export const redirectToLink = (link: string): void => { window.open(link, '_blank'); diff --git a/styles/index.scss b/styles/index.scss index 53f46c33..5e94001a 100644 --- a/styles/index.scss +++ b/styles/index.scss @@ -397,19 +397,22 @@ footer .ms-Label { } /* Group types selector */ -@media only screen and (max-width: 440px) { +@media only screen and (max-width: 480px) { .group-types-selector { - img { - max-width: 80px; - } - .group-type-selector { - max-width: 140px; - max-height: 180px; + max-width: 160px; + max-height: 200px; } span { font-size: 14px; } } +} + +@media only screen and (max-width: 352px) { + .group-type-selector { + min-width: 250px; + max-width: 100% !important; + } } \ No newline at end of file