Przeglądaj źródła

Finished UI desktop

djoka/header
Djordje Mitrovic 2 lat temu
rodzic
commit
04aad493c5

+ 6
- 1
src/components/Header/HeaderProfileBar/HeaderNotifications/HeaderNotifications.js Wyświetl plik

<NotificationsIcon /> <NotificationsIcon />
</HeaderIconContainer> </HeaderIconContainer>
} }
contentContainerStyles={{ borderRadius: "8px" }}
contentContainerStyles={{
"& .MuiPopover-paper": {
borderRadius: "6px",
backgroundColor: "transparent",
},
}}
content={<HeaderNotificationsContent />} content={<HeaderNotificationsContent />}
popoverProps={{ popoverProps={{
anchorOrigin: { anchorOrigin: {

+ 30
- 8
src/components/Header/HeaderProfileBar/HeaderNotifications/HeaderNotificationsContent/HeaderNotificationsContent.js Wyświetl plik

import React from "react";
import React, { useMemo } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { import {
HeaderLatestNotificationsContainer, HeaderLatestNotificationsContainer,
HeaderNotificationsContentHeaderTitle, HeaderNotificationsContentHeaderTitle,
} from "./HeaderNotificationsContent.styled"; } from "./HeaderNotificationsContent.styled";
import SingleNotification from "./SingleNotification/SingleNotification"; import SingleNotification from "./SingleNotification/SingleNotification";
import { useNotificationsQuery } from "features/user/usersApiSlice";
import { AccountCircle } from "@mui/icons-material";
import { useTranslation } from "react-i18next";


const HeaderNotificationsContent = (props) => {
console.log(props);
const dummyNotification = {
userPicture: <AccountCircle />,
notificationText: (
<p>
<i>Olivia Saturday</i> commented on your post.
</p>
),
date: new Date(2023, 6, 10),
};

const HeaderNotificationsContent = () => {
const { data } = useNotificationsQuery(); //eslint-disable-line
const { t } = useTranslation();

const latestNotifications = useMemo(() => {
// return data?.notifications?.slice(0, 3);
return Array(3).fill(dummyNotification, 0);
});
return ( return (
<HeaderNotificationsContentContainer> <HeaderNotificationsContentContainer>
<HeaderNotificationsContentHeader> <HeaderNotificationsContentHeader>
<HeaderNotificationsContentHeaderTitle> <HeaderNotificationsContentHeaderTitle>
Notifications
{t("notifications.title")}
</HeaderNotificationsContentHeaderTitle> </HeaderNotificationsContentHeaderTitle>
<HeaderNotificationsContentHeaderSeeMore> <HeaderNotificationsContentHeaderSeeMore>
See more
{t("common.seeMore")}
</HeaderNotificationsContentHeaderSeeMore> </HeaderNotificationsContentHeaderSeeMore>
</HeaderNotificationsContentHeader> </HeaderNotificationsContentHeader>
<HeaderLatestNotificationsContainer> <HeaderLatestNotificationsContainer>
<SingleNotification />
<SingleNotification />
<SingleNotification />
{latestNotifications?.map?.((singleNotification, index) => (
<SingleNotification
key={index}
notificationObject={singleNotification}
/>
))}
</HeaderLatestNotificationsContainer> </HeaderLatestNotificationsContainer>
</HeaderNotificationsContentContainer> </HeaderNotificationsContentContainer>
); );

+ 32
- 12
src/components/Header/HeaderProfileBar/HeaderNotifications/HeaderNotificationsContent/SingleNotification/SingleNotification.js Wyświetl plik

import React from "react";
import React, { useEffect, useMemo } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { import {
SingleNotificationContainer, SingleNotificationContainer,
SingleNotificationProfile, SingleNotificationProfile,
SingleNotificationUnseen, SingleNotificationUnseen,
} from "./SingleNotification.styled"; } from "./SingleNotification.styled";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
import { formatTimeSpan } from "util/dateHelpers";
import { useSeeNotificationMutation } from "features/user/usersApiSlice";
import { makeErrorToastMessage } from "util/toastMessage";

const SingleNotification = (props) => {

const [seeNotification, result] = useSeeNotificationMutation();
const timespan = useMemo(
() => formatTimeSpan(props?.notificationObject?.date),
[props?.notificationObject?.date]
);

const handleSeeNotification = () => {
seeNotification(2);
console.log(result)
}

useEffect(() => {
if (result.isError) {
makeErrorToastMessage("Server error")
}
}, [result.isError])


const SingleNotification = () => {
return ( return (
<SingleNotificationContainer>
<SingleNotificationContainer onClick={handleSeeNotification}>
<SingleNotificationProfile> <SingleNotificationProfile>
<AccountCircleIcon />
{props?.notificationObject?.userPicture}
</SingleNotificationProfile> </SingleNotificationProfile>
<SingleNotificationDetails> <SingleNotificationDetails>
<SingleNotificationContent> <SingleNotificationContent>
<p>
<i>Olivia Saturday</i> commented on your
post.
</p>
{props?.notificationObject?.notificationText}
</SingleNotificationContent> </SingleNotificationContent>
<SingleNotificationDate>
12 hours ago
</SingleNotificationDate>
<SingleNotificationDate>{timespan}</SingleNotificationDate>
</SingleNotificationDetails> </SingleNotificationDetails>
<SingleNotificationUnseen /> <SingleNotificationUnseen />
</SingleNotificationContainer> </SingleNotificationContainer>


SingleNotification.propTypes = { SingleNotification.propTypes = {
children: PropTypes.node, children: PropTypes.node,
notificationObject: PropTypes.shape({
userPicture: PropTypes.oneOfType([PropTypes.element, PropTypes.node]),
notificationText: PropTypes.node,
date: PropTypes.object,
}),
}; };


export default SingleNotification; export default SingleNotification;

+ 22
- 1
src/components/Header/HeaderProfileBar/HeaderProfile/HeaderProfile.js Wyświetl plik

import { HeaderProfileContainer } from "./HeaderProfile.styled"; import { HeaderProfileContainer } from "./HeaderProfile.styled";
import PersonIcon from "@mui/icons-material/Person"; import PersonIcon from "@mui/icons-material/Person";
import AccountCircleIcon from "@mui/icons-material/AccountCircle"; import AccountCircleIcon from "@mui/icons-material/AccountCircle";
import PopoverComponent from "components/Popover/Popover";
import { HeaderIconContainer } from "components/Header/Header.styled";
import HeaderProfilePopoverContent from "./HeaderProfilePopoverContent/HeaderProfilePopoverContent";
const HeaderProfile = () => { const HeaderProfile = () => {
return <HeaderProfileContainer>profile</HeaderProfileContainer>;
return (
<HeaderProfileContainer>
<PopoverComponent
contentContainerStyles={{
"& .MuiPopover-paper": {
borderRadius: "8px",
overflow: "hidden",
backgroundColor: "transparent",
},
}}
trigger={
<HeaderIconContainer>
<AccountCircleIcon />
</HeaderIconContainer>
}
content={<HeaderProfilePopoverContent />}
/>
</HeaderProfileContainer>
);
}; };


HeaderProfile.propTypes = { HeaderProfile.propTypes = {

+ 5
- 1
src/components/Header/HeaderProfileBar/HeaderProfile/HeaderProfile.styled.js Wyświetl plik

import { Box } from "@mui/material"; import { Box } from "@mui/material";
import styled from "styled-components"; import styled from "styled-components";


export const HeaderProfileContainer = styled(Box)``
export const HeaderProfileContainer = styled(Box)`
& .MuiPopover-paper {
border-radius: 8px;
}
`

+ 56
- 0
src/components/Header/HeaderProfileBar/HeaderProfile/HeaderProfilePopoverContent/HeaderProfilePopoverContent.js Wyświetl plik

import React, { useMemo } from "react";
import PropTypes from "prop-types";
import {
HeaderProfileMenu,
HeaderProfileMenuItem,
HeaderProfilePopoverContentContainer,
HeaderProfilePopoverContentHeaderContainer,
ProfileDetails,
ProfileInitials,
ProfileMail,
ProfileName,
} from "./HeaderProfilePopoverContent.styled";
import { PAGES } from "constants/pages";
import { useMyUserQuery } from "features/user/usersApiSlice";
import { useTranslation } from "react-i18next";

const HeaderProfilePopoverContent = () => {
const { data } = useMyUserQuery(); //eslint-disable-line
const { t } = useTranslation();

const myInitials = useMemo(() => {
// return `${data?.firstName?.[0]}${data?.lastName?.[0]}`
return "AM";
}, [data]);

const myName = useMemo(() => {
// return `${data?.firstName} ${data?.lastName}`
return "Andrew Marks";
}, [data]);

const myMail = useMemo(() => {
// return `${data?.mail}`
return "dummymail@dilig.net";
}, [data]);
return (
<HeaderProfilePopoverContentContainer>
<HeaderProfilePopoverContentHeaderContainer>
<ProfileInitials to={PAGES.PROFILE.route}>{myInitials}</ProfileInitials>
<ProfileDetails>
<ProfileName to={PAGES.PROFILE.route}>{myName}</ProfileName>
<ProfileMail>{myMail}</ProfileMail>
</ProfileDetails>
</HeaderProfilePopoverContentHeaderContainer>
<HeaderProfileMenu>
<HeaderProfileMenuItem>{t("pages.settings")}</HeaderProfileMenuItem>
<HeaderProfileMenuItem>{t("common.logout")}</HeaderProfileMenuItem>
</HeaderProfileMenu>
</HeaderProfilePopoverContentContainer>
);
};

HeaderProfilePopoverContent.propTypes = {
children: PropTypes.node,
};

export default HeaderProfilePopoverContent;

+ 66
- 0
src/components/Header/HeaderProfileBar/HeaderProfile/HeaderProfilePopoverContent/HeaderProfilePopoverContent.styled.js Wyświetl plik

import { Box, Typography } from "@mui/material";
import { NavLink } from "react-router-dom";
import styled from "styled-components";

export const HeaderProfilePopoverContentContainer = styled(Box)`
width: 250px;
padding: 0 8px;
padding-bottom: 8px;
cursor: default;
background-color: ${(props) => props?.theme?.colors?.primaryDark};
border-radius: 8px;
`;
export const HeaderProfilePopoverContentHeaderContainer = styled(Box)`
display: flex;
align-items: center;
gap: 12px;
height: 60px;
border-bottom: 1px solid ${(props) => props?.theme?.colors?.secondaryDark};
`;
export const ProfileInitials = styled(NavLink)`
width: 44px;
height: 44px;
min-width: 44px;
min-height: 44px;
border-radius: 100%;
text-align: center;
vertical-align: middle;
line-height: 44px;
text-decoration: none;
background-color: ${(props) => props?.theme?.colors?.primaryLighter};
`;
export const ProfileDetails = styled(Box)`
display: flex;
flex-direction: column;
justify-content: space-evenly;
height: 100%;
padding: 8px 0;
`;
export const ProfileName = styled(NavLink)`
font-family: Inter;
text-decoration: none;
font-size: 18px;
font-weight: 700;
color: ${(props) => props?.theme?.colors?.textColor};
`;
export const ProfileMail = styled(Typography)`
font-family: Inter;
font-size: 14px;
font-weight: 400;
color: ${(props) => props?.theme?.colors?.textColor};
`;
export const HeaderProfileMenu = styled(Box)`
display: flex;
flex-direction: column;
`;
export const HeaderProfileMenuItem = styled(NavLink)`
padding: 4px 8px;
border-radius: 8px;
text-decoration: none;
cursor: pointer;
color: ${(props) => props?.theme?.colors?.textColor};
&:hover {
background-color: ${(props) => props?.theme?.colors?.textColor};
color: ${(props) => props?.theme?.colors?.primaryDark};
}
`;

+ 1
- 1
src/components/Popover/Popover.js Wyświetl plik

{props?.trigger} {props?.trigger}
</PopoverTrigger> </PopoverTrigger>
<Popover <Popover
style={props?.contentContainerStyles}
sx={props?.contentContainerStyles}
open={isOpened} open={isOpened}
anchorEl={anchorEl} anchorEl={anchorEl}
onClose={togglePopover} onClose={togglePopover}

+ 2
- 2
src/components/Popover/Popover.styled.js Wyświetl plik

import { Box } from "@mui/material"; import { Box } from "@mui/material";
import styled from "styled-components"; import styled from "styled-components";


export const PopoverContainer = styled(Box)``
export const PopoverTrigger = styled(Box)``
export const PopoverContainer = styled(Box)``;
export const PopoverTrigger = styled(Box)``;

+ 33
- 0
src/features/user/usersApiSlice.js Wyświetl plik

import { apiSlice } from "features/api/apiSlice";

const notificationTag = "notifications";

export const usersApiSlice = apiSlice.injectEndpoints({
tagTypes: [notificationTag],
endpoints: (builder) => ({
myUser: builder.query({
query: () => ({
url: "api/user/me",
}),
}),
notifications: builder.query({
query: () => ({
url: "api/user/me/notifications",
}),
providesTags: [notificationTag],
}),
seeNotification: builder.mutation({
query: (notificationId) => ({
url: `api/user/me/notifications/${notificationId}`,
method: "PATCH",
}),
invalidatesTags: [notificationTag],
}),
}),
});

export const {
useMyUserQuery,
useNotificationsQuery,
useSeeNotificationMutation,
} = usersApiSlice;

+ 17
- 1
src/i18nt/resources/sr.js Wyświetl plik

select: "Select...", select: "Select...",
none: "None", none: "None",
date: { date: {
range: "{{start}} to {{end}}",
range: "{{start}} do {{end}}",
}, },
logout: "Izloguj se",
seeMore: "Vidi još",
}, },


notifications: {
title: "Obaveštenja",
},
pages: { pages: {
home: "Početna", home: "Početna",
login: "Login", login: "Login",
WrongPasswordAccountIsLocked: "Wrong credentials, account is locked", WrongPasswordAccountIsLocked: "Wrong credentials, account is locked",
AccountIsLocked: "Account is locked", AccountIsLocked: "Account is locked",
}, },
date: {
timespan: {
yearsAgo: "Pre {{years}} godine",
monthsAgo: "Pre {{months}} meseca",
daysAgo: "Pre {{days}} dana",
hoursAgo: "Pre {{hours}} sata",
minutesAgo: "Pre {{minutes}} minuta",
secondsAgo: "Pre {{seconds}} sekunde",
now: "Upravo sada",
},
},
}; };

+ 73
- 2
src/util/dateHelpers.js Wyświetl plik

import { format } from "date-fns"; import { format } from "date-fns";
import { enUS } from "date-fns/locale"; import { enUS } from "date-fns/locale";
import i18next from "i18next";
import i18n from "../i18nt/index";


export function formatDate(date, fmt = "MM/dd/y", locale = enUS) { export function formatDate(date, fmt = "MM/dd/y", locale = enUS) {
const dt = new Date(date); const dt = new Date(date);
export function formatDateRange(dates) { export function formatDateRange(dates) {
const start = formatDate(dates.start); const start = formatDate(dates.start);
const end = formatDate(dates.end); const end = formatDate(dates.end);
return i18next.t("common.date.range", { start, end });
return i18n.t("common.date.range", { start, end });
} }

export const calculateTimeSpan = (date) => {
let currentDate = new Date();
let timeElapsed = (currentDate.getTime() - date?.getTime()) / 1000;
if (timeElapsed <= 0) {
return {
yearsPassed: 0,
monthsPassed: 0,
daysPassed: 0,
hoursPassed: 0,
minutesPassed: 0,
secondsPassed: 0,
};
}

const yearsPassed = Math.floor(timeElapsed / (60 * 60 * 24 * 365));
timeElapsed -= yearsPassed * (60 * 60 * 24 * 365);

const monthsPassed = Math.floor(timeElapsed / (60 * 60 * 24 * 31));
timeElapsed -= monthsPassed * (60 * 60 * 24 * 31);

const daysPassed = Math.floor(timeElapsed / (60 * 60 * 24));
timeElapsed -= daysPassed * (60 * 60 * 24);

const hoursPassed = Math.floor(timeElapsed / (60 * 60));
timeElapsed -= hoursPassed * (60 * 60);

const minutesPassed = Math.floor(timeElapsed / 60);
timeElapsed -= minutesPassed * 60;

const secondsPassed = Math.floor(timeElapsed);
timeElapsed -= secondsPassed;

return {
yearsPassed,
monthsPassed,
daysPassed,
hoursPassed,
minutesPassed,
secondsPassed,
};
};

export const formatTimeSpan = (date) => {
const timespanObject = calculateTimeSpan(date);
if (timespanObject.yearsPassed > 0)
return i18n.t("date.timespan.yearsAgo", {
years: timespanObject.yearsPassed,
});
if (timespanObject.monthsPassed > 0)
return i18n.t("date.timespan.monthsAgo", {
months: timespanObject.monthsPassed,
});
if (timespanObject.daysPassed > 0)
return i18n.t("date.timespan.daysAgo", {
days: timespanObject.daysPassed,
});
if (timespanObject.hoursPassed > 0)
return i18n.t("date.timespan.hoursAgo", {
hours: timespanObject.hoursPassed,
});
if (timespanObject.minutesPassed > 0)
return i18n.t("date.timespan.minutesAgo", {
minutes: timespanObject.minutesPassed,
});
if (timespanObject.secondsPassed > 0)
return i18n.t("date.timespan.secondsAgo", {
seconds: timespanObject.secondsPassed,
});
return i18n.t("date.timespan.now")
};

Ładowanie…
Anuluj
Zapisz