Skip to content

Commit 6e5fb90

Browse files
author
Lauren Pothuru
committed
Implement May's Redesign
1 parent 71105a2 commit 6e5fb90

6 files changed

Lines changed: 382 additions & 29 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// eslint-disable-next-line import/no-unresolved
2+
import { ApartmentWithId } from '@common/types/db-types';
3+
import { ReactElement } from 'react';
4+
import { Section, Heading } from '@react-email/components';
5+
import PropertyCard from './PropertyCard';
6+
7+
type Props = {
8+
topProperties: ApartmentWithId[];
9+
reviewedProperties: ApartmentWithId[];
10+
propertyReview: string;
11+
};
12+
13+
/**
14+
* Top Loved Properties Component
15+
*
16+
* This component displays a newsletter section about top loved properties. It displays
17+
* 2-3 properties that are highest rated and most reviewed.
18+
*
19+
* @component
20+
* @param {Object} props - Component properties.
21+
* @param {ApartmentWithId[]} props.topProperties - Array of top loved properties.
22+
* @param {ApartmentWithId[]} props.reviewedProperties - Array of most reviewed properties.
23+
* @param {string} props.propertyReview - A brief review about a featured property.
24+
* @returns {ReactElement} LandlordHighlight component.
25+
*/
26+
const LovedPropertiesSpotlight: React.FC<Props> = ({
27+
topProperties,
28+
reviewedProperties,
29+
propertyReview,
30+
}: Props): ReactElement => {
31+
// Property list section
32+
const PropertyList = ({ properties }: { properties: ApartmentWithId[] }) => (
33+
<>
34+
{properties.length > 0 ? (
35+
<div style={{ display: 'flex', marginBottom: '15px' }}>
36+
{properties.map((property) => (
37+
<Section
38+
style={{
39+
background: 'white',
40+
borderRadius: '6px',
41+
border: '0.406px solid #E8E8E8',
42+
width: '33%',
43+
padding: '7px',
44+
margin: '5px',
45+
}}
46+
>
47+
<PropertyCard key={property.id} property={property} />
48+
</Section>
49+
))}
50+
</div>
51+
) : (
52+
<p style={{ color: '#5D5D5D', fontSize: '13px', fontStyle: 'italic' }}>
53+
No properties to display
54+
</p>
55+
)}
56+
</>
57+
);
58+
59+
return (
60+
<Section
61+
style={{
62+
backgroundColor: '#F6F6F6',
63+
borderRadius: '8px',
64+
padding: '22px 20px',
65+
width: '100%',
66+
display: 'flex',
67+
flexDirection: 'column',
68+
alignItems: 'center',
69+
marginBottom: '20px',
70+
}}
71+
>
72+
<Heading
73+
style={{
74+
color: '#B94630',
75+
fontSize: '22.5px',
76+
fontWeight: '700',
77+
margin: '15px 0',
78+
marginLeft: '5px',
79+
}}
80+
>
81+
Top Loved Properties
82+
</Heading>
83+
<h2
84+
style={{
85+
color: '#000',
86+
fontSize: '14.5px',
87+
fontWeight: '600',
88+
margin: '0 0 10px 0',
89+
marginLeft: '5px',
90+
}}
91+
>
92+
Highest Rated
93+
</h2>
94+
<PropertyList properties={topProperties} />
95+
<h2
96+
style={{
97+
color: '#000',
98+
fontSize: '14.5px',
99+
fontWeight: '600',
100+
margin: '0 0 10px 0',
101+
marginLeft: '5px',
102+
}}
103+
>
104+
Most Reviewed
105+
</h2>
106+
<PropertyList properties={reviewedProperties} />
107+
<table style={{ width: '100%' }}>
108+
<tbody>
109+
<tr>
110+
<td style={{ verticalAlign: 'middle', justifyContent: 'center', textAlign: 'center' }}>
111+
<img
112+
src="https://i.postimg.cc/m21M8DVG/chatbubble-ellipses-outline.png"
113+
alt="chatbubble"
114+
style={{ width: '50%', height: 'auto' }}
115+
/>
116+
</td>
117+
<td style={{ verticalAlign: 'top' }}>
118+
<h2
119+
style={{
120+
color: '#000',
121+
fontSize: '14.5px',
122+
fontWeight: '600',
123+
margin: '0 0 5px 0',
124+
}}
125+
>
126+
Property Review
127+
</h2>
128+
<p style={{ color: '#5D5D5D', fontSize: '14.976px' }}>{propertyReview}</p>
129+
</td>
130+
</tr>
131+
</tbody>
132+
</table>
133+
</Section>
134+
);
135+
};
136+
137+
export default LovedPropertiesSpotlight;
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { ApartmentWithId } from '@common/types/db-types';
2+
import { ReactElement } from 'react';
3+
import { Section, Heading } from '@react-email/components';
4+
import PropertyCard from './PropertyCard';
5+
6+
type Props = {
7+
nearbyProperties: ApartmentWithId[];
8+
budgetProperties: ApartmentWithId[];
9+
};
10+
11+
/**
12+
* Recently Released/Vacant Component
13+
*
14+
* This component displays a comprehensive section about recently released or vacant properties. It displays
15+
* 2-3 properties that are close to campus and 2-3 that are budget friendly.
16+
*
17+
* @component
18+
* @param {Object} props - Component properties.
19+
* @param {ApartmentWithId[]} props.nearbyProperties - Array of nearby properties.
20+
* @param {ApartmentWithId[]} props.budgetProperties - Array of budget-friendly properties.
21+
* @returns {ReactElement} LandlordHighlight component.
22+
*/
23+
const RecentPropertiesSpotlight: React.FC<Props> = ({
24+
nearbyProperties,
25+
budgetProperties,
26+
}: Props): ReactElement => {
27+
// Property list section
28+
const PropertyList = ({ properties }: { properties: ApartmentWithId[] }) => (
29+
<>
30+
{properties.length > 0 ? (
31+
<div style={{ display: 'flex', marginBottom: '15px' }}>
32+
{properties.map((property) => (
33+
<Section
34+
style={{
35+
background: 'white',
36+
borderRadius: '6px',
37+
border: '0.406px solid #E8E8E8',
38+
width: '33%',
39+
padding: '7px',
40+
margin: '5px',
41+
}}
42+
>
43+
<PropertyCard key={property.id} property={property} />
44+
</Section>
45+
))}
46+
</div>
47+
) : (
48+
<p style={{ color: '#5D5D5D', fontSize: '13px', fontStyle: 'italic' }}>
49+
No properties to display
50+
</p>
51+
)}
52+
</>
53+
);
54+
55+
return (
56+
<Section
57+
style={{
58+
backgroundColor: '#F6F6F6',
59+
borderRadius: '8px',
60+
padding: '22px 20px',
61+
width: '100%',
62+
display: 'flex',
63+
flexDirection: 'column',
64+
alignItems: 'center',
65+
marginBottom: '20px',
66+
}}
67+
>
68+
<Heading
69+
style={{
70+
color: '#B94630',
71+
fontSize: '22.5px',
72+
fontWeight: '700',
73+
margin: '15px 0',
74+
marginLeft: '5px',
75+
}}
76+
>
77+
Recently Released / Vacant
78+
</Heading>
79+
<h2
80+
style={{
81+
color: '#000',
82+
fontSize: '14.5px',
83+
fontWeight: '600',
84+
margin: '0 0 10px 0',
85+
marginLeft: '5px',
86+
}}
87+
>
88+
Close to Campus
89+
</h2>
90+
<PropertyList properties={nearbyProperties} />
91+
<h2
92+
style={{
93+
color: '#000',
94+
fontSize: '14.5px',
95+
fontWeight: '600',
96+
margin: '0 0 10px 0',
97+
marginLeft: '5px',
98+
}}
99+
>
100+
Budget-Friendly
101+
</h2>
102+
<PropertyList properties={budgetProperties} />
103+
</Section>
104+
);
105+
};
106+
107+
export default RecentPropertiesSpotlight;

backend/scripts/email/emailService.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:3000';
2020
type EmailCampaignOptions = {
2121
subject?: string;
2222
toEmail?: string;
23-
recentLandlordPropertyIDs?: string[];
24-
lovedPropertyIds?: string[];
23+
nearbyPropertyIDs?: string[];
24+
budgetPropertyIDs?: string[];
2525
recentAreaPropertyIDs?: string[];
26+
lovedPropertyIDs?: string[];
27+
reviewedPropertyIDs?: string[];
2628
};
2729

2830
/**
@@ -32,9 +34,11 @@ type EmailCampaignOptions = {
3234
* @param options - Configuration options for the email campaign
3335
* @param options.subject - Email subject line (default: 'Check Out These New Apartments!')
3436
* @param options.toEmail - Primary recipient email address (default: 'laurenpothuru@gmail.com')
35-
* @param options.recentLandlordPropertyIDs - List of property IDs to feature as recent landlord properties
36-
* @param options.lovedPropertyIds - List of property IDs to feature as loved properties
37+
* @param options.nearbyPropertyIDs - List of property IDs to feature as nearby available properties
38+
* @param options.budgetPropertyIDs - List of property IDs to feature as budget-friendly properties
3739
* @param options.recentAreaPropertyIDs - List of property IDs to feature as recent area properties
40+
* @param options.lovedPropertyIDs - List of property IDs to feature as top loved properties
41+
* @param options.reviewedPropertyIDs - List of property IDs to feature as most reviewed properties
3842
* @returns Promise that resolves when all email batches have been sent
3943
*/
4044
const sendEmailCampaign = async (options: EmailCampaignOptions = {}): Promise<void> => {
@@ -53,8 +57,6 @@ const sendEmailCampaign = async (options: EmailCampaignOptions = {}): Promise<vo
5357
return;
5458
}
5559

56-
// const { API_BASE_URL } = process.env;
57-
5860
/**
5961
* getPropertiesByIds
6062
* Fetches apartment data for a given list of property IDs.
@@ -82,19 +84,27 @@ const sendEmailCampaign = async (options: EmailCampaignOptions = {}): Promise<vo
8284
};
8385

8486
// Loads chosen properties
85-
const recentLandlordProperties = options.recentLandlordPropertyIDs
86-
? await getPropertiesByIds(options.recentLandlordPropertyIDs)
87+
const nearbyProperties = options.nearbyPropertyIDs
88+
? await getPropertiesByIds(options.nearbyPropertyIDs)
8789
: [];
88-
const lovedProperties = options.lovedPropertyIds
89-
? await getPropertiesByIds(options.lovedPropertyIds)
90+
const budgetProperties = options.budgetPropertyIDs
91+
? await getPropertiesByIds(options.budgetPropertyIDs)
9092
: [];
9193
const recentAreaProperties = options.recentAreaPropertyIDs
9294
? await getPropertiesByIds(options.recentAreaPropertyIDs)
9395
: [];
96+
const lovedProperties = options.lovedPropertyIDs
97+
? await getPropertiesByIds(options.lovedPropertyIDs)
98+
: [];
99+
const reviewedProperties = options.reviewedPropertyIDs
100+
? await getPropertiesByIds(options.reviewedPropertyIDs)
101+
: [];
94102

95-
console.log(
96-
`Fetched ${recentLandlordProperties.length} recent properties (landlord highlight), ${lovedProperties.length} loved properties (landlord highlight), and ${recentAreaProperties.length} recent properties (area spotlight).`
97-
);
103+
console.log(`Fetched ${nearbyProperties.length} nearby properties (recently released spotlight)`);
104+
console.log(`Fetched ${budgetProperties.length} budget properties (recently released spotlight)`);
105+
console.log(`Fetched ${recentAreaProperties.length} recent area properties (area spotlight)`);
106+
console.log(`Fetched ${lovedProperties.length} loved properties (loved spotlight)`);
107+
console.log(`Fetched ${reviewedProperties.length} reviewed properties (loved spotlight)`);
98108

99109
const resend = new Resend(apiKey);
100110

@@ -135,9 +145,11 @@ const sendEmailCampaign = async (options: EmailCampaignOptions = {}): Promise<vo
135145
// bcc: bccEmails,
136146
subject,
137147
react: React.createElement(GenerateNewsletter, {
138-
recentLandlordProperties,
139-
lovedProperties,
148+
nearbyProperties,
149+
budgetProperties,
140150
recentAreaProperties,
151+
lovedProperties,
152+
reviewedProperties,
141153
}),
142154
});
143155

@@ -169,9 +181,11 @@ const sendEmailCampaign = async (options: EmailCampaignOptions = {}): Promise<vo
169181
to: 'laurenpothuru@gmail.com',
170182
subject,
171183
react: React.createElement(GenerateNewsletter, {
172-
recentLandlordProperties,
173-
lovedProperties,
184+
nearbyProperties,
185+
budgetProperties,
174186
recentAreaProperties,
187+
lovedProperties,
188+
reviewedProperties,
175189
}),
176190
});
177191
if (error) {
@@ -202,8 +216,10 @@ async function main() {
202216
await sendEmailCampaign({
203217
subject: 'New Apartment Listings Available!',
204218
recentAreaPropertyIDs: ['12', '2', '24'],
205-
lovedPropertyIds: ['23', '24', '24'],
206-
recentLandlordPropertyIDs: ['14', '23', '24'],
219+
budgetPropertyIDs: ['23', '24', '24'],
220+
nearbyPropertyIDs: ['14', '23', '24'],
221+
reviewedPropertyIDs: ['1', '2', '3'],
222+
lovedPropertyIDs: ['4', '5', '6'],
207223
});
208224

209225
console.log('Campaign completed successfully!');

0 commit comments

Comments
 (0)