Skip to content

Commit 892c3b4

Browse files
committed
more progress
1 parent 0de5b18 commit 892c3b4

File tree

6 files changed

+85
-55
lines changed

6 files changed

+85
-55
lines changed

exercises/99.final/01.solution.final/src/components/icon.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,15 @@ export function Icon({
4848
}) {
4949
if (children) {
5050
return (
51-
<span className={`inline-flex ${childrenSizeClassName[size]}`}>
52-
<Icon name={name} size={size} className={className} {...props} />
51+
<span
52+
className={`inline-flex items-center ${childrenSizeClassName[size]}`}
53+
>
54+
<Icon
55+
name={name}
56+
size={size}
57+
className={clsx(className, 'flex-none self-start')}
58+
{...props}
59+
/>
5360
{children}
5461
</span>
5562
)

exercises/99.final/01.solution.final/src/data.json

+13-13
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,26 @@
66
"countryCode": "+1",
77
"phone": "5551234567",
88
"timeZone": "America/Denver",
9-
"schedule": { "cron": "10 10 * * 4" },
9+
"schedule": { "cron": "10 10 * * 1,3,5" },
1010
"messages": [
1111
{
1212
"id": "1",
13-
"text": "Thank you for being my partner in every adventure and my comfort in every storm.\n\nI love you more than you know.",
13+
"content": "Thank you for being my partner in every adventure and my comfort in every storm.\n\nI love you more than you know.",
1414
"sentAt": "2044-06-06T10:00:00Z"
1515
},
1616
{
1717
"id": "2",
18-
"text": "You are my best friend, my confidant, and my greatest support. Thank you for being you.",
18+
"content": "You are my best friend, my confidant, and my greatest support. Thank you for being you.",
1919
"sentAt": "2044-06-13T10:00:00Z"
2020
},
2121
{
2222
"id": "3",
23-
"text": "Thank you for all the sacrifices you make for our family. Your hard work and dedication mean the world to me.",
23+
"content": "Thank you for all the sacrifices you make for our family. Your hard work and dedication mean the world to me.",
2424
"sentAt": null
2525
},
2626
{
2727
"id": "4",
28-
"text": "Thank you for being my rock and always supporting me. I am grateful for your unwavering love.",
28+
"content": "Thank you for being my rock and always supporting me. I am grateful for your unwavering love.",
2929
"sentAt": null
3030
}
3131
]
@@ -40,12 +40,12 @@
4040
"messages": [
4141
{
4242
"id": "1",
43-
"text": "Happy Mother's Day! Thank you for all the love, guidance, and support you've given me throughout my life.",
43+
"content": "Happy Mother's Day! Thank you for all the love, guidance, and support you've given me throughout my life.",
4444
"sentAt": "2044-05-12T10:00:00Z"
4545
},
4646
{
4747
"id": "2",
48-
"text": "Just wanted to let you know I'm thinking of you and I love you. Your strength and wisdom inspire me every day.",
48+
"content": "Just wanted to let you know I'm thinking of you and I love you. Your strength and wisdom inspire me every day.",
4949
"sentAt": null
5050
}
5151
]
@@ -60,12 +60,12 @@
6060
"messages": [
6161
{
6262
"id": "1",
63-
"text": "Happy Father's Day! Thank you for being the best role model and for always believing in me.",
63+
"content": "Happy Father's Day! Thank you for being the best role model and for always believing in me.",
6464
"sentAt": "2044-06-16T10:00:00Z"
6565
},
6666
{
6767
"id": "2",
68-
"text": "I really appreciate all the life lessons you've taught me. You've shaped who I am today.",
68+
"content": "I really appreciate all the life lessons you've taught me. You've shaped who I am today.",
6969
"sentAt": "2044-06-17T10:00:00Z"
7070
}
7171
]
@@ -80,12 +80,12 @@
8080
"messages": [
8181
{
8282
"id": "1",
83-
"text": "Happy birthday to my amazing sister! Wishing you all the joy and success in the world.",
83+
"content": "Happy birthday to my amazing sister! Wishing you all the joy and success in the world.",
8484
"sentAt": "2044-05-20T09:00:00Z"
8585
},
8686
{
8787
"id": "2",
88-
"text": "Can't wait for our weekend getaway! It's going to be so much fun catching up.",
88+
"content": "Can't wait for our weekend getaway! It's going to be so much fun catching up.",
8989
"sentAt": null
9090
}
9191
]
@@ -100,12 +100,12 @@
100100
"messages": [
101101
{
102102
"id": "1",
103-
"text": "Thank you for all your wonderful stories and the delicious recipes you've shared with me. They keep our family traditions alive.",
103+
"content": "Thank you for all your wonderful stories and the delicious recipes you've shared with me. They keep our family traditions alive.",
104104
"sentAt": "2044-06-02T11:00:00Z"
105105
},
106106
{
107107
"id": "2",
108-
"text": "Looking forward to Sunday lunch together. Your cooking always makes everything better.",
108+
"content": "Looking forward to Sunday lunch together. Your cooking always makes everything better.",
109109
"sentAt": null
110110
}
111111
]

exercises/99.final/01.solution.final/src/data.ts

+29-15
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ import CronExpressionParser from 'cron-parser'
22
import * as rawData from '#src/data.json'
33

44
export const recipients = rawData.recipients.map((recipient) => {
5-
const cronInstancePast = recipient.schedule?.cron
6-
? CronExpressionParser.parse(recipient.schedule.cron, {
5+
// the fallback is just to calculate previous messages in the event
6+
// the schedule is paused
7+
const scheduleCron = recipient.schedule?.cron ?? '0 15 * * 3'
8+
const cronInstancePast = scheduleCron
9+
? CronExpressionParser.parse(scheduleCron, {
710
tz: recipient.timeZone,
811
})
912
: null
13+
14+
// don't use the fallback because we shouldn't have a cron for future messages
1015
const cronInstanceFuture = recipient.schedule?.cron
1116
? CronExpressionParser.parse(recipient.schedule.cron, {
1217
tz: recipient.timeZone,
@@ -15,9 +20,25 @@ export const recipients = rawData.recipients.map((recipient) => {
1520
let next = cronInstanceFuture?.next()
1621

1722
// make the mocked messages more realistic timing wise
18-
const processedMessages = recipient.messages.map((message) => {
19-
// If message doesn't have a sentAt, keep it as null
20-
if (!message.sentAt || !cronInstancePast) {
23+
const pastMessages = recipient.messages
24+
.filter((m) => m.sentAt && cronInstancePast)
25+
.map((message) => {
26+
if (!cronInstancePast) throw new Error('cronInstancePast is null')
27+
28+
const sentAt = cronInstancePast?.prev().toDate()
29+
30+
return {
31+
...message,
32+
sentAt,
33+
status: 'sent',
34+
scheduledAt: sentAt,
35+
}
36+
})
37+
38+
const futureMessages = recipient.messages
39+
.filter((m) => !m.sentAt || !cronInstancePast)
40+
.reverse()
41+
.map((message) => {
2142
const scheduledAt = next?.toDate()
2243
next = cronInstanceFuture?.next()
2344
return {
@@ -26,16 +47,9 @@ export const recipients = rawData.recipients.map((recipient) => {
2647
sentAt: null,
2748
scheduledAt,
2849
}
29-
}
30-
const sentAt = cronInstancePast.prev().toDate()
31-
32-
return {
33-
...message,
34-
sentAt,
35-
status: 'sent',
36-
scheduledAt: sentAt,
37-
}
38-
})
50+
})
51+
52+
const processedMessages = [...pastMessages, ...futureMessages]
3953

4054
// Sort messages: null values last, otherwise by sentAt date (oldest first)
4155
const sortedMessages = processedMessages.sort((a, b) => {

exercises/99.final/01.solution.final/src/routes/app/recipients/$id.tsx

+20-19
Original file line numberDiff line numberDiff line change
@@ -82,24 +82,7 @@ export function RecipientRoute() {
8282
<span className="text-sm opacity-80 md:text-base">
8383
Sent on{' '}
8484
{message.sentAt
85-
? new Date(message.sentAt).toLocaleDateString(
86-
'en-US',
87-
{
88-
weekday: 'short',
89-
month: 'short',
90-
day: 'numeric',
91-
year: 'numeric',
92-
},
93-
)
94-
: 'Unknown date'}
95-
</span>
96-
</Icon>
97-
) : (
98-
<Icon name="Clock">
99-
<span className="text-sm opacity-80 md:text-base">
100-
Scheduled for{' '}
101-
{nextScheduledTime
102-
? nextScheduledTime.toLocaleDateString('en-US', {
85+
? message.sentAt.toLocaleDateString('en-US', {
10386
weekday: 'short',
10487
month: 'short',
10588
day: 'numeric',
@@ -108,6 +91,24 @@ export function RecipientRoute() {
10891
: 'Unknown date'}
10992
</span>
11093
</Icon>
94+
) : (
95+
<Icon name="Clock">
96+
{nextScheduledTime ? (
97+
<span className="text-sm opacity-80 md:text-base">
98+
Scheduled for{' '}
99+
{nextScheduledTime.toLocaleDateString('en-US', {
100+
weekday: 'short',
101+
month: 'short',
102+
day: 'numeric',
103+
year: 'numeric',
104+
})}
105+
</span>
106+
) : (
107+
<span className="text-sm opacity-80 md:text-base">
108+
Schedule paused
109+
</span>
110+
)}
111+
</Icon>
111112
)}
112113
</div>
113114
<Button
@@ -124,7 +125,7 @@ export function RecipientRoute() {
124125
</div>
125126
{/* break-words does not work for long strings of unbroken text */}
126127
<p className="text-sm [word-break:break-word] whitespace-pre-line md:text-base">
127-
{message.text}
128+
{message.content}
128129
</p>
129130
</div>
130131
)

exercises/99.final/01.solution.final/src/routes/app/recipients/layout.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ export function RecipientsLayout() {
4444
</div>
4545
)}
4646
</NavLink>
47-
{recipient.messages.every((m) => m.sentAt) ? (
47+
{recipient.messages.some(
48+
(m) => m.status === 'scheduled',
49+
) ? null : (
4850
<Icon
4951
name="ExclamationCircle"
5052
className="text-danger-foreground"
5153
title="no messages scheduled"
5254
/>
53-
) : null}
55+
)}
5456
</div>
5557
))}
5658
</div>

exercises/99.final/01.solution.final/src/routes/app/recipients/recipient-editor.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,15 @@ export function RecipientEditor({
9898
<label className="mb-2 block">Create a Schedule</label>
9999
<div className="grid grid-cols-2 gap-4">
100100
<select
101-
name="scheduleDay"
101+
name="scheduleDays"
102+
multiple
102103
className="w-full rounded-lg border p-3"
103104
required
104-
defaultValue={recipient?.nextScheduledAt?.getDay()}
105+
defaultValue={recipient?.schedule?.cron
106+
?.split(' ')[4]
107+
.split(',')
108+
.map(String)}
105109
>
106-
<option value="">Select Day</option>
107110
<option value="1">Monday</option>
108111
<option value="2">Tuesday</option>
109112
<option value="3">Wednesday</option>
@@ -148,7 +151,10 @@ export function RecipientEditor({
148151

149152
<div className="mt-2 flex items-center gap-2">
150153
<Icon name="Info">
151-
<p>Your messages will arrive every week at this day and time</p>
154+
<p>
155+
Your messages will arrive every week on the selected days at this
156+
time
157+
</p>
152158
</Icon>
153159
</div>
154160

0 commit comments

Comments
 (0)