Skip to content

Commit 6e7b1a5

Browse files
authored
Merge pull request #20 from m8rmclaren/main
EagleSat-2 project & update experience
2 parents 09d186f + 319abd6 commit 6e7b1a5

File tree

4 files changed

+202
-91
lines changed

4 files changed

+202
-91
lines changed
2.1 MB
Loading

static/output.css

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@
245245
.me-2 {
246246
margin-inline-end: calc(var(--spacing) * 2);
247247
}
248+
.mt-10 {
249+
margin-top: calc(var(--spacing) * 10);
250+
}
248251
.mt-auto {
249252
margin-top: auto;
250253
}
@@ -323,9 +326,6 @@
323326
.w-28 {
324327
width: calc(var(--spacing) * 28);
325328
}
326-
.w-36 {
327-
width: calc(var(--spacing) * 36);
328-
}
329329
.w-full {
330330
width: 100%;
331331
}
@@ -335,9 +335,15 @@
335335
.max-w-3xl {
336336
max-width: var(--container-3xl);
337337
}
338+
.min-w-40 {
339+
min-width: calc(var(--spacing) * 40);
340+
}
338341
.min-w-\[2rem\] {
339342
min-width: 2rem;
340343
}
344+
.shrink-0 {
345+
flex-shrink: 0;
346+
}
341347
.flex-grow {
342348
flex-grow: 1;
343349
}
@@ -408,6 +414,9 @@
408414
.self-end {
409415
align-self: flex-end;
410416
}
417+
.justify-self-start {
418+
justify-self: flex-start;
419+
}
411420
.rounded {
412421
border-radius: 0.25rem;
413422
}
@@ -802,11 +811,6 @@
802811
height: calc(var(--spacing) * 96);
803812
}
804813
}
805-
.md\:w-2\/5 {
806-
@media (width >= 48rem) {
807-
width: calc(2/5 * 100%);
808-
}
809-
}
810814
.md\:w-96 {
811815
@media (width >= 48rem) {
812816
width: calc(var(--spacing) * 96);

template/view/experience.templ

Lines changed: 96 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
package view
22

3-
import (
4-
"github.com/m8rmclaren/website/template/components"
5-
"html"
6-
"regexp"
7-
"strings"
8-
)
3+
import "github.com/m8rmclaren/website/template/components"
94

105
templ keyfactorProductOwnerDescription() {
116
<p>
@@ -36,9 +31,8 @@ templ keyfactorIntegrationEngineerDescription() {
3631
hover:text-blue-800 hover:decoration-blue-600 hover:underline-offset-4
3732
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
3833
>Simple Environment Service</a>
39-
, a full-stack platform that lets teams spin up demo, development, and QA environments for Keyfactor’s enterprise products with an
40-
@components.InlineBold("intuitive UI.")
41-
The UI and backend fully abstract the underlying infrastructure and product dependencies to deliver a reliable and repeatable workflow. The system was adopted by
34+
, a full-stack platform that lets teams spin up batteries-included demo, development, and QA environments for Keyfactor’s enterprise products in under 10 minutes.
35+
The service abstracts the deployment & configuration of applications using Kubernetes under the hood to deliver a reliable and repeatable workflow. The system was adopted by
4236
@components.InlineBold("20%+")
4337
of staff, and scaled to
4438
@components.InlineBold("100+ environments")
@@ -80,105 +74,128 @@ templ keyfactorIntegrationEngineerInternDescription() {
8074
</p>
8175
}
8276

77+
templ eaglesatBusSystemsIntegrationTeamLead() {
78+
<p>
79+
Designed mission profiles for each sub-system on board EagleSat-2 to implement the required satellite functionality. Mission profiles are implemented with finite state machines that maintain nominal satellite operation while EagleSat-2 is not in contact with the ground station on ERAU's campus.
80+
</p>
81+
<p>
82+
Configured and managed the On-Board Computer (OBC) real-time operating system (FreeRTOS with CMSIS RTOS V2 API) and developed low-level drivers for serial communication (CAN, UART, I2C).
83+
</p>
84+
}
85+
86+
templ eaglesatCommunicationsTeamLead() {
87+
<p>
88+
Led the communications sub-system team for the EagleSat mission, leading the design, development, and integration of a full-stack satellite communication framework to implement command and control (C2) over EagleSat-2.
89+
</p>
90+
<p>
91+
Designed and implemented a headless UHF ground station and a packet-radio communication application (C++) to handle packet routing between the primary C2 platform and the satellite, provide connection statistics, detect satellite range, and calculate Doppler shift.
92+
</p>
93+
}
94+
8395
templ homeAdvisorITInternDescription() {
8496
<p>
85-
First-line IT support for a team of over 2,800 at HomeAdvisor.
97+
First-line IT support for a team of over 2,800 at HomeAdvisor.
8698
</p>
8799
<p>
88-
Coordinated incident response to proper teams during outages.
100+
Coordinated incident response to proper teams during outages, administered Active Directory & Exchange environments, provisioned new-hire workstations & software.
89101
</p>
90102
}
91103

92104
templ erauSoftwareEngineeringDescription() {
93105
<p>
94-
Space Track
106+
@components.InlineBold("GPA: ")
107+
3.89 (magna cum laude)
108+
</p>
109+
<p>
110+
@components.InlineBold("Activities & Societies: ")
111+
EagleSat Engineering Team Lead, UAS Club Secretary, Cyber Defense Club, Computer Science II TA
95112
</p>
96113
}
97114

98115
templ Experience() {
99-
<div class="flex flex-col gap-2">
100-
<div class="flex flex-col md:flex-row gap-8">
101-
<h2 class="text-3xl font-bold w-36">Industry</h2>
102-
<div class="flex flex-col gap-8">
103-
@experienceCard(
104-
"Keyfactor",
105-
"Product Owner",
106-
"Remote",
107-
"November 2024-August 2025",
108-
keyfactorProductOwnerDescription(),
109-
)
110-
@experienceCard(
111-
"Keyfactor",
112-
"Software Engineer - Integrations",
113-
"Remote",
114-
"May 2023-November 2024",
115-
keyfactorIntegrationEngineerDescription(),
116-
)
117-
@experienceCard(
118-
"Keyfactor",
119-
"Software Engineer Intern - Integrations",
120-
"Remote",
121-
"June 2020-May 2023",
122-
keyfactorIntegrationEngineerInternDescription(),
123-
)
124-
@experienceCard(
125-
"Home Advisor",
126-
"Service Desk Intern",
127-
"Denver, CO",
128-
"June 2017-August 2019",
129-
homeAdvisorITInternDescription(),
130-
)
131-
</div>
116+
<div class="flex flex-col md:flex-row gap-8">
117+
<div class="min-w-40 shrink-0">
118+
<h2 class="text-3xl font-bold">Industry</h2>
119+
</div>
120+
<div class="flex flex-col gap-8">
121+
@experienceCard(
122+
"Keyfactor",
123+
"Product Management",
124+
"Product Owner",
125+
"Remote",
126+
"Nov 2024-Aug 2025",
127+
keyfactorProductOwnerDescription(),
128+
)
129+
@experienceCard(
130+
"Keyfactor",
131+
"Integrations",
132+
"Software Engineer",
133+
"Remote",
134+
"May 2023-Nov 2024",
135+
keyfactorIntegrationEngineerDescription(),
136+
)
137+
@experienceCard(
138+
"Keyfactor",
139+
"Integrations",
140+
"Software Engineer Intern",
141+
"Remote",
142+
"Jun 2020-May 2023",
143+
keyfactorIntegrationEngineerInternDescription(),
144+
)
145+
@experienceCard(
146+
"EagleSat",
147+
"",
148+
"Bus Systems Integration Manager",
149+
"Prescott, AZ",
150+
"Sep 2022-May 2023",
151+
eaglesatBusSystemsIntegrationTeamLead(),
152+
)
153+
@experienceCard(
154+
"EagleSat",
155+
"",
156+
"Communications & Ground Station Team Lead",
157+
"Prescott, AZ",
158+
"May 2021-Sep 2022",
159+
eaglesatCommunicationsTeamLead(),
160+
)
161+
@experienceCard(
162+
"Home Advisor",
163+
"Technical Operations",
164+
"Service Desk Intern",
165+
"Denver, CO",
166+
"Jun 2017-Aug 2019",
167+
homeAdvisorITInternDescription(),
168+
)
132169
</div>
133170
</div>
134171
}
135172

136173
templ Education() {
137-
<div class="flex flex-col gap-2">
138-
<div class="flex flex-col md:flex-row gap-8">
139-
<h2 class="text-3xl font-bold w-36">Academia</h2>
174+
<div class="flex flex-col md:flex-row gap-8">
175+
<div class="min-w-40 shrink-0">
176+
<h2 class="text-3xl font-bold">Academia</h2>
177+
</div>
178+
<div class="flex flex-col gap-8 justify-self-start">
140179
@experienceCard(
141180
"Embry-Riddle Aeronautical University",
181+
"",
142182
"BS, Software Engineering",
143183
"Prescott, AZ",
144-
"August 2019-May 2023",
184+
"Aug 2019-May 2023",
145185
erauSoftwareEngineeringDescription(),
146186
)
147187
</div>
148188
</div>
149189
}
150190

151-
var boldRe = regexp.MustCompile(`\*\*(.+?)\*\*`)
152-
153-
func parseBoldTempl(line string) templ.Component {
154-
var b strings.Builder
155-
last := 0
156-
157-
for _, m := range boldRe.FindAllStringSubmatchIndex(line, -1) {
158-
start, end := m[0], m[1]
159-
innerStart, innerEnd := m[2], m[3]
160-
161-
// Write text before **...**, escaped.
162-
b.WriteString(html.EscapeString(line[last:start]))
163-
164-
// Write the bold span with escaped inner text.
165-
b.WriteString(`<span class="font-bold">`)
166-
b.WriteString(html.EscapeString(line[innerStart:innerEnd]))
167-
b.WriteString(`</span>`)
168-
169-
last = end
170-
}
171-
172-
// Write the trailing text, escaped.
173-
b.WriteString(html.EscapeString(line[last:]))
174-
175-
// Important: tell Templ this is safe HTML so it won't escape the tags.
176-
return templ.Raw(b.String())
177-
}
178-
179-
templ experienceCard(company, title, location, duration string, description templ.Component) {
191+
templ experienceCard(company, team, title, location, duration string, description templ.Component) {
180192
<div class="flex flex-col gap-1">
181-
<h2 class="text-xl font-bold">{ company } &#x2022; { title }</h2>
193+
<h3 class="text-xl font-bold">
194+
{ company } &#x2022; { title }
195+
if team != "" {
196+
&mdash; { team }
197+
}
198+
</h3>
182199
<p class="font-medium">
183200
{ location } &#x2022; { duration }
184201
</p>

template/view/projects.templ

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,70 @@
11
package view
22

3-
import "github.com/m8rmclaren/website/template/view/blog"
3+
import (
4+
"github.com/m8rmclaren/website/template/components"
5+
"github.com/m8rmclaren/website/template/view/blog"
6+
"strings"
7+
)
48

59
templ Projects() {
6-
<div class="flex flex-col gap-4">
10+
<div class="flex flex-col">
711
<h2 class="text-3xl font-bold">Projects I'm Proud of</h2>
812
<div class="flex flex-col gap-8">
9-
@SESProject()
13+
@projectCard(project{
14+
projectName: "EagleSat-2 (3U CubeSat)",
15+
tagline: "Testing memory resilience in the harshness of Earth orbit",
16+
imageSrc: "/images/eaglesat/eaglesat-2-orbiting-earth.png",
17+
imagePlaceholder: "EagleSat-2 Orbiting Earth Render",
18+
articleHref: "https://www.linkedin.com/feed/update/urn:li:activity:7379209278949289985/",
19+
articleButtonText: "Read the Post",
20+
currentStatusText: "Launched on September 14, 2025 aboard the Cygnus XL NG-23 mission on a SpaceX Falcon 9",
21+
})
22+
@projectCard(project{
23+
projectName: "Simple Environment Service",
24+
tagline: "Scalable, on-demand application provisioning using Kubernetes",
25+
imageSrc: "/images/ses/framed_ses_dashboard_browser_2.png",
26+
imagePlaceholder: "SES Dashboard Markup",
27+
articleHref: "/blog/simple-environment-service",
28+
articleButtonText: "Read the Article",
29+
currentStatusText: "",
30+
})
31+
</div>
32+
</div>
33+
}
34+
35+
templ EagleSat2Project() {
36+
<div class="flex flex-col items-center justify-center text-center rounded-2xl bg-gray-200 dark:bg-gray-700 shadow-2xl min-h-96">
37+
<h2 class="lg:pt-20 pt-10 w-4/5 text-2xl font-medium">
38+
Testing memory resilience in the harshness of Earth orbit
39+
</h2>
40+
<h3 class="pt-4 lg:pt-8 text-base font-medium">
41+
EagleSat-2
42+
</h3>
43+
<div class="pt-12 w-3/5">
44+
@components.Image(
45+
"/images/eaglesat/eaglesat-2-orbiting-earth.png",
46+
"EagleSat-2 Orbiting Earth Render",
47+
"rounded-2xl",
48+
)
49+
</div>
50+
<p class="pt-4 text-base font-medium">
51+
Currently: orbiting Earth
52+
</p>
53+
<div class="pt-8 pb-4">
54+
<a
55+
href="/blog/simple-environment-service"
56+
rel="noopener"
57+
class="text-white bg-[#4876A2] hover:bg-[#4876A2]/90 font-medium rounded-lg text-sm px-5 py-2.5 text-center flex flex-row gap-2 items-center dark:hover:bg-[#050708]/30 me-2 mb-2 border border-gray-300 hover:border-gray-500 hover:px-6 transition-all duration-200"
58+
>
59+
Read the Post
60+
</a>
1061
</div>
1162
</div>
1263
}
1364

1465
templ SESProject() {
1566
<div class="flex flex-col items-center justify-center text-center rounded-2xl bg-gray-200 dark:bg-gray-700 shadow-2xl min-h-96">
16-
<h2 class="lg:pt-20 pt-10 w-4/5 md:w-2/5 text-2xl font-medium">
67+
<h2 class="lg:pt-20 pt-10 w-4/5 text-2xl font-medium">
1768
Scalable, On-Demand Application Provisioning using Kubernetes
1869
</h2>
1970
<h3 class="pt-4 lg:pt-8 text-base font-medium">
@@ -31,3 +82,42 @@ templ SESProject() {
3182
</div>
3283
</div>
3384
}
85+
86+
type project struct {
87+
projectName, tagline, imageSrc, imagePlaceholder, articleHref, articleButtonText, currentStatusText string
88+
}
89+
90+
templ projectCard(project project) {
91+
<div class="mt-10 flex flex-col items-center justify-center text-center rounded-2xl bg-gray-200 dark:bg-gray-700 shadow-2xl min-h-96">
92+
<h2 class="pt-10 w-4/5 text-2xl font-medium">
93+
{ project.tagline }
94+
</h2>
95+
<h3 class="pt-4 text-base font-medium">
96+
{ project.projectName }
97+
</h3>
98+
<div class="pt-12 w-3/5 flex justify-center">
99+
@components.Image(
100+
project.imageSrc,
101+
project.imagePlaceholder,
102+
"rounded-2xl",
103+
)
104+
</div>
105+
if project.currentStatusText != "" {
106+
<p class="pt-4 w-4/5 text-base font-medium">
107+
{ project.currentStatusText }
108+
</p>
109+
}
110+
<div class="pt-8 pb-4">
111+
<a
112+
href={ project.articleHref }
113+
if strings.HasPrefix(project.articleHref, "http") {
114+
target="_blank"
115+
}
116+
rel="noopener"
117+
class="text-white bg-[#4876A2] hover:bg-[#4876A2]/90 font-medium rounded-lg text-sm px-5 py-2.5 text-center flex flex-row gap-2 items-center dark:hover:bg-[#050708]/30 me-2 mb-2 border border-gray-300 hover:border-gray-500 hover:px-6 transition-all duration-200"
118+
>
119+
{ project.articleButtonText }
120+
</a>
121+
</div>
122+
</div>
123+
}

0 commit comments

Comments
 (0)