Implemented emai notifications.
This commit is contained in:
parent
8f2cc49f9b
commit
96f7713f4d
12
assets/icons/mailcheckicon.svg
Normal file
12
assets/icons/mailcheckicon.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,0,-0.113382)">
|
||||||
|
<g transform="matrix(0.725404,0,0,0.725404,3.539221,11.473342)">
|
||||||
|
<path d="M43.789,56.563L11.031,56.562C3.922,56.562 0,52.672 0,45.594L0,11C0,3.922 3.906,0.031 10.516,0.031L67.422,0.031C74.547,0.031 78.469,3.922 78.469,11L78.469,39.642C76.475,37.218 73.921,35.265 71.016,33.991L71.016,11.879L54.327,26.905L60.012,32.59C57.573,32.984 55.276,33.831 53.211,35.039L49.46,31.288L45.719,34.656C43.594,36.578 41.609,37.391 39.219,37.391C36.844,37.391 34.844,36.578 32.734,34.656L28.993,31.29L11.188,49.108L11.328,49.109L43.533,49.109C43.374,50.11 43.292,51.133 43.292,52.174C43.292,53.679 43.464,55.148 43.789,56.563ZM7.453,43.594L24.132,26.915L7.453,11.906L7.453,43.594ZM12.279,7.484L36.734,29.547C37.547,30.266 38.359,30.641 39.219,30.641C40.094,30.641 40.906,30.266 41.719,29.547L66.174,7.484L12.279,7.484Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.692828,0,0,0.692828,-0,0.822733)">
|
||||||
|
<path d="M87.672,70C87.672,78.984 80.188,86.469 71.219,86.469C62.203,86.469 54.766,79.016 54.766,70C54.766,61 62.203,53.562 71.219,53.562C80.234,53.562 87.672,61 87.672,70ZM75.922,62.812L68.984,72.391L65.766,68.766C65.281,68.219 64.609,67.938 63.797,67.938C62.391,67.938 61.062,68.906 61.062,70.641C61.062,71.328 61.391,72.031 61.891,72.578L67.047,78.156C67.594,78.766 68.453,79 69.188,79C70.078,79 70.938,78.625 71.391,78.016L80.375,65.828C80.719,65.344 80.875,64.781 80.875,64.297C80.875,62.812 79.672,61.625 78.172,61.625C77.266,61.625 76.453,62.078 75.922,62.812Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
7
assets/icons/mailicon.svg
Normal file
7
assets/icons/mailicon.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.725404,0,0,0.725404,3.539221,11.473342)">
|
||||||
|
<path d="M11.031,56.562C3.922,56.562 0,52.672 0,45.594L0,11C0,3.922 3.906,0.031 10.516,0.031L67.422,0.031C74.547,0.031 78.469,3.922 78.469,11L78.469,45.594C78.469,52.672 74.562,56.562 67.953,56.562L11.031,56.562ZM7.453,43.594L24.132,26.915L7.453,11.906L7.453,43.594ZM67.279,49.107L49.46,31.288L45.719,34.656C43.594,36.578 41.609,37.391 39.219,37.391C36.844,37.391 34.844,36.578 32.734,34.656L28.993,31.29L11.188,49.108C11.234,49.109 11.281,49.109 11.328,49.109L67.109,49.109C67.167,49.109 67.223,49.109 67.279,49.107ZM71.016,11.879L54.327,26.905L71.016,43.594L71.016,11.879ZM12.279,7.484L36.734,29.547C37.547,30.266 38.359,30.641 39.219,30.641C40.094,30.641 40.906,30.266 41.719,29.547L66.174,7.484L12.279,7.484Z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -28,6 +28,7 @@ import { NotificationProvider } from './components/Dashboard/context/Notificatio
|
|||||||
import { ElectronProvider } from './components/Dashboard/context/ElectronContext.jsx'
|
import { ElectronProvider } from './components/Dashboard/context/ElectronContext.jsx'
|
||||||
import { MessageProvider } from './components/Dashboard/context/MessageContext.jsx'
|
import { MessageProvider } from './components/Dashboard/context/MessageContext.jsx'
|
||||||
import AuthCallback from './components/App/AuthCallback.jsx'
|
import AuthCallback from './components/App/AuthCallback.jsx'
|
||||||
|
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProductionRoutes,
|
ProductionRoutes,
|
||||||
@ -92,6 +93,10 @@ const AppContent = () => {
|
|||||||
path='/auth/callback'
|
path='/auth/callback'
|
||||||
element={<AuthCallback />}
|
element={<AuthCallback />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path='/email/notification'
|
||||||
|
element={<EmailNotificationTemplate />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path='/dashboard'
|
path='/dashboard'
|
||||||
element={
|
element={
|
||||||
|
|||||||
@ -259,7 +259,12 @@ const DashboardNavigation = () => {
|
|||||||
onClick={() => showSpotlight()}
|
onClick={() => showSpotlight()}
|
||||||
/>
|
/>
|
||||||
</KeyboardShortcut>
|
</KeyboardShortcut>
|
||||||
<Badge count={unreadCount} size='small' offset={[-4, 5]}>
|
<Badge
|
||||||
|
count={unreadCount}
|
||||||
|
size='small'
|
||||||
|
offset={[-5, 8]}
|
||||||
|
style={{ padding: 0, fontWeight: 600 }}
|
||||||
|
>
|
||||||
<KeyboardShortcut
|
<KeyboardShortcut
|
||||||
shortcut='alt+n'
|
shortcut='alt+n'
|
||||||
hint='ALT N'
|
hint='ALT N'
|
||||||
|
|||||||
@ -23,7 +23,9 @@ const Notification = ({
|
|||||||
showCard = true,
|
showCard = true,
|
||||||
showDelete = true,
|
showDelete = true,
|
||||||
showExtraInfo = true,
|
showExtraInfo = true,
|
||||||
inlineIcon = false
|
inlineIcon = false,
|
||||||
|
largeSpacing = false,
|
||||||
|
showSince = true
|
||||||
}) => {
|
}) => {
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
|
|
||||||
@ -192,10 +194,13 @@ const Notification = ({
|
|||||||
getMetadataDisplay(notification.metadata, notification.type)}
|
getMetadataDisplay(notification.metadata, notification.type)}
|
||||||
{showExtraInfo && (
|
{showExtraInfo && (
|
||||||
<>
|
<>
|
||||||
<Divider style={{ margin: 0 }} />
|
<Divider style={{ margin: largeSpacing ? '10px 0' : 0 }} />
|
||||||
<Flex justify='space-between' align='center'>
|
<Flex justify='space-between' align='center'>
|
||||||
{getNotificationTag(notification.type)}
|
{getNotificationTag(notification.type)}
|
||||||
<TimeDisplay dateTime={notification.createdAt} showSince={true} />
|
<TimeDisplay
|
||||||
|
dateTime={notification.createdAt}
|
||||||
|
showSince={showSince}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -236,7 +241,9 @@ Notification.propTypes = {
|
|||||||
showCard: PropTypes.bool,
|
showCard: PropTypes.bool,
|
||||||
showDelete: PropTypes.bool,
|
showDelete: PropTypes.bool,
|
||||||
showExtraInfo: PropTypes.bool,
|
showExtraInfo: PropTypes.bool,
|
||||||
inlineIcon: PropTypes.bool
|
inlineIcon: PropTypes.bool,
|
||||||
|
largeSpacing: PropTypes.bool,
|
||||||
|
showSince: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Notification
|
export default Notification
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import { useState, useEffect, useContext } from 'react'
|
|||||||
import { Button, message, Popover, Typography, Space, Flex } from 'antd'
|
import { Button, message, Popover, Typography, Space, Flex } from 'antd'
|
||||||
import { UserOutlined } from '@ant-design/icons'
|
import { UserOutlined } from '@ant-design/icons'
|
||||||
import BellIcon from '../../Icons/BellIcon'
|
import BellIcon from '../../Icons/BellIcon'
|
||||||
import NewMailIcon from '../../Icons/NewMailIcon'
|
import MailCheckIcon from '../../Icons/MailCheckIcon'
|
||||||
|
import MailIcon from '../../Icons/MailIcon'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
@ -176,11 +177,15 @@ const UserNotifierToggle = ({
|
|||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
icon={
|
icon={
|
||||||
<NewMailIcon
|
item.email ? (
|
||||||
style={{
|
<MailCheckIcon
|
||||||
color: item.email ? 'var(--color-primary)' : undefined
|
style={{
|
||||||
}}
|
color: 'var(--color-primary)'
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MailIcon />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
size='small'
|
size='small'
|
||||||
disabled={!isCurrentUser(item.user)}
|
disabled={!isCurrentUser(item.user)}
|
||||||
@ -204,7 +209,7 @@ const UserNotifierToggle = ({
|
|||||||
arrow={false}
|
arrow={false}
|
||||||
open={popoverOpen}
|
open={popoverOpen}
|
||||||
onOpenChange={setPopoverOpen}
|
onOpenChange={setPopoverOpen}
|
||||||
styles={{ body: { padding: '10px 15px' } }}
|
styles={{ body: { padding: '10px 12.5px 10px 15px' } }}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
{...buttonProps}
|
{...buttonProps}
|
||||||
|
|||||||
19
src/components/Email/EmailNotificationTemplate.css
Normal file
19
src/components/Email/EmailNotificationTemplate.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#email-notification-root {
|
||||||
|
padding: 10% 20px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-notification-card {
|
||||||
|
border-radius: 25px;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-notification-card-actions {
|
||||||
|
margin-top: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-notification-card-footer {
|
||||||
|
margin-top: 75px;
|
||||||
|
}
|
||||||
113
src/components/Email/EmailNotificationTemplate.jsx
Normal file
113
src/components/Email/EmailNotificationTemplate.jsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import './EmailNotificationTemplate.css'
|
||||||
|
import { Button, Card, ConfigProvider, Flex, theme, Typography } from 'antd'
|
||||||
|
import FarmControlLogo from '../Logos/FarmControlLogo'
|
||||||
|
import Notification from '../Dashboard/common/Notification'
|
||||||
|
import { useThemeContext } from '../Dashboard/context/ThemeContext'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email notification template - renders notification data for server-side HTML capture.
|
||||||
|
* Used by the API's sendEmailNotification with Puppeteer.
|
||||||
|
* Params: title, message, type, metadata (JSON string)
|
||||||
|
*/
|
||||||
|
const EmailNotificationTemplate = () => {
|
||||||
|
const { themeConfig } = useThemeContext()
|
||||||
|
const [searchParams] = useSearchParams()
|
||||||
|
const title = searchParams.get('title') || 'Notification'
|
||||||
|
const message = searchParams.get('message') || ''
|
||||||
|
const type = searchParams.get('type') || 'info'
|
||||||
|
const read = searchParams.get('read') || false
|
||||||
|
const email = searchParams.get('email') || ''
|
||||||
|
const createdAt = searchParams.get('createdAt') || new Date()
|
||||||
|
const updatedAt = searchParams.get('updatedAt') || new Date()
|
||||||
|
const origin = window.location.origin
|
||||||
|
|
||||||
|
let metadata = {}
|
||||||
|
try {
|
||||||
|
const metaStr = searchParams.get('metadata')
|
||||||
|
if (metaStr) metadata = JSON.parse(metaStr)
|
||||||
|
} catch {
|
||||||
|
// ignore parse errors
|
||||||
|
}
|
||||||
|
|
||||||
|
const lightThemeConfig = {
|
||||||
|
...themeConfig,
|
||||||
|
algorithm: theme.defaultAlgorithm,
|
||||||
|
components: {
|
||||||
|
...themeConfig.components,
|
||||||
|
Layout: { headerBg: '#ffffff' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNotifictionActions = () => {
|
||||||
|
switch (type) {
|
||||||
|
case 'editObject':
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
href={`${origin}/dashboard/${metadata.objectType}/${metadata.id}`}
|
||||||
|
type='primary'
|
||||||
|
>
|
||||||
|
View in Dashboard
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfigProvider theme={lightThemeConfig}>
|
||||||
|
<div id='email-notification-root' data-rendered='true'>
|
||||||
|
<FarmControlLogo
|
||||||
|
style={{
|
||||||
|
fontSize: '500px',
|
||||||
|
height: '40px',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
margin: '0 auto 60px 0'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Card className='email-notification-card'>
|
||||||
|
<Notification
|
||||||
|
showCard={false}
|
||||||
|
showDelete={false}
|
||||||
|
inlineIcon={false}
|
||||||
|
largeSpacing={true}
|
||||||
|
showSince={false}
|
||||||
|
notification={{
|
||||||
|
title: title,
|
||||||
|
message: message,
|
||||||
|
type: type,
|
||||||
|
metadata: metadata,
|
||||||
|
read: read,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Flex justify='center' className='email-notification-card-actions'>
|
||||||
|
{getNotifictionActions()}
|
||||||
|
</Flex>
|
||||||
|
{email && (
|
||||||
|
<Flex justify='center' className='email-notification-card-footer'>
|
||||||
|
<Text
|
||||||
|
type='secondary'
|
||||||
|
style={{
|
||||||
|
fontSize: '12px',
|
||||||
|
maxWidth: '300px',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
This email was sent to {email}. Please do not reply to this email.
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EmailNotificationTemplate
|
||||||
6
src/components/Icons/MailCheckIcon.jsx
Normal file
6
src/components/Icons/MailCheckIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/mailcheckicon.svg?react'
|
||||||
|
|
||||||
|
const MailCheckIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default MailCheckIcon
|
||||||
6
src/components/Icons/MailIcon.jsx
Normal file
6
src/components/Icons/MailIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/mailicon.svg?react'
|
||||||
|
|
||||||
|
const MailIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default MailIcon
|
||||||
Loading…
x
Reference in New Issue
Block a user